Commit 810eaab3 authored by Gus Caplan's avatar Gus Caplan Committed by Commit Bot

[fastcall] enable accessing template data from fast callback

Adds a `data` field to `v8::FastApiCallbackOptions`.

Bug: chromium:1052746
Change-Id: I0c4ac1a0ea1191e90d3bbc041aec5d8d860d7057
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2603925
Commit-Queue: Gus Caplan <snek@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72207}
parent d3f97acd
...@@ -257,12 +257,39 @@ class CFunctionInfo { ...@@ -257,12 +257,39 @@ class CFunctionInfo {
virtual const CTypeInfo& ReturnInfo() const = 0; virtual const CTypeInfo& ReturnInfo() const = 0;
virtual unsigned int ArgumentCount() const = 0; virtual unsigned int ArgumentCount() const = 0;
virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0; virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0;
virtual bool HasOptions() const = 0;
}; };
struct ApiObject { struct ApiObject {
uintptr_t address; uintptr_t address;
}; };
/**
* A struct which may be passed to a fast call callback, like so:
* \code
* void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
* \endcode
*/
struct FastApiCallbackOptions {
/**
* If the callback wants to signal an error condition or to perform an
* allocation, it must set options.fallback to true and do an early return
* from the fast method. Then V8 checks the value of options.fallback and if
* it's true, falls back to executing the SlowCallback, which is capable of
* reporting the error (either by throwing a JS exception or logging to the
* console) or doing the allocation. It's the embedder's responsibility to
* ensure that the fast callback is idempotent up to the point where error and
* fallback conditions are checked, because otherwise executing the slow
* callback might produce visible side-effects twice.
*/
bool fallback;
/**
* The `data` passed to the FunctionTemplate constructor, or `undefined`.
*/
const ApiObject data;
};
namespace internal { namespace internal {
template <typename T> template <typename T>
...@@ -328,16 +355,28 @@ struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {}; ...@@ -328,16 +355,28 @@ struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {};
template <typename T> template <typename T>
struct GetCType<T*> : public GetCTypePointerImpl<T> {}; struct GetCType<T*> : public GetCTypePointerImpl<T> {};
template <typename R, bool RaisesException, typename... Args> // Helper to count the number of occurances of `T` in `List`
template <typename T, typename... List>
struct count : std::integral_constant<int, 0> {};
template <typename T, typename... Args>
struct count<T, T, Args...>
: std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
template <typename T, typename U, typename... Args>
struct count<T, U, Args...> : count<T, Args...> {};
template <typename R, typename... Args>
class CFunctionInfoImpl : public CFunctionInfo { class CFunctionInfoImpl : public CFunctionInfo {
public: public:
static constexpr int kFallbackArgCount = (RaisesException ? 1 : 0); static constexpr int kOptionsArgCount =
count<FastApiCallbackOptions&, Args...>();
static constexpr int kReceiverCount = 1; static constexpr int kReceiverCount = 1;
CFunctionInfoImpl() CFunctionInfoImpl()
: return_info_(internal::GetCType<R>::Get()), : return_info_(internal::GetCType<R>::Get()),
arg_count_(sizeof...(Args) - kFallbackArgCount), arg_count_(sizeof...(Args) - kOptionsArgCount),
arg_info_{internal::GetCType<Args>::Get()...} { arg_info_{internal::GetCType<Args>::Get()...} {
static_assert(sizeof...(Args) >= kFallbackArgCount + kReceiverCount, static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
"Only one options parameter is supported.");
static_assert(sizeof...(Args) >= kOptionsArgCount + kReceiverCount,
"The receiver or the fallback argument is missing."); "The receiver or the fallback argument is missing.");
static_assert( static_assert(
internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid, internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
...@@ -352,6 +391,7 @@ class CFunctionInfoImpl : public CFunctionInfo { ...@@ -352,6 +391,7 @@ class CFunctionInfoImpl : public CFunctionInfo {
} }
return arg_info_[index]; return arg_info_[index];
} }
bool HasOptions() const override { return kOptionsArgCount == 1; }
private: private:
const CTypeInfo return_info_; const CTypeInfo return_info_;
...@@ -382,8 +422,9 @@ class V8_EXPORT CFunction { ...@@ -382,8 +422,9 @@ class V8_EXPORT CFunction {
} }
template <typename F> template <typename F>
V8_DEPRECATED("Use CFunction::Make instead.")
static CFunction MakeWithFallbackSupport(F* func) { static CFunction MakeWithFallbackSupport(F* func) {
return ArgUnwrap<F*>::MakeWithFallbackSupport(func); return ArgUnwrap<F*>::Make(func);
} }
template <typename F> template <typename F>
...@@ -397,9 +438,9 @@ class V8_EXPORT CFunction { ...@@ -397,9 +438,9 @@ class V8_EXPORT CFunction {
CFunction(const void* address, const CFunctionInfo* type_info); CFunction(const void* address, const CFunctionInfo* type_info);
template <typename R, bool RaisesException, typename... Args> template <typename R, typename... Args>
static CFunctionInfo* GetCFunctionInfo() { static CFunctionInfo* GetCFunctionInfo() {
static internal::CFunctionInfoImpl<R, RaisesException, Args...> instance; static internal::CFunctionInfoImpl<R, Args...> instance;
return &instance; return &instance;
} }
...@@ -414,19 +455,11 @@ class V8_EXPORT CFunction { ...@@ -414,19 +455,11 @@ class V8_EXPORT CFunction {
public: public:
static CFunction Make(R (*func)(Args...)) { static CFunction Make(R (*func)(Args...)) {
return CFunction(reinterpret_cast<const void*>(func), return CFunction(reinterpret_cast<const void*>(func),
GetCFunctionInfo<R, false, Args...>()); GetCFunctionInfo<R, Args...>());
}
static CFunction MakeWithFallbackSupport(R (*func)(Args...)) {
return CFunction(reinterpret_cast<const void*>(func),
GetCFunctionInfo<R, true, Args...>());
} }
}; };
}; };
struct FastApiCallbackOptions {
bool fallback;
};
} // namespace v8 } // namespace v8
#endif // INCLUDE_V8_FAST_API_CALLS_H_ #endif // INCLUDE_V8_FAST_API_CALLS_H_
...@@ -5004,23 +5004,33 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) { ...@@ -5004,23 +5004,33 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
value_input_count); value_input_count);
if (fast_api_call_stack_slot_ == nullptr) { if (fast_api_call_stack_slot_ == nullptr) {
// Add the { fallback } output parameter. int kAlign = alignof(v8::FastApiCallbackOptions);
int kAlign = 4;
int kSize = sizeof(v8::FastApiCallbackOptions); int kSize = sizeof(v8::FastApiCallbackOptions);
// If this check fails, probably you've added new fields to // If this check fails, you've probably added new fields to
// v8::FastApiCallbackOptions, which means you'll need to write code // v8::FastApiCallbackOptions, which means you'll need to write code
// that initializes and reads from them too (see the Store and Load to // that initializes and reads from them too (see the Store and Load to
// fast_api_call_stack_slot_ below). // fast_api_call_stack_slot_ below).
CHECK_EQ(kSize, 1); CHECK_EQ(kSize, sizeof(uintptr_t) * 2);
fast_api_call_stack_slot_ = __ StackSlot(kSize, kAlign); fast_api_call_stack_slot_ = __ StackSlot(kSize, kAlign);
} }
// Generate the store to `fast_api_call_stack_slot_`. // Leave the slot uninit if the callback doesn't use it.
__ Store(StoreRepresentation(MachineRepresentation::kWord32, kNoWriteBarrier), if (c_signature->HasOptions()) {
fast_api_call_stack_slot_, 0, jsgraph()->ZeroConstant()); // Generate the stores to `fast_api_call_stack_slot_`.
__ Store(
StoreRepresentation(MachineRepresentation::kWord32, kNoWriteBarrier),
fast_api_call_stack_slot_,
static_cast<int>(offsetof(v8::FastApiCallbackOptions, fallback)),
jsgraph()->ZeroConstant());
__ Store(StoreRepresentation(MachineRepresentation::kTaggedPointer,
kNoWriteBarrier),
fast_api_call_stack_slot_,
static_cast<int>(offsetof(v8::FastApiCallbackOptions, data)),
n.SlowCallArgument(FastApiCallNode::kSlowCallDataArgumentIndex));
}
MachineSignature::Builder builder( MachineSignature::Builder builder(
graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasErrorInputCount); graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasOptionsInputCount);
MachineType return_type = MachineTypeFor(c_signature->ReturnInfo().GetType()); MachineType return_type = MachineTypeFor(c_signature->ReturnInfo().GetType());
builder.AddReturn(return_type); builder.AddReturn(return_type);
for (int i = 0; i < c_arg_count; ++i) { for (int i = 0; i < c_arg_count; ++i) {
...@@ -5067,47 +5077,54 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) { ...@@ -5067,47 +5077,54 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
kNoWriteBarrier), kNoWriteBarrier),
target_address, 0, __ IntPtrConstant(0)); target_address, 0, __ IntPtrConstant(0));
// Generate the load from `fast_api_call_stack_slot_`. if (c_signature->HasOptions()) {
Node* load = __ Load(MachineType::Int32(), fast_api_call_stack_slot_, 0); // Generate the load from `fast_api_call_stack_slot_`.
Node* load = __ Load(
TNode<Boolean> cond = MachineType::Int32(), fast_api_call_stack_slot_,
TNode<Boolean>::UncheckedCast(__ Word32Equal(load, __ Int32Constant(0))); static_cast<int>(offsetof(v8::FastApiCallbackOptions, fallback)));
// Hint to true.
auto if_success = __ MakeLabel(); TNode<Boolean> cond = TNode<Boolean>::UncheckedCast(
auto if_error = __ MakeDeferredLabel(); __ Word32Equal(load, __ Int32Constant(0)));
auto merge = __ MakeLabel(MachineRepresentation::kTagged); // Hint to true.
__ Branch(cond, &if_success, &if_error); auto if_success = __ MakeLabel();
auto if_error = __ MakeDeferredLabel();
// Generate fast call. auto merge = __ MakeLabel(MachineRepresentation::kTagged);
__ Bind(&if_success); __ Branch(cond, &if_success, &if_error);
Node* then_result = [&]() { return __ UndefinedConstant(); }();
__ Goto(&merge, then_result); // Generate fast call.
__ Bind(&if_success);
// Generate direct slow call. Node* then_result = [&]() { return __ UndefinedConstant(); }();
__ Bind(&if_error); __ Goto(&merge, then_result);
Node* else_result = [&]() {
Node** const slow_inputs = graph()->zone()->NewArray<Node*>( // Generate direct slow call.
n.SlowCallArgumentCount() + __ Bind(&if_error);
FastApiCallNode::kEffectAndControlInputCount); Node* else_result = [&]() {
Node** const slow_inputs = graph()->zone()->NewArray<Node*>(
int fast_call_params = c_arg_count + FastApiCallNode::kFastTargetInputCount; n.SlowCallArgumentCount() +
CHECK_EQ(value_input_count - fast_call_params, n.SlowCallArgumentCount()); FastApiCallNode::kEffectAndControlInputCount);
int index = 0;
for (; index < n.SlowCallArgumentCount(); ++index) { int fast_call_params =
slow_inputs[index] = n.SlowCallArgument(index); c_arg_count + FastApiCallNode::kFastTargetInputCount;
} CHECK_EQ(value_input_count - fast_call_params, n.SlowCallArgumentCount());
int index = 0;
for (; index < n.SlowCallArgumentCount(); ++index) {
slow_inputs[index] = n.SlowCallArgument(index);
}
slow_inputs[index] = __ effect();
slow_inputs[index + 1] = __ control();
Node* slow_call = __ Call(
params.descriptor(),
index + FastApiCallNode::kEffectAndControlInputCount, slow_inputs);
return slow_call;
}();
__ Goto(&merge, else_result);
__ Bind(&merge);
return merge.PhiAt(0);
}
slow_inputs[index] = __ effect(); return __ UndefinedConstant();
slow_inputs[index + 1] = __ control();
Node* slow_call = __ Call(
params.descriptor(),
index + FastApiCallNode::kEffectAndControlInputCount, slow_inputs);
return slow_call;
}();
__ Goto(&merge, else_result);
__ Bind(&merge);
return merge.PhiAt(0);
} }
Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) { Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
......
...@@ -1134,19 +1134,22 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase { ...@@ -1134,19 +1134,22 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
static constexpr int kExtraInputCount = static constexpr int kExtraInputCount =
kFastTargetInputCount + kFastReceiverInputCount; kFastTargetInputCount + kFastReceiverInputCount;
static constexpr int kHasErrorInputCount = 1; static constexpr int kHasOptionsInputCount = 1;
static constexpr int kArityInputCount = 1; static constexpr int kArityInputCount = 1;
static constexpr int kNewTargetInputCount = 1; static constexpr int kNewTargetInputCount = 1;
static constexpr int kHolderInputCount = 1; static constexpr int kHolderInputCount = 1;
static constexpr int kContextAndFrameStateInputCount = 2; static constexpr int kContextAndFrameStateInputCount = 2;
static constexpr int kEffectAndControlInputCount = 2; static constexpr int kEffectAndControlInputCount = 2;
static constexpr int kFastCallExtraInputCount = static constexpr int kFastCallExtraInputCount = kFastTargetInputCount +
kFastTargetInputCount + kHasErrorInputCount + kEffectAndControlInputCount; kHasOptionsInputCount +
kEffectAndControlInputCount;
static constexpr int kSlowCallExtraInputCount = static constexpr int kSlowCallExtraInputCount =
kSlowTargetInputCount + kArityInputCount + kNewTargetInputCount + kSlowTargetInputCount + kArityInputCount + kNewTargetInputCount +
kSlowReceiverInputCount + kHolderInputCount + kSlowReceiverInputCount + kHolderInputCount +
kContextAndFrameStateInputCount + kEffectAndControlInputCount; kContextAndFrameStateInputCount + kEffectAndControlInputCount;
static constexpr int kSlowCallDataArgumentIndex = 3;
// This is the arity fed into FastApiCallArguments. // This is the arity fed into FastApiCallArguments.
static constexpr int ArityForArgc(int c_arg_count, int js_arg_count) { static constexpr int ArityForArgc(int c_arg_count, int js_arg_count) {
return c_arg_count + kFastTargetInputCount + js_arg_count + return c_arg_count + kFastTargetInputCount + js_arg_count +
......
...@@ -27504,10 +27504,13 @@ template <typename Value, typename Impl> ...@@ -27504,10 +27504,13 @@ template <typename Value, typename Impl>
struct BasicApiChecker { struct BasicApiChecker {
static void FastCallback(v8::ApiObject receiver, Value argument, static void FastCallback(v8::ApiObject receiver, Value argument,
v8::FastApiCallbackOptions& options) { v8::FastApiCallbackOptions& options) {
const v8::Value* data = reinterpret_cast<const v8::Value*>(&options.data);
CHECK(data->IsNumber());
CHECK_EQ(reinterpret_cast<const v8::Number*>(data)->Value(), 42.0);
Impl::FastCallback(receiver, argument, options); Impl::FastCallback(receiver, argument, options);
} }
static void FastCallbackNoFallback(v8::ApiObject receiver, Value argument) { static void FastCallbackNoFallback(v8::ApiObject receiver, Value argument) {
v8::FastApiCallbackOptions options; v8::FastApiCallbackOptions options = {false, {0}};
Impl::FastCallback(receiver, argument, options); Impl::FastCallback(receiver, argument, options);
} }
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
...@@ -27629,8 +27632,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env, ...@@ -27629,8 +27632,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
v8::CFunction c_func; v8::CFunction c_func;
if (supports_fallback) { if (supports_fallback) {
c_func = v8::CFunction::MakeWithFallbackSupport( c_func = v8::CFunction::Make(BasicApiChecker<Value, Impl>::FastCallback);
BasicApiChecker<Value, Impl>::FastCallback);
} else { } else {
c_func = v8::CFunction::Make( c_func = v8::CFunction::Make(
BasicApiChecker<Value, Impl>::FastCallbackNoFallback); BasicApiChecker<Value, Impl>::FastCallbackNoFallback);
...@@ -27639,7 +27641,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env, ...@@ -27639,7 +27641,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New( Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New(
isolate, BasicApiChecker<Value, Impl>::SlowCallback, isolate, BasicApiChecker<Value, Impl>::SlowCallback,
v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 1, v8::Number::New(isolate, 42), v8::Local<v8::Signature>(), 1,
v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasSideEffect, v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasSideEffect,
&c_func); &c_func);
if (!accept_any_receiver) { if (!accept_any_receiver) {
...@@ -27877,6 +27879,8 @@ class TestCFunctionInfo : public v8::CFunctionInfo { ...@@ -27877,6 +27879,8 @@ class TestCFunctionInfo : public v8::CFunctionInfo {
UNREACHABLE(); UNREACHABLE();
} }
} }
bool HasOptions() const override { return false; }
}; };
void CheckDynamicTypeInfo() { void CheckDynamicTypeInfo() {
...@@ -4002,8 +4002,7 @@ TEST(FastApiCPUProfiler) { ...@@ -4002,8 +4002,7 @@ TEST(FastApiCPUProfiler) {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
v8::CFunction c_func = v8::CFunction c_func = v8::CFunction::Make(FastApiReceiver::FastCallback);
v8::CFunction::MakeWithFallbackSupport(FastApiReceiver::FastCallback);
Local<v8::FunctionTemplate> receiver_templ = v8::FunctionTemplate::New( Local<v8::FunctionTemplate> receiver_templ = v8::FunctionTemplate::New(
isolate, FastApiReceiver::SlowCallback, v8::Local<v8::Value>(), isolate, FastApiReceiver::SlowCallback, v8::Local<v8::Value>(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment