[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: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#72207}
This commit is contained in:
parent
d3f97acd4c
commit
810eaab3c5
@ -257,12 +257,39 @@ class CFunctionInfo {
|
||||
virtual const CTypeInfo& ReturnInfo() const = 0;
|
||||
virtual unsigned int ArgumentCount() const = 0;
|
||||
virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0;
|
||||
virtual bool HasOptions() const = 0;
|
||||
};
|
||||
|
||||
struct ApiObject {
|
||||
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 {
|
||||
|
||||
template <typename T>
|
||||
@ -328,16 +355,28 @@ struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {};
|
||||
template <typename 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 {
|
||||
public:
|
||||
static constexpr int kFallbackArgCount = (RaisesException ? 1 : 0);
|
||||
static constexpr int kOptionsArgCount =
|
||||
count<FastApiCallbackOptions&, Args...>();
|
||||
static constexpr int kReceiverCount = 1;
|
||||
CFunctionInfoImpl()
|
||||
: return_info_(internal::GetCType<R>::Get()),
|
||||
arg_count_(sizeof...(Args) - kFallbackArgCount),
|
||||
arg_count_(sizeof...(Args) - kOptionsArgCount),
|
||||
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.");
|
||||
static_assert(
|
||||
internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
|
||||
@ -352,6 +391,7 @@ class CFunctionInfoImpl : public CFunctionInfo {
|
||||
}
|
||||
return arg_info_[index];
|
||||
}
|
||||
bool HasOptions() const override { return kOptionsArgCount == 1; }
|
||||
|
||||
private:
|
||||
const CTypeInfo return_info_;
|
||||
@ -382,8 +422,9 @@ class V8_EXPORT CFunction {
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
V8_DEPRECATED("Use CFunction::Make instead.")
|
||||
static CFunction MakeWithFallbackSupport(F* func) {
|
||||
return ArgUnwrap<F*>::MakeWithFallbackSupport(func);
|
||||
return ArgUnwrap<F*>::Make(func);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
@ -397,9 +438,9 @@ class V8_EXPORT CFunction {
|
||||
|
||||
CFunction(const void* address, const CFunctionInfo* type_info);
|
||||
|
||||
template <typename R, bool RaisesException, typename... Args>
|
||||
template <typename R, typename... Args>
|
||||
static CFunctionInfo* GetCFunctionInfo() {
|
||||
static internal::CFunctionInfoImpl<R, RaisesException, Args...> instance;
|
||||
static internal::CFunctionInfoImpl<R, Args...> instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
@ -414,19 +455,11 @@ class V8_EXPORT CFunction {
|
||||
public:
|
||||
static CFunction Make(R (*func)(Args...)) {
|
||||
return CFunction(reinterpret_cast<const void*>(func),
|
||||
GetCFunctionInfo<R, false, Args...>());
|
||||
}
|
||||
static CFunction MakeWithFallbackSupport(R (*func)(Args...)) {
|
||||
return CFunction(reinterpret_cast<const void*>(func),
|
||||
GetCFunctionInfo<R, true, Args...>());
|
||||
GetCFunctionInfo<R, Args...>());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct FastApiCallbackOptions {
|
||||
bool fallback;
|
||||
};
|
||||
|
||||
} // namespace v8
|
||||
|
||||
#endif // INCLUDE_V8_FAST_API_CALLS_H_
|
||||
|
@ -5004,23 +5004,33 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
value_input_count);
|
||||
|
||||
if (fast_api_call_stack_slot_ == nullptr) {
|
||||
// Add the { fallback } output parameter.
|
||||
int kAlign = 4;
|
||||
int kAlign = alignof(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
|
||||
// that initializes and reads from them too (see the Store and Load to
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Generate the store to `fast_api_call_stack_slot_`.
|
||||
__ Store(StoreRepresentation(MachineRepresentation::kWord32, kNoWriteBarrier),
|
||||
fast_api_call_stack_slot_, 0, jsgraph()->ZeroConstant());
|
||||
// Leave the slot uninit if the callback doesn't use it.
|
||||
if (c_signature->HasOptions()) {
|
||||
// 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(
|
||||
graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasErrorInputCount);
|
||||
graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasOptionsInputCount);
|
||||
MachineType return_type = MachineTypeFor(c_signature->ReturnInfo().GetType());
|
||||
builder.AddReturn(return_type);
|
||||
for (int i = 0; i < c_arg_count; ++i) {
|
||||
@ -5067,47 +5077,54 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
kNoWriteBarrier),
|
||||
target_address, 0, __ IntPtrConstant(0));
|
||||
|
||||
// Generate the load from `fast_api_call_stack_slot_`.
|
||||
Node* load = __ Load(MachineType::Int32(), fast_api_call_stack_slot_, 0);
|
||||
if (c_signature->HasOptions()) {
|
||||
// Generate the load from `fast_api_call_stack_slot_`.
|
||||
Node* load = __ Load(
|
||||
MachineType::Int32(), fast_api_call_stack_slot_,
|
||||
static_cast<int>(offsetof(v8::FastApiCallbackOptions, fallback)));
|
||||
|
||||
TNode<Boolean> cond =
|
||||
TNode<Boolean>::UncheckedCast(__ Word32Equal(load, __ Int32Constant(0)));
|
||||
// Hint to true.
|
||||
auto if_success = __ MakeLabel();
|
||||
auto if_error = __ MakeDeferredLabel();
|
||||
auto merge = __ MakeLabel(MachineRepresentation::kTagged);
|
||||
__ Branch(cond, &if_success, &if_error);
|
||||
TNode<Boolean> cond = TNode<Boolean>::UncheckedCast(
|
||||
__ Word32Equal(load, __ Int32Constant(0)));
|
||||
// Hint to true.
|
||||
auto if_success = __ MakeLabel();
|
||||
auto if_error = __ MakeDeferredLabel();
|
||||
auto merge = __ MakeLabel(MachineRepresentation::kTagged);
|
||||
__ Branch(cond, &if_success, &if_error);
|
||||
|
||||
// Generate fast call.
|
||||
__ Bind(&if_success);
|
||||
Node* then_result = [&]() { return __ UndefinedConstant(); }();
|
||||
__ Goto(&merge, then_result);
|
||||
// Generate fast call.
|
||||
__ Bind(&if_success);
|
||||
Node* then_result = [&]() { return __ UndefinedConstant(); }();
|
||||
__ Goto(&merge, then_result);
|
||||
|
||||
// Generate direct slow call.
|
||||
__ Bind(&if_error);
|
||||
Node* else_result = [&]() {
|
||||
Node** const slow_inputs = graph()->zone()->NewArray<Node*>(
|
||||
n.SlowCallArgumentCount() +
|
||||
FastApiCallNode::kEffectAndControlInputCount);
|
||||
// Generate direct slow call.
|
||||
__ Bind(&if_error);
|
||||
Node* else_result = [&]() {
|
||||
Node** const slow_inputs = graph()->zone()->NewArray<Node*>(
|
||||
n.SlowCallArgumentCount() +
|
||||
FastApiCallNode::kEffectAndControlInputCount);
|
||||
|
||||
int fast_call_params = 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);
|
||||
}
|
||||
int fast_call_params =
|
||||
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);
|
||||
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);
|
||||
__ Bind(&merge);
|
||||
return merge.PhiAt(0);
|
||||
}
|
||||
|
||||
return __ UndefinedConstant();
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
|
||||
|
@ -1134,19 +1134,22 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
|
||||
static constexpr int kExtraInputCount =
|
||||
kFastTargetInputCount + kFastReceiverInputCount;
|
||||
|
||||
static constexpr int kHasErrorInputCount = 1;
|
||||
static constexpr int kHasOptionsInputCount = 1;
|
||||
static constexpr int kArityInputCount = 1;
|
||||
static constexpr int kNewTargetInputCount = 1;
|
||||
static constexpr int kHolderInputCount = 1;
|
||||
static constexpr int kContextAndFrameStateInputCount = 2;
|
||||
static constexpr int kEffectAndControlInputCount = 2;
|
||||
static constexpr int kFastCallExtraInputCount =
|
||||
kFastTargetInputCount + kHasErrorInputCount + kEffectAndControlInputCount;
|
||||
static constexpr int kFastCallExtraInputCount = kFastTargetInputCount +
|
||||
kHasOptionsInputCount +
|
||||
kEffectAndControlInputCount;
|
||||
static constexpr int kSlowCallExtraInputCount =
|
||||
kSlowTargetInputCount + kArityInputCount + kNewTargetInputCount +
|
||||
kSlowReceiverInputCount + kHolderInputCount +
|
||||
kContextAndFrameStateInputCount + kEffectAndControlInputCount;
|
||||
|
||||
static constexpr int kSlowCallDataArgumentIndex = 3;
|
||||
|
||||
// This is the arity fed into FastApiCallArguments.
|
||||
static constexpr int ArityForArgc(int c_arg_count, int js_arg_count) {
|
||||
return c_arg_count + kFastTargetInputCount + js_arg_count +
|
||||
|
@ -27504,10 +27504,13 @@ template <typename Value, typename Impl>
|
||||
struct BasicApiChecker {
|
||||
static void FastCallback(v8::ApiObject receiver, Value argument,
|
||||
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);
|
||||
}
|
||||
static void FastCallbackNoFallback(v8::ApiObject receiver, Value argument) {
|
||||
v8::FastApiCallbackOptions options;
|
||||
v8::FastApiCallbackOptions options = {false, {0}};
|
||||
Impl::FastCallback(receiver, argument, options);
|
||||
}
|
||||
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
@ -27629,8 +27632,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||
|
||||
v8::CFunction c_func;
|
||||
if (supports_fallback) {
|
||||
c_func = v8::CFunction::MakeWithFallbackSupport(
|
||||
BasicApiChecker<Value, Impl>::FastCallback);
|
||||
c_func = v8::CFunction::Make(BasicApiChecker<Value, Impl>::FastCallback);
|
||||
} else {
|
||||
c_func = v8::CFunction::Make(
|
||||
BasicApiChecker<Value, Impl>::FastCallbackNoFallback);
|
||||
@ -27639,7 +27641,7 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||
|
||||
Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New(
|
||||
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,
|
||||
&c_func);
|
||||
if (!accept_any_receiver) {
|
||||
@ -27877,6 +27879,8 @@ class TestCFunctionInfo : public v8::CFunctionInfo {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool HasOptions() const override { return false; }
|
||||
};
|
||||
|
||||
void CheckDynamicTypeInfo() {
|
||||
|
@ -4002,8 +4002,7 @@ TEST(FastApiCPUProfiler) {
|
||||
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
v8::CFunction c_func =
|
||||
v8::CFunction::MakeWithFallbackSupport(FastApiReceiver::FastCallback);
|
||||
v8::CFunction c_func = v8::CFunction::Make(FastApiReceiver::FastCallback);
|
||||
|
||||
Local<v8::FunctionTemplate> receiver_templ = v8::FunctionTemplate::New(
|
||||
isolate, FastApiReceiver::SlowCallback, v8::Local<v8::Value>(),
|
||||
|
Loading…
Reference in New Issue
Block a user