[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:
Gus Caplan 2021-01-20 11:45:29 -06:00 committed by Commit Bot
parent d3f97acd4c
commit 810eaab3c5
5 changed files with 124 additions and 68 deletions

View File

@ -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_

View File

@ -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) {

View File

@ -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 +

View File

@ -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() {

View File

@ -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>(),