[turbofan] Fast API calls support for default fallback
This change adds a has_error parameter on the stack which allows the fast callback to report an error. In case this parameter is set to non-zero, the generated code calls the slow (default) callback, which can throw the exception. Bug: chromium:1052746 Change-Id: Ib11f6b0bef37d5eb1d04cd6d0a3ef59028dcc448 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2183929 Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Commit-Queue: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#68871}
This commit is contained in:
parent
1905c05c10
commit
ca1e6573e4
@ -331,13 +331,17 @@ struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {};
|
||||
template <typename T>
|
||||
struct GetCType<T*> : public GetCTypePointerImpl<T> {};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
template <typename R, bool RaisesException, typename... Args>
|
||||
class CFunctionInfoImpl : public CFunctionInfo {
|
||||
public:
|
||||
static constexpr int kHasErrorArgCount = (RaisesException ? 1 : 0);
|
||||
static constexpr int kReceiverCount = 1;
|
||||
CFunctionInfoImpl()
|
||||
: return_info_(internal::GetCType<R>::Get()),
|
||||
arg_count_(sizeof...(Args)),
|
||||
arg_count_(sizeof...(Args) - kHasErrorArgCount),
|
||||
arg_info_{internal::GetCType<Args>::Get()...} {
|
||||
static_assert(sizeof...(Args) >= kHasErrorArgCount + kReceiverCount,
|
||||
"The receiver or the has_error argument is missing.");
|
||||
static_assert(
|
||||
internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
|
||||
"Only void return types are currently supported.");
|
||||
@ -351,9 +355,9 @@ class CFunctionInfoImpl : public CFunctionInfo {
|
||||
}
|
||||
|
||||
private:
|
||||
CTypeInfo return_info_;
|
||||
const CTypeInfo return_info_;
|
||||
const unsigned int arg_count_;
|
||||
CTypeInfo arg_info_[sizeof...(Args)];
|
||||
const CTypeInfo arg_info_[sizeof...(Args)];
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@ -378,6 +382,11 @@ class V8_EXPORT CFunction {
|
||||
return ArgUnwrap<F*>::Make(func);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static CFunction MakeRaisesException(F* func) {
|
||||
return ArgUnwrap<F*>::MakeRaisesException(func);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static CFunction Make(F* func, const CFunctionInfo* type_info) {
|
||||
return CFunction(reinterpret_cast<const void*>(func), type_info);
|
||||
@ -389,9 +398,9 @@ class V8_EXPORT CFunction {
|
||||
|
||||
CFunction(const void* address, const CFunctionInfo* type_info);
|
||||
|
||||
template <typename R, typename... Args>
|
||||
template <typename R, bool RaisesException, typename... Args>
|
||||
static CFunctionInfo* GetCFunctionInfo() {
|
||||
static internal::CFunctionInfoImpl<R, Args...> instance;
|
||||
static internal::CFunctionInfoImpl<R, RaisesException, Args...> instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
@ -406,7 +415,11 @@ class V8_EXPORT CFunction {
|
||||
public:
|
||||
static CFunction Make(R (*func)(Args...)) {
|
||||
return CFunction(reinterpret_cast<const void*>(func),
|
||||
GetCFunctionInfo<R, Args...>());
|
||||
GetCFunctionInfo<R, false, Args...>());
|
||||
}
|
||||
static CFunction MakeRaisesException(R (*func)(Args...)) {
|
||||
return CFunction(reinterpret_cast<const void*>(func),
|
||||
GetCFunctionInfo<R, true, Args...>());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/compiler/effect-control-linearizer.h"
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/base/bits.h"
|
||||
#include "src/codegen/code-factory.h"
|
||||
#include "src/codegen/machine-type.h"
|
||||
@ -185,6 +186,7 @@ class EffectControlLinearizer {
|
||||
void LowerTransitionElementsKind(Node* node);
|
||||
Node* LowerLoadFieldByIndex(Node* node);
|
||||
Node* LowerLoadMessage(Node* node);
|
||||
Node* LowerFastApiCall(Node* node);
|
||||
Node* LowerLoadTypedElement(Node* node);
|
||||
Node* LowerLoadDataViewElement(Node* node);
|
||||
Node* LowerLoadStackArgument(Node* node);
|
||||
@ -1246,6 +1248,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
|
||||
case IrOpcode::kStoreMessage:
|
||||
LowerStoreMessage(node);
|
||||
break;
|
||||
case IrOpcode::kFastApiCall:
|
||||
result = LowerFastApiCall(node);
|
||||
break;
|
||||
case IrOpcode::kLoadFieldByIndex:
|
||||
result = LowerLoadFieldByIndex(node);
|
||||
break;
|
||||
@ -4798,6 +4803,121 @@ void EffectControlLinearizer::LowerStoreMessage(Node* node) {
|
||||
__ StoreField(AccessBuilder::ForExternalIntPtr(), offset, object_pattern);
|
||||
}
|
||||
|
||||
// TODO(mslekova): Avoid code duplication with simplified lowering.
|
||||
static MachineType MachineTypeFor(CTypeInfo::Type type) {
|
||||
switch (type) {
|
||||
case CTypeInfo::Type::kVoid:
|
||||
return MachineType::AnyTagged();
|
||||
case CTypeInfo::Type::kBool:
|
||||
return MachineType::Bool();
|
||||
case CTypeInfo::Type::kInt32:
|
||||
return MachineType::Int32();
|
||||
case CTypeInfo::Type::kUint32:
|
||||
return MachineType::Uint32();
|
||||
case CTypeInfo::Type::kInt64:
|
||||
return MachineType::Int64();
|
||||
case CTypeInfo::Type::kUint64:
|
||||
return MachineType::Uint64();
|
||||
case CTypeInfo::Type::kFloat32:
|
||||
return MachineType::Float32();
|
||||
case CTypeInfo::Type::kFloat64:
|
||||
return MachineType::Float64();
|
||||
case CTypeInfo::Type::kUnwrappedApiObject:
|
||||
return MachineType::Pointer();
|
||||
}
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
FastApiCallNode n(node);
|
||||
FastApiCallParameters const& params = n.Parameters();
|
||||
const CFunctionInfo* c_signature = params.signature();
|
||||
const int c_arg_count = c_signature->ArgumentCount();
|
||||
CallDescriptor* js_call_descriptor = params.descriptor();
|
||||
int js_arg_count = static_cast<int>(js_call_descriptor->ParameterCount());
|
||||
const int value_input_count = node->op()->ValueInputCount();
|
||||
CHECK_EQ(FastApiCallNode::ArityForArgc(c_arg_count, js_arg_count),
|
||||
value_input_count);
|
||||
|
||||
// Add the { has_error } output parameter.
|
||||
int kAlign = 4;
|
||||
int kSize = 4;
|
||||
Node* has_error = __ StackSlot(kSize, kAlign);
|
||||
// Generate the store to `has_error`.
|
||||
__ Store(StoreRepresentation(MachineRepresentation::kWord32, kNoWriteBarrier),
|
||||
has_error, 0, jsgraph()->ZeroConstant());
|
||||
|
||||
MachineSignature::Builder builder(
|
||||
graph()->zone(), 1, c_arg_count + FastApiCallNode::kHasErrorInputCount);
|
||||
MachineType return_type = MachineTypeFor(c_signature->ReturnInfo().GetType());
|
||||
builder.AddReturn(return_type);
|
||||
for (int i = 0; i < c_arg_count; ++i) {
|
||||
MachineType machine_type =
|
||||
MachineTypeFor(c_signature->ArgumentInfo(i).GetType());
|
||||
builder.AddParam(machine_type);
|
||||
}
|
||||
builder.AddParam(MachineType::Pointer()); // has_error
|
||||
|
||||
CallDescriptor* call_descriptor = Linkage::GetSimplifiedCDescriptor(
|
||||
graph()->zone(), builder.Build(), CallDescriptor::kNoFlags);
|
||||
|
||||
call_descriptor->SetCFunctionInfo(c_signature);
|
||||
|
||||
Node** const inputs = graph()->zone()->NewArray<Node*>(
|
||||
c_arg_count + FastApiCallNode::kFastCallExtraInputCount);
|
||||
for (int i = 0; i < c_arg_count + FastApiCallNode::kFastTargetInputCount;
|
||||
++i) {
|
||||
inputs[i] = NodeProperties::GetValueInput(node, i);
|
||||
}
|
||||
inputs[c_arg_count + 1] = has_error;
|
||||
inputs[c_arg_count + 2] = __ effect();
|
||||
inputs[c_arg_count + 3] = __ control();
|
||||
|
||||
__ Call(call_descriptor,
|
||||
c_arg_count + FastApiCallNode::kFastCallExtraInputCount, inputs);
|
||||
|
||||
// Generate the load from `has_error`.
|
||||
Node* load = __ Load(MachineType::Int32(), has_error, 0);
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerLoadFieldByIndex(Node* node) {
|
||||
Node* object = node->InputAt(0);
|
||||
Node* index = node->InputAt(1);
|
||||
|
@ -736,6 +736,17 @@ Node* GraphAssembler::DeoptimizeIfNot(DeoptimizeReason reason,
|
||||
condition, frame_state, effect(), control()));
|
||||
}
|
||||
|
||||
TNode<Object> GraphAssembler::Call(const CallDescriptor* call_descriptor,
|
||||
int inputs_size, Node** inputs) {
|
||||
return Call(common()->Call(call_descriptor), inputs_size, inputs);
|
||||
}
|
||||
|
||||
TNode<Object> GraphAssembler::Call(const Operator* op, int inputs_size,
|
||||
Node** inputs) {
|
||||
DCHECK_EQ(IrOpcode::kCall, op->opcode());
|
||||
return AddNode<Object>(graph()->NewNode(op, inputs_size, inputs));
|
||||
}
|
||||
|
||||
void GraphAssembler::BranchWithCriticalSafetyCheck(
|
||||
Node* condition, GraphAssemblerLabel<0u>* if_true,
|
||||
GraphAssemblerLabel<0u>* if_false) {
|
||||
|
@ -297,10 +297,14 @@ class V8_EXPORT_PRIVATE GraphAssembler {
|
||||
DeoptimizeReason reason, FeedbackSource const& feedback, Node* condition,
|
||||
Node* frame_state,
|
||||
IsSafetyCheck is_safety_check = IsSafetyCheck::kSafetyCheck);
|
||||
TNode<Object> Call(const CallDescriptor* call_descriptor, int inputs_size,
|
||||
Node** inputs);
|
||||
TNode<Object> Call(const Operator* op, int inputs_size, Node** inputs);
|
||||
template <typename... Args>
|
||||
Node* Call(const CallDescriptor* call_descriptor, Args... args);
|
||||
TNode<Object> Call(const CallDescriptor* call_descriptor, Node* first_arg,
|
||||
Args... args);
|
||||
template <typename... Args>
|
||||
Node* Call(const Operator* op, Args... args);
|
||||
TNode<Object> Call(const Operator* op, Node* first_arg, Args... args);
|
||||
|
||||
// Basic control operations.
|
||||
template <size_t VarCount>
|
||||
@ -720,19 +724,19 @@ void GraphAssembler::GotoIfNot(Node* condition,
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Node* GraphAssembler::Call(const CallDescriptor* call_descriptor,
|
||||
Args... args) {
|
||||
TNode<Object> GraphAssembler::Call(const CallDescriptor* call_descriptor,
|
||||
Node* first_arg, Args... args) {
|
||||
const Operator* op = common()->Call(call_descriptor);
|
||||
return Call(op, args...);
|
||||
return Call(op, first_arg, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Node* GraphAssembler::Call(const Operator* op, Args... args) {
|
||||
DCHECK_EQ(IrOpcode::kCall, op->opcode());
|
||||
Node* args_array[] = {args..., effect(), control()};
|
||||
int size = static_cast<int>(sizeof...(args)) + op->EffectInputCount() +
|
||||
TNode<Object> GraphAssembler::Call(const Operator* op, Node* first_arg,
|
||||
Args... args) {
|
||||
Node* args_array[] = {first_arg, args..., effect(), control()};
|
||||
int size = static_cast<int>(1 + sizeof...(args)) + op->EffectInputCount() +
|
||||
op->ControlInputCount();
|
||||
return AddNode(graph()->NewNode(op, size, args_array));
|
||||
return Call(op, size, args_array);
|
||||
}
|
||||
|
||||
class V8_EXPORT_PRIVATE JSGraphAssembler : public GraphAssembler {
|
||||
|
@ -874,63 +874,115 @@ class PromiseBuiltinReducerAssembler : public JSCallReducerAssembler {
|
||||
|
||||
class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||
public:
|
||||
FastApiCallReducerAssembler(JSGraph* jsgraph, Zone* zone, Node* node,
|
||||
Address c_function,
|
||||
const CFunctionInfo* c_signature)
|
||||
FastApiCallReducerAssembler(
|
||||
JSGraph* jsgraph, Zone* zone, Node* node, Address c_function,
|
||||
const CFunctionInfo* c_signature,
|
||||
const FunctionTemplateInfoRef function_template_info, Node* receiver,
|
||||
Node* holder, const SharedFunctionInfoRef shared, Node* target,
|
||||
const int arity, Node* effect)
|
||||
: JSCallReducerAssembler(jsgraph, zone, node),
|
||||
c_function_(c_function),
|
||||
c_signature_(c_signature) {
|
||||
c_signature_(c_signature),
|
||||
function_template_info_(function_template_info),
|
||||
receiver_(receiver),
|
||||
holder_(holder),
|
||||
shared_(shared),
|
||||
target_(target),
|
||||
arity_(arity) {
|
||||
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
||||
DCHECK_NE(c_function_, kNullAddress);
|
||||
CHECK_NOT_NULL(c_signature_);
|
||||
InitializeEffectControl(effect, NodeProperties::GetControlInput(node));
|
||||
}
|
||||
|
||||
TNode<Object> ReduceFastApiCall() {
|
||||
JSCallNode n(node_ptr());
|
||||
// C arguments include the receiver at index 0. Thus C index 1 corresponds
|
||||
// to the JS argument 0, etc.
|
||||
static constexpr int kReceiver = 1;
|
||||
const int c_argument_count =
|
||||
static_cast<int>(c_signature_->ArgumentCount());
|
||||
const bool c_arguments_contain_receiver = c_argument_count > 0;
|
||||
CHECK_GE(c_argument_count, kReceiver);
|
||||
|
||||
int cursor = 0;
|
||||
base::SmallVector<Node*, kInlineSize + kExtraInputsCount> inputs(
|
||||
c_argument_count + kExtraInputsCount);
|
||||
base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ +
|
||||
kExtraInputsCount);
|
||||
inputs[cursor++] = ExternalConstant(ExternalReference::Create(c_function_));
|
||||
|
||||
if (c_arguments_contain_receiver) {
|
||||
inputs[cursor++] = MaybeUnwrapApiObject(
|
||||
c_signature_->ArgumentInfo(0).GetType(), ReceiverInput());
|
||||
}
|
||||
inputs[cursor++] = MaybeUnwrapApiObject(
|
||||
c_signature_->ArgumentInfo(0).GetType(), n.receiver());
|
||||
|
||||
// TODO(jgruber,mslekova): Consider refactoring CFunctionInfo to distinguish
|
||||
// TODO(turbofan): Consider refactoring CFunctionInfo to distinguish
|
||||
// between receiver and arguments, simplifying this (and related) spots.
|
||||
for (int i = 0; i < c_argument_count - kReceiver; ++i) {
|
||||
if (i < ArgumentCount()) {
|
||||
int js_args_count = c_argument_count - kReceiver;
|
||||
for (int i = 0; i < js_args_count; ++i) {
|
||||
if (i < n.ArgumentCount()) {
|
||||
CTypeInfo::Type type =
|
||||
c_signature_->ArgumentInfo(i + kReceiver).GetType();
|
||||
inputs[cursor++] = MaybeUnwrapApiObject(type, Argument(i));
|
||||
inputs[cursor++] = MaybeUnwrapApiObject(type, n.Argument(i));
|
||||
} else {
|
||||
inputs[cursor++] = UndefinedConstant();
|
||||
}
|
||||
}
|
||||
|
||||
// Here we add the arguments for the slow call, which will be
|
||||
// reconstructed at a later phase. Those are effectively the same
|
||||
// arguments as for the fast call, but we want to have them as
|
||||
// separate inputs, so that SimplifiedLowering can provide the best
|
||||
// possible UseInfos for each of them. The inputs to FastApiCall
|
||||
// look like:
|
||||
// [fast callee, receiver, ... C arguments,
|
||||
// call code, external constant for function, argc, call handler info data,
|
||||
// holder, receiver, ... JS arguments, context, new frame state]
|
||||
CallHandlerInfoRef call_handler_info = *function_template_info_.call_code();
|
||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||
CallDescriptor* call_descriptor =
|
||||
Linkage::GetStubCallDescriptor(graph()->zone(), cid, arity_ + kReceiver,
|
||||
CallDescriptor::kNeedsFrameState);
|
||||
ApiFunction api_function(call_handler_info.callback());
|
||||
ExternalReference function_reference = ExternalReference::Create(
|
||||
&api_function, ExternalReference::DIRECT_API_CALL);
|
||||
|
||||
Node* continuation_frame_state =
|
||||
CreateGenericLazyDeoptContinuationFrameState(
|
||||
jsgraph(), shared_, target_, ContextInput(), receiver_,
|
||||
FrameStateInput());
|
||||
|
||||
inputs[cursor++] = HeapConstant(call_api_callback.code());
|
||||
inputs[cursor++] = ExternalConstant(function_reference);
|
||||
inputs[cursor++] = NumberConstant(arity_);
|
||||
inputs[cursor++] = Constant(call_handler_info.data());
|
||||
inputs[cursor++] = holder_;
|
||||
inputs[cursor++] = receiver_;
|
||||
for (int i = 0; i < arity_; ++i) {
|
||||
inputs[cursor++] = Argument(i);
|
||||
}
|
||||
inputs[cursor++] = ContextInput();
|
||||
inputs[cursor++] = continuation_frame_state;
|
||||
inputs[cursor++] = effect();
|
||||
inputs[cursor++] = control();
|
||||
DCHECK_EQ(cursor, c_argument_count + kExtraInputsCount);
|
||||
|
||||
return FastApiCall(inputs.begin(), inputs.size());
|
||||
DCHECK_EQ(cursor, c_argument_count + arity_ + kExtraInputsCount);
|
||||
|
||||
return FastApiCall(call_descriptor, inputs.begin(), inputs.size());
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kTargetEffectAndControl = 3;
|
||||
static constexpr int kExtraInputsCount = kTargetEffectAndControl;
|
||||
static constexpr int kInlineSize = 10;
|
||||
static constexpr int kTarget = 1;
|
||||
static constexpr int kEffectAndControl = 2;
|
||||
static constexpr int kContextAndFrameState = 2;
|
||||
static constexpr int kCallCodeDataAndArgc = 3;
|
||||
static constexpr int kHolder = 1, kReceiver = 1;
|
||||
static constexpr int kExtraInputsCount =
|
||||
kTarget * 2 + kEffectAndControl + kContextAndFrameState +
|
||||
kCallCodeDataAndArgc + kHolder + kReceiver;
|
||||
static constexpr int kInlineSize = 12;
|
||||
|
||||
TNode<Object> FastApiCall(Node** inputs, size_t inputs_size) {
|
||||
return AddNode<Object>(
|
||||
graph()->NewNode(simplified()->FastApiCall(c_signature_, feedback()),
|
||||
static_cast<int>(inputs_size), inputs));
|
||||
TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs,
|
||||
size_t inputs_size) {
|
||||
return AddNode<Object>(graph()->NewNode(
|
||||
simplified()->FastApiCall(c_signature_, feedback(), descriptor),
|
||||
static_cast<int>(inputs_size), inputs));
|
||||
}
|
||||
|
||||
TNode<RawPtrT> UnwrapApiObject(TNode<JSObject> node) {
|
||||
@ -961,6 +1013,12 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||
|
||||
const Address c_function_;
|
||||
const CFunctionInfo* const c_signature_;
|
||||
const FunctionTemplateInfoRef function_template_info_;
|
||||
Node* const receiver_;
|
||||
Node* const holder_;
|
||||
const SharedFunctionInfoRef shared_;
|
||||
Node* const target_;
|
||||
const int arity_;
|
||||
};
|
||||
|
||||
TNode<Number> JSCallReducerAssembler::SpeculativeToNumber(
|
||||
@ -3580,18 +3638,22 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
if (FLAG_turbo_fast_api_calls && c_function != kNullAddress) {
|
||||
const CFunctionInfo* c_signature = function_template_info.c_signature();
|
||||
FastApiCallReducerAssembler a(jsgraph(), graph()->zone(), node, c_function,
|
||||
c_signature);
|
||||
Node* c_call = a.ReduceFastApiCall();
|
||||
ReplaceWithSubgraph(&a, c_call);
|
||||
return Replace(c_call);
|
||||
c_signature, function_template_info, receiver,
|
||||
holder, shared, target, argc,
|
||||
receiver /* The ConvertReceiver node needs to
|
||||
be connected in the effect chain.*/);
|
||||
Node* fast_call_subgraph = a.ReduceFastApiCall();
|
||||
ReplaceWithSubgraph(&a, fast_call_subgraph);
|
||||
|
||||
return Replace(fast_call_subgraph);
|
||||
}
|
||||
|
||||
CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
|
||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||
auto call_descriptor = Linkage::GetStubCallDescriptor(
|
||||
graph()->zone(), cid, argc + 1 /* implicit receiver */,
|
||||
CallDescriptor::kNeedsFrameState);
|
||||
auto call_descriptor =
|
||||
Linkage::GetStubCallDescriptor(graph()->zone(), cid, argc + 1 /*
|
||||
implicit receiver */, CallDescriptor::kNeedsFrameState);
|
||||
ApiFunction api_function(call_handler_info.callback());
|
||||
ExternalReference function_reference = ExternalReference::Create(
|
||||
&api_function, ExternalReference::DIRECT_API_CALL);
|
||||
@ -3607,9 +3669,10 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
node->InsertInput(graph()->zone(), 3,
|
||||
jsgraph()->Constant(call_handler_info.data()));
|
||||
node->InsertInput(graph()->zone(), 4, holder);
|
||||
node->ReplaceInput(5, receiver); // Update receiver input.
|
||||
node->ReplaceInput(7 + argc, continuation_frame_state);
|
||||
node->ReplaceInput(8 + argc, effect); // Update effect input.
|
||||
node->ReplaceInput(5, receiver); // Update receiver input.
|
||||
// 6 + argc is context input.
|
||||
node->ReplaceInput(6 + argc + 1, continuation_frame_state);
|
||||
node->ReplaceInput(6 + argc + 2, effect);
|
||||
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
|
||||
return Changed(node);
|
||||
}
|
||||
|
@ -1709,7 +1709,7 @@ class RepresentationSelector {
|
||||
static MachineType MachineTypeFor(CTypeInfo::Type type) {
|
||||
switch (type) {
|
||||
case CTypeInfo::Type::kVoid:
|
||||
return MachineType::Int32();
|
||||
return MachineType::AnyTagged();
|
||||
case CTypeInfo::Type::kBool:
|
||||
return MachineType::Bool();
|
||||
case CTypeInfo::Type::kInt32:
|
||||
@ -1759,48 +1759,48 @@ class RepresentationSelector {
|
||||
static constexpr int kInitialArgumentsCount = 10;
|
||||
|
||||
template <Phase T>
|
||||
void VisitFastApiCall(Node* node) {
|
||||
FastApiCallParameters const& params = FastApiCallParametersOf(node->op());
|
||||
const CFunctionInfo* c_signature = params.signature();
|
||||
int c_arg_count = c_signature->ArgumentCount();
|
||||
int value_input_count = node->op()->ValueInputCount();
|
||||
// function, ... C args
|
||||
CHECK_EQ(c_arg_count + 1, value_input_count);
|
||||
void VisitFastApiCall(Node* node, SimplifiedLowering* lowering) {
|
||||
FastApiCallParameters const& op_params =
|
||||
FastApiCallParametersOf(node->op());
|
||||
const CFunctionInfo* c_signature = op_params.signature();
|
||||
const int c_arg_count = c_signature->ArgumentCount();
|
||||
CallDescriptor* call_descriptor = op_params.descriptor();
|
||||
int js_arg_count = static_cast<int>(call_descriptor->ParameterCount());
|
||||
const int value_input_count = node->op()->ValueInputCount();
|
||||
CHECK_EQ(FastApiCallNode::ArityForArgc(c_arg_count, js_arg_count),
|
||||
value_input_count);
|
||||
|
||||
base::SmallVector<UseInfo, kInitialArgumentsCount> arg_use_info(
|
||||
c_arg_count);
|
||||
// The target of the fast call.
|
||||
ProcessInput<T>(node, 0, UseInfo::Word());
|
||||
// Propagate representation information from TypeInfo.
|
||||
for (int i = 0; i < c_arg_count; i++) {
|
||||
arg_use_info[i] = UseInfoForFastApiCallArgument(
|
||||
c_signature->ArgumentInfo(i).GetType(), params.feedback());
|
||||
ProcessInput<T>(node, i + 1, arg_use_info[i]);
|
||||
c_signature->ArgumentInfo(i).GetType(), op_params.feedback());
|
||||
ProcessInput<T>(node, i + FastApiCallNode::kFastTargetInputCount,
|
||||
arg_use_info[i]);
|
||||
}
|
||||
|
||||
// The call code for the slow call.
|
||||
ProcessInput<T>(node, c_arg_count + FastApiCallNode::kFastTargetInputCount,
|
||||
UseInfo::AnyTagged());
|
||||
for (int i = 1; i <= js_arg_count; i++) {
|
||||
ProcessInput<T>(node,
|
||||
c_arg_count + FastApiCallNode::kFastTargetInputCount + i,
|
||||
TruncatingUseInfoFromRepresentation(
|
||||
call_descriptor->GetInputType(i).representation()));
|
||||
}
|
||||
for (int i = c_arg_count + FastApiCallNode::kFastTargetInputCount +
|
||||
js_arg_count;
|
||||
i < value_input_count; ++i) {
|
||||
ProcessInput<T>(node, i, UseInfo::AnyTagged());
|
||||
}
|
||||
ProcessRemainingInputs<T>(node, value_input_count);
|
||||
|
||||
MachineType return_type =
|
||||
MachineTypeFor(c_signature->ReturnInfo().GetType());
|
||||
SetOutput<T>(node, return_type.representation());
|
||||
|
||||
if (lower<T>()) {
|
||||
MachineSignature::Builder builder(graph()->zone(), 1, c_arg_count);
|
||||
builder.AddReturn(return_type);
|
||||
for (int i = 0; i < c_arg_count; ++i) {
|
||||
MachineType machine_type =
|
||||
MachineTypeFor(c_signature->ArgumentInfo(i).GetType());
|
||||
// Here the arg_use_info are indexed starting from 1 because of the
|
||||
// function input, while this loop is only over the actual arguments.
|
||||
DCHECK_EQ(arg_use_info[i].representation(),
|
||||
machine_type.representation());
|
||||
builder.AddParam(machine_type);
|
||||
}
|
||||
|
||||
CallDescriptor* call_descriptor = Linkage::GetSimplifiedCDescriptor(
|
||||
graph()->zone(), builder.Build(), CallDescriptor::kNoFlags);
|
||||
|
||||
call_descriptor->SetCFunctionInfo(c_signature);
|
||||
|
||||
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatching routine for visiting the node {node} with the usage {use}.
|
||||
@ -3686,7 +3686,7 @@ class RepresentationSelector {
|
||||
}
|
||||
|
||||
case IrOpcode::kFastApiCall: {
|
||||
VisitFastApiCall<T>(node);
|
||||
VisitFastApiCall<T>(node, lowering);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/base/lazy-instance.h"
|
||||
#include "src/compiler/linkage.h"
|
||||
#include "src/compiler/opcodes.h"
|
||||
#include "src/compiler/operator.h"
|
||||
#include "src/compiler/types.h"
|
||||
@ -1292,16 +1293,6 @@ const Operator* SimplifiedOperatorBuilder::AssertType(Type type) {
|
||||
"AssertType", 1, 0, 0, 1, 0, 0, type);
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::FastApiCall(
|
||||
const CFunctionInfo* signature, FeedbackSource const& feedback) {
|
||||
// function, c args
|
||||
int value_input_count = signature->ArgumentCount() + 1;
|
||||
return zone()->New<Operator1<FastApiCallParameters>>(
|
||||
IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall",
|
||||
value_input_count, 1, 1, 1, 1, 0,
|
||||
FastApiCallParameters(signature, feedback));
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::CheckIf(
|
||||
DeoptimizeReason reason, const FeedbackSource& feedback) {
|
||||
if (!feedback.IsValid()) {
|
||||
@ -1709,6 +1700,27 @@ CheckIfParameters const& CheckIfParametersOf(Operator const* op) {
|
||||
return OpParameter<CheckIfParameters>(op);
|
||||
}
|
||||
|
||||
FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) {
|
||||
DCHECK_EQ(IrOpcode::kFastApiCall, op->opcode());
|
||||
return OpParameter<FastApiCallParameters>(op);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FastApiCallParameters const& p) {
|
||||
return os << p.signature() << ", " << p.feedback() << ", " << p.descriptor();
|
||||
}
|
||||
|
||||
size_t hash_value(FastApiCallParameters const& p) {
|
||||
return base::hash_combine(p.signature(), FeedbackSource::Hash()(p.feedback()),
|
||||
p.descriptor());
|
||||
}
|
||||
|
||||
bool operator==(FastApiCallParameters const& lhs,
|
||||
FastApiCallParameters const& rhs) {
|
||||
return lhs.signature() == rhs.signature() &&
|
||||
lhs.feedback() == rhs.feedback() &&
|
||||
lhs.descriptor() == rhs.descriptor();
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::NewDoubleElements(
|
||||
AllocationType allocation) {
|
||||
return zone()->New<Operator1<AllocationType>>( // --
|
||||
@ -1744,25 +1756,6 @@ int NewArgumentsElementsMappedCountOf(const Operator* op) {
|
||||
return OpParameter<int>(op);
|
||||
}
|
||||
|
||||
FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) {
|
||||
DCHECK_EQ(IrOpcode::kFastApiCall, op->opcode());
|
||||
return OpParameter<FastApiCallParameters>(op);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FastApiCallParameters const& p) {
|
||||
return os << p.signature() << ", " << p.feedback();
|
||||
}
|
||||
|
||||
size_t hash_value(FastApiCallParameters const& p) {
|
||||
return base::hash_combine(p.signature(),
|
||||
FeedbackSource::Hash()(p.feedback()));
|
||||
}
|
||||
|
||||
bool operator==(FastApiCallParameters const& lhs,
|
||||
FastApiCallParameters const& rhs) {
|
||||
return lhs.signature() == rhs.signature() && lhs.feedback() == rhs.feedback();
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::Allocate(Type type,
|
||||
AllocationType allocation) {
|
||||
return zone()->New<Operator1<AllocateParameters>>(
|
||||
@ -1900,6 +1893,35 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreNonNumberElement(
|
||||
"TransitionAndStoreNonNumberElement", 3, 1, 1, 0, 1, 0, parameters);
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::FastApiCall(
|
||||
const CFunctionInfo* signature, FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor) {
|
||||
int value_input_count =
|
||||
(signature->ArgumentCount() +
|
||||
FastApiCallNode::kFastTargetInputCount) + // fast call
|
||||
static_cast<int>(descriptor->ParameterCount()) + // slow call
|
||||
FastApiCallNode::kEffectAndControlInputCount;
|
||||
return new (zone_) Operator1<FastApiCallParameters>(
|
||||
IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall",
|
||||
value_input_count, 1, 1, 1, 1, 0,
|
||||
FastApiCallParameters(signature, feedback, descriptor));
|
||||
}
|
||||
|
||||
int FastApiCallNode::FastCallArgumentCount() const {
|
||||
FastApiCallParameters p = FastApiCallParametersOf(node()->op());
|
||||
const CFunctionInfo* signature = p.signature();
|
||||
CHECK_NOT_NULL(signature);
|
||||
return signature->ArgumentCount();
|
||||
}
|
||||
|
||||
int FastApiCallNode::SlowCallArgumentCount() const {
|
||||
FastApiCallParameters p = FastApiCallParametersOf(node()->op());
|
||||
CallDescriptor* descriptor = p.descriptor();
|
||||
CHECK_NOT_NULL(descriptor);
|
||||
return static_cast<int>(descriptor->ParameterCount()) +
|
||||
kContextAndFrameStateInputCount;
|
||||
}
|
||||
|
||||
#undef PURE_OP_LIST
|
||||
#undef EFFECT_DEPENDENT_OP_LIST
|
||||
#undef SPECULATIVE_NUMBER_BINOP_LIST
|
||||
|
@ -9,8 +9,11 @@
|
||||
|
||||
#include "src/base/compiler-specific.h"
|
||||
#include "src/codegen/machine-type.h"
|
||||
#include "src/codegen/tnode.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/compiler/feedback-source.h"
|
||||
#include "src/compiler/node-properties.h"
|
||||
#include "src/compiler/node.h"
|
||||
#include "src/compiler/operator.h"
|
||||
#include "src/compiler/types.h"
|
||||
#include "src/compiler/write-barrier-kind.h"
|
||||
@ -22,6 +25,8 @@
|
||||
#include "src/zone/zone-handle-set.h"
|
||||
|
||||
namespace v8 {
|
||||
class CFunctionInfo;
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Forward declarations.
|
||||
@ -33,6 +38,7 @@ namespace compiler {
|
||||
// Forward declarations.
|
||||
class Operator;
|
||||
struct SimplifiedOperatorGlobalCache;
|
||||
class CallDescriptor;
|
||||
|
||||
enum BaseTaggedness : uint8_t { kUntaggedBase, kTaggedBase };
|
||||
|
||||
@ -594,15 +600,18 @@ int NewArgumentsElementsMappedCountOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||
class FastApiCallParameters {
|
||||
public:
|
||||
explicit FastApiCallParameters(const CFunctionInfo* signature,
|
||||
FeedbackSource const& feedback)
|
||||
: signature_(signature), feedback_(feedback) {}
|
||||
FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor)
|
||||
: signature_(signature), feedback_(feedback), descriptor_(descriptor) {}
|
||||
|
||||
const CFunctionInfo* signature() const { return signature_; }
|
||||
FeedbackSource const& feedback() const { return feedback_; }
|
||||
CallDescriptor* descriptor() const { return descriptor_; }
|
||||
|
||||
private:
|
||||
const CFunctionInfo* signature_;
|
||||
const FeedbackSource feedback_;
|
||||
CallDescriptor* descriptor_;
|
||||
};
|
||||
|
||||
FastApiCallParameters const& FastApiCallParametersOf(const Operator* op)
|
||||
@ -959,9 +968,10 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
|
||||
const Operator* DateNow();
|
||||
|
||||
// Stores the signature and feedback of a fast C call
|
||||
// Represents the inputs necessary to construct a fast and a slow API call.
|
||||
const Operator* FastApiCall(const CFunctionInfo* signature,
|
||||
FeedbackSource const& feedback);
|
||||
FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor);
|
||||
|
||||
private:
|
||||
Zone* zone() const { return zone_; }
|
||||
@ -972,6 +982,86 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
DISALLOW_COPY_AND_ASSIGN(SimplifiedOperatorBuilder);
|
||||
};
|
||||
|
||||
#define DEFINE_INPUT_ACCESSORS(Name, name, TheIndex, Type) \
|
||||
static constexpr int Name##Index() { return TheIndex; } \
|
||||
TNode<Type> name() const { \
|
||||
return TNode<Type>::UncheckedCast( \
|
||||
NodeProperties::GetValueInput(node(), TheIndex)); \
|
||||
}
|
||||
|
||||
class FastApiCallNode final : public NodeWrapper {
|
||||
public:
|
||||
explicit constexpr FastApiCallNode(Node* node) : NodeWrapper(node) {
|
||||
CONSTEXPR_DCHECK(node->opcode() == IrOpcode::kFastApiCall);
|
||||
}
|
||||
|
||||
const FastApiCallParameters& Parameters() const {
|
||||
return FastApiCallParametersOf(node()->op());
|
||||
}
|
||||
|
||||
#define INPUTS(V) \
|
||||
V(Target, target, 0, Object) \
|
||||
V(Receiver, receiver, 1, Object)
|
||||
INPUTS(DEFINE_INPUT_ACCESSORS)
|
||||
#undef INPUTS
|
||||
|
||||
// Besides actual arguments, FastApiCall nodes also take:
|
||||
static constexpr int kFastTargetInputCount = 1;
|
||||
static constexpr int kSlowTargetInputCount = 1;
|
||||
static constexpr int kFastReceiverInputCount = 1;
|
||||
static constexpr int kSlowReceiverInputCount = 1;
|
||||
static constexpr int kExtraInputCount =
|
||||
kFastTargetInputCount + kFastReceiverInputCount;
|
||||
|
||||
static constexpr int kHasErrorInputCount = 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 kSlowCallExtraInputCount =
|
||||
kSlowTargetInputCount + kArityInputCount + kNewTargetInputCount +
|
||||
kSlowReceiverInputCount + kHolderInputCount +
|
||||
kContextAndFrameStateInputCount + kEffectAndControlInputCount;
|
||||
|
||||
// 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 +
|
||||
kEffectAndControlInputCount;
|
||||
}
|
||||
|
||||
int FastCallArgumentCount() const;
|
||||
int SlowCallArgumentCount() const;
|
||||
|
||||
constexpr int FirstFastCallArgumentIndex() const {
|
||||
return ReceiverIndex() + 1;
|
||||
}
|
||||
constexpr int FastCallArgumentIndex(int i) const {
|
||||
return FirstFastCallArgumentIndex() + i;
|
||||
}
|
||||
TNode<Object> FastCallArgument(int i) const {
|
||||
DCHECK_LT(i, FastCallArgumentCount());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
NodeProperties::GetValueInput(node(), FastCallArgumentIndex(i)));
|
||||
}
|
||||
|
||||
int FirstSlowCallArgumentIndex() const {
|
||||
return FastCallArgumentCount() + FastApiCallNode::kFastTargetInputCount;
|
||||
}
|
||||
int SlowCallArgumentIndex(int i) const {
|
||||
return FirstSlowCallArgumentIndex() + i;
|
||||
}
|
||||
TNode<Object> SlowCallArgument(int i) const {
|
||||
DCHECK_LT(i, SlowCallArgumentCount());
|
||||
return TNode<Object>::UncheckedCast(
|
||||
NodeProperties::GetValueInput(node(), SlowCallArgumentIndex(i)));
|
||||
}
|
||||
};
|
||||
|
||||
#undef DEFINE_INPUT_ACCESSORS
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1605,8 +1605,9 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckTypeIs(node, Type::BigInt());
|
||||
break;
|
||||
case IrOpcode::kFastApiCall:
|
||||
CHECK_GE(value_count, 1);
|
||||
CheckValueInputIs(node, 0, Type::ExternalPointer());
|
||||
CHECK_GE(value_count, 2);
|
||||
CheckValueInputIs(node, 0, Type::ExternalPointer()); // callee
|
||||
CheckValueInputIs(node, 1, Type::Any()); // receiver
|
||||
break;
|
||||
|
||||
// Machine operators
|
||||
|
@ -27180,66 +27180,88 @@ struct GetDeoptValue<bool> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ApiNumberChecker {
|
||||
enum Result {
|
||||
kNotCalled,
|
||||
kSlowCalled,
|
||||
kFastCalled,
|
||||
};
|
||||
enum class ApiCheckerResult : uint8_t {
|
||||
kNotCalled = 0,
|
||||
kSlowCalled = 1 << 0,
|
||||
kFastCalled = 1 << 1,
|
||||
};
|
||||
using ApiCheckerResultFlags = v8::base::Flags<ApiCheckerResult>;
|
||||
DEFINE_OPERATORS_FOR_FLAGS(ApiCheckerResultFlags)
|
||||
|
||||
explicit ApiNumberChecker(T value) {}
|
||||
|
||||
static void CheckArgFast(ApiNumberChecker<T>* receiver, T argument) {
|
||||
CHECK_NE(receiver, nullptr);
|
||||
receiver->result = kFastCalled;
|
||||
receiver->fast_value = argument;
|
||||
template <typename Value, typename Impl>
|
||||
struct BasicApiChecker {
|
||||
static void FastCallback(BasicApiChecker<Value, Impl>* receiver,
|
||||
Value argument, int* fallback) {
|
||||
Impl::FastCallback(static_cast<Impl*>(receiver), argument, fallback);
|
||||
}
|
||||
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
Impl::SlowCallback(info);
|
||||
}
|
||||
|
||||
static void CheckArgSlow(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
CHECK_EQ(info.Length(), 1);
|
||||
bool DidCallFast() const { return (result_ & ApiCheckerResult::kFastCalled); }
|
||||
bool DidCallSlow() const { return (result_ & ApiCheckerResult::kSlowCalled); }
|
||||
|
||||
ApiCheckerResultFlags result_ = ApiCheckerResult::kNotCalled;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ApiNumberChecker : BasicApiChecker<T, ApiNumberChecker<T>> {
|
||||
explicit ApiNumberChecker(T value, bool raise_exception = false,
|
||||
int args_count = 1)
|
||||
: raise_exception_(raise_exception), args_count_(args_count) {}
|
||||
|
||||
static void FastCallback(ApiNumberChecker<T>* receiver, T argument,
|
||||
int* fallback) {
|
||||
receiver->result_ |= ApiCheckerResult::kFastCalled;
|
||||
receiver->fast_value_ = argument;
|
||||
if (receiver->raise_exception_) {
|
||||
*fallback = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
v8::Object* receiver = v8::Object::Cast(*info.Holder());
|
||||
ApiNumberChecker<T>* checker = static_cast<ApiNumberChecker<T>*>(
|
||||
receiver->GetAlignedPointerFromInternalField(kV8WrapperObjectIndex));
|
||||
CHECK_EQ(info.Length(), checker->args_count_);
|
||||
|
||||
CHECK_NOT_NULL(checker);
|
||||
if (checker->result == kSlowCalled) return;
|
||||
checker->result = kSlowCalled;
|
||||
checker->result_ |= ApiCheckerResult::kSlowCalled;
|
||||
|
||||
LocalContext env;
|
||||
checker->slow_value = GetDeoptValue<T>::Get(info[0], env.local());
|
||||
checker->slow_value_ = GetDeoptValue<T>::Get(info[0], env.local());
|
||||
|
||||
if (checker->raise_exception_) {
|
||||
info.GetIsolate()->ThrowException(v8_str("Callback error"));
|
||||
}
|
||||
}
|
||||
|
||||
T fast_value = T();
|
||||
Maybe<T> slow_value = v8::Nothing<T>();
|
||||
Result result = kNotCalled;
|
||||
T fast_value_ = T();
|
||||
Maybe<T> slow_value_ = v8::Nothing<T>();
|
||||
bool raise_exception_ = false;
|
||||
int args_count_ = 1;
|
||||
};
|
||||
|
||||
enum class Behavior {
|
||||
kSuccess, // The callback function should be called with the expected value,
|
||||
// which == initial.
|
||||
kThrow, // An exception should be thrown by the callback function.
|
||||
kNoException,
|
||||
kException, // An exception should be thrown by the callback function.
|
||||
};
|
||||
|
||||
enum class PathTaken {
|
||||
kFast, // The fast path is taken after optimization.
|
||||
kSlow, // The slow path is taken always.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||
ApiNumberChecker<T>* checker) {
|
||||
template <typename Value, typename Impl>
|
||||
bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||
BasicApiChecker<Value, Impl>* checker, const char* source_code) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
v8::CFunction c_func = v8::CFunction::Make(ApiNumberChecker<T>::CheckArgFast);
|
||||
v8::CFunction c_func = v8::CFunction::MakeRaisesException(
|
||||
BasicApiChecker<Value, Impl>::FastCallback);
|
||||
CHECK_EQ(c_func.ArgumentInfo(0).GetType(),
|
||||
v8::CTypeInfo::Type::kUnwrappedApiObject);
|
||||
|
||||
Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New(
|
||||
isolate, ApiNumberChecker<T>::CheckArgSlow, v8::Local<v8::Value>(),
|
||||
v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
|
||||
v8::SideEffectType::kHasSideEffect, &c_func);
|
||||
isolate, BasicApiChecker<Value, Impl>::SlowCallback,
|
||||
v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 1,
|
||||
v8::ConstructorBehavior::kAllow, v8::SideEffectType::kHasSideEffect,
|
||||
&c_func);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> object_template =
|
||||
v8::ObjectTemplate::New(isolate);
|
||||
@ -27259,41 +27281,49 @@ void SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||
->Global()
|
||||
->Set(env->local(), v8_str("value"), initial_value)
|
||||
.FromJust());
|
||||
CompileRun(
|
||||
"function func(arg) { receiver.api_func(arg); }"
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"func(value);"
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func(value);");
|
||||
v8::Local<v8::Value> result = CompileRun(source_code);
|
||||
if (!try_catch.HasCaught()) {
|
||||
CHECK(result->IsUndefined());
|
||||
}
|
||||
return try_catch.HasCaught();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CallAndCheck(T expected_value, Behavior expected_behavior,
|
||||
PathTaken expected_path, v8::Local<v8::Value> initial_value) {
|
||||
ApiCheckerResultFlags expected_path,
|
||||
v8::Local<v8::Value> initial_value,
|
||||
bool raise_exception = false) {
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(CcTest::isolate());
|
||||
ApiNumberChecker<T> checker(expected_value);
|
||||
ApiNumberChecker<T> checker(expected_value, raise_exception);
|
||||
|
||||
SetupTest<T>(initial_value, &env, &checker);
|
||||
bool has_caught = SetupTest<T, ApiNumberChecker<T>>(
|
||||
initial_value, &env, &checker,
|
||||
"function func(arg) { return receiver.api_func(arg); }"
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"func(value);");
|
||||
checker.result_ = ApiCheckerResult::kNotCalled;
|
||||
|
||||
if (expected_behavior == Behavior::kThrow) {
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK_NE(checker.result, ApiNumberChecker<T>::kFastCalled);
|
||||
} else {
|
||||
CHECK_EQ(try_catch.HasCaught(), false);
|
||||
}
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::TryCatch try_catch(isolate);
|
||||
CompileRun(
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func(value);");
|
||||
|
||||
if (expected_path == PathTaken::kSlow) {
|
||||
// The slow version callback should have been called twice.
|
||||
CHECK_EQ(checker.result, ApiNumberChecker<T>::kSlowCalled);
|
||||
CHECK_EQ(expected_behavior == Behavior::kException, has_caught);
|
||||
CHECK_EQ(expected_path == ApiCheckerResult::kSlowCalled,
|
||||
!checker.DidCallFast());
|
||||
CHECK_EQ(expected_path == ApiCheckerResult::kFastCalled,
|
||||
!checker.DidCallSlow());
|
||||
|
||||
if (expected_behavior != Behavior::kThrow) {
|
||||
T slow_value_typed = checker.slow_value.ToChecked();
|
||||
if (expected_path & ApiCheckerResult::kSlowCalled) {
|
||||
if (expected_behavior != Behavior::kException) {
|
||||
T slow_value_typed = checker.slow_value_.ToChecked();
|
||||
CHECK_EQ(slow_value_typed, expected_value);
|
||||
}
|
||||
} else if (expected_path == PathTaken::kFast) {
|
||||
CHECK_EQ(checker.result, ApiNumberChecker<T>::kFastCalled);
|
||||
CHECK_EQ(checker.fast_value, expected_value);
|
||||
}
|
||||
if (expected_path & ApiCheckerResult::kFastCalled) {
|
||||
CHECK(checker.DidCallFast());
|
||||
CHECK_EQ(checker.fast_value_, expected_value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27301,7 +27331,12 @@ void CallAndDeopt() {
|
||||
LocalContext env;
|
||||
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||
ApiNumberChecker<int32_t> checker(42);
|
||||
SetupTest(initial_value, &env, &checker);
|
||||
SetupTest(initial_value, &env, &checker,
|
||||
"function func(arg) { return receiver.api_func(arg); }"
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"func(value);"
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func(value);");
|
||||
|
||||
v8::Local<v8::Value> function = CompileRun(
|
||||
"try { func(BigInt(42)); } catch(e) {}"
|
||||
@ -27318,28 +27353,31 @@ void CallAndDeopt() {
|
||||
void CallWithLessArguments() {
|
||||
LocalContext env;
|
||||
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||
ApiNumberChecker<int32_t> checker(42);
|
||||
SetupTest(initial_value, &env, &checker);
|
||||
ApiNumberChecker<int32_t> checker(42, false, 0);
|
||||
SetupTest(initial_value, &env, &checker,
|
||||
"function func() { return receiver.api_func(); }"
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"func();"
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func();");
|
||||
|
||||
CompileRun("func();");
|
||||
|
||||
// Passing not enough arguments should go through the slow path.
|
||||
CHECK_EQ(checker.result, ApiNumberChecker<int32_t>::kSlowCalled);
|
||||
// Passing not enough arguments should go through the fast path.
|
||||
CHECK(checker.DidCallFast());
|
||||
}
|
||||
|
||||
void CallWithMoreArguments() {
|
||||
LocalContext env;
|
||||
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||
ApiNumberChecker<int32_t> checker(42);
|
||||
SetupTest(initial_value, &env, &checker);
|
||||
|
||||
CompileRun(
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func(value, value);");
|
||||
ApiNumberChecker<int32_t> checker(42, false, 2);
|
||||
SetupTest(initial_value, &env, &checker,
|
||||
"function func(arg) { receiver.api_func(arg, arg); }"
|
||||
"%PrepareFunctionForOptimization(func);"
|
||||
"func(value);"
|
||||
"%OptimizeFunctionOnNextCall(func);"
|
||||
"func(value);");
|
||||
|
||||
// Passing too many arguments should just ignore the extra ones.
|
||||
CHECK_EQ(checker.result, ApiNumberChecker<int32_t>::kFastCalled);
|
||||
CHECK(checker.DidCallFast());
|
||||
}
|
||||
|
||||
class TestCFunctionInfo : public v8::CFunctionInfo {
|
||||
@ -27372,7 +27410,7 @@ void CheckDynamicTypeInfo() {
|
||||
|
||||
static TestCFunctionInfo type_info;
|
||||
v8::CFunction c_func =
|
||||
v8::CFunction::Make(ApiNumberChecker<bool>::CheckArgFast, &type_info);
|
||||
v8::CFunction::Make(ApiNumberChecker<bool>::FastCallback, &type_info);
|
||||
CHECK_EQ(c_func.ArgumentCount(), 2);
|
||||
CHECK_EQ(c_func.ArgumentInfo(0).GetType(),
|
||||
v8::CTypeInfo::Type::kUnwrappedApiObject);
|
||||
@ -27383,7 +27421,15 @@ void CheckDynamicTypeInfo() {
|
||||
|
||||
namespace v8 {
|
||||
template <typename T>
|
||||
class WrapperTraits<ApiNumberChecker<T>> {
|
||||
class WrapperTraits<BasicApiChecker<T, ApiNumberChecker<T>>> {
|
||||
public:
|
||||
static const void* GetTypeInfo() {
|
||||
static const int tag = 0;
|
||||
return reinterpret_cast<const void*>(&tag);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
class WrapperTraits<int> {
|
||||
public:
|
||||
static const void* GetTypeInfo() {
|
||||
static const int tag = 0;
|
||||
@ -27396,6 +27442,7 @@ class WrapperTraits<ApiNumberChecker<T>> {
|
||||
TEST(FastApiCalls) {
|
||||
#ifndef V8_LITE_MODE
|
||||
if (i::FLAG_jitless) return;
|
||||
if (i::FLAG_turboprop) return;
|
||||
|
||||
i::FLAG_turbo_fast_api_calls = true;
|
||||
i::FLAG_opt = true;
|
||||
@ -27413,90 +27460,121 @@ TEST(FastApiCalls) {
|
||||
LocalContext env;
|
||||
|
||||
// Main cases (the value fits in the type)
|
||||
CallAndCheck<int32_t>(-42, Behavior::kSuccess, PathTaken::kFast, v8_num(-42));
|
||||
CallAndCheck<uint32_t>(i::Smi::kMaxValue, Behavior::kSuccess,
|
||||
PathTaken::kFast, v8_num(i::Smi::kMaxValue));
|
||||
CallAndCheck<int32_t>(-42, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(-42));
|
||||
CallAndCheck<uint32_t>(i::Smi::kMaxValue, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(i::Smi::kMaxValue));
|
||||
#ifdef V8_TARGET_ARCH_X64
|
||||
CallAndCheck<int64_t>(static_cast<int64_t>(i::Smi::kMaxValue) + 1,
|
||||
Behavior::kSuccess, PathTaken::kFast,
|
||||
Behavior::kNoException, ApiCheckerResult::kFastCalled,
|
||||
v8_num(static_cast<int64_t>(i::Smi::kMaxValue) + 1));
|
||||
#endif // V8_TARGET_ARCH_X64
|
||||
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8::Boolean::New(isolate, false));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8::Boolean::New(isolate, true));
|
||||
|
||||
// Corner cases (the value is out of bounds or of different type) - int32_t
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast, v8_num(-0.0));
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(-0.0));
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<double>::quiet_NaN()));
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<double>::infinity()));
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
v8_str("some_string"));
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled, v8_str("some_string"));
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled,
|
||||
v8::Object::New(isolate));
|
||||
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
v8::Array::New(isolate));
|
||||
CallAndCheck<int32_t>(0, Behavior::kThrow, PathTaken::kSlow,
|
||||
CallAndCheck<int32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled, v8::Array::New(isolate));
|
||||
CallAndCheck<int32_t>(0, Behavior::kException, ApiCheckerResult::kSlowCalled,
|
||||
v8::BigInt::New(isolate, 42));
|
||||
CallAndCheck<int32_t>(std::numeric_limits<int32_t>::min(), Behavior::kSuccess,
|
||||
PathTaken::kFast,
|
||||
CallAndCheck<int32_t>(std::numeric_limits<int32_t>::min(),
|
||||
Behavior::kNoException, ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<int32_t>::min()));
|
||||
CallAndCheck<int32_t>(
|
||||
std::numeric_limits<int32_t>::min(), Behavior::kSuccess, PathTaken::kFast,
|
||||
std::numeric_limits<int32_t>::min(), Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(static_cast<double>(std::numeric_limits<int32_t>::max()) + 1));
|
||||
|
||||
CallAndCheck<int32_t>(3, Behavior::kSuccess, PathTaken::kFast, v8_num(3.14));
|
||||
CallAndCheck<int32_t>(3, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(3.14));
|
||||
|
||||
// Corner cases - uint32_t
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast, v8_num(-0.0));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(-0.0));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<double>::quiet_NaN()));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<double>::infinity()));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
v8_str("some_string"));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled, v8_str("some_string"));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled,
|
||||
v8::Object::New(isolate));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||
CallAndCheck<uint32_t>(0, Behavior::kNoException,
|
||||
ApiCheckerResult::kSlowCalled,
|
||||
v8::Array::New(isolate));
|
||||
CallAndCheck<uint32_t>(0, Behavior::kThrow, PathTaken::kSlow,
|
||||
CallAndCheck<uint32_t>(0, Behavior::kException, ApiCheckerResult::kSlowCalled,
|
||||
v8::BigInt::New(isolate, 42));
|
||||
CallAndCheck<uint32_t>(std::numeric_limits<uint32_t>::min(),
|
||||
Behavior::kSuccess, PathTaken::kFast,
|
||||
Behavior::kNoException, ApiCheckerResult::kFastCalled,
|
||||
v8_num(std::numeric_limits<uint32_t>::max() + 1));
|
||||
CallAndCheck<uint32_t>(3, Behavior::kSuccess, PathTaken::kFast, v8_num(3.14));
|
||||
CallAndCheck<uint32_t>(3, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(3.14));
|
||||
|
||||
// Corner cases - bool
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||
v8::Undefined(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||
v8::Null(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast, v8_num(0));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast, v8_num(42));
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast, v8_str(""));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||
v8_str("some_string"));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||
v8::Symbol::New(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8::Undefined(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8::Null(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(0));
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(42));
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_str(""));
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_str("some_string"));
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8::Symbol::New(isolate));
|
||||
CallAndCheck<bool>(false, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8::BigInt::New(isolate, 0));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled,
|
||||
v8::BigInt::New(isolate, 42));
|
||||
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||
v8::Object::New(isolate));
|
||||
CallAndCheck<bool>(true, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8::Object::New(isolate));
|
||||
|
||||
// Check for the deopt loop protection
|
||||
CallAndDeopt();
|
||||
|
||||
CheckDynamicTypeInfo();
|
||||
|
||||
// Fallback to slow call
|
||||
CallAndCheck<int32_t>(
|
||||
42, Behavior::kException,
|
||||
ApiCheckerResult::kFastCalled | ApiCheckerResult::kSlowCalled, v8_num(42),
|
||||
true);
|
||||
|
||||
// Doesn't fallback to slow call
|
||||
CallAndCheck<int32_t>(42, Behavior::kNoException,
|
||||
ApiCheckerResult::kFastCalled, v8_num(42), false);
|
||||
|
||||
// Wrong number of arguments
|
||||
CallWithLessArguments();
|
||||
CallWithMoreArguments();
|
||||
|
||||
CheckDynamicTypeInfo();
|
||||
|
||||
// TODO(mslekova): Add corner cases for 64-bit values.
|
||||
// TODO(mslekova): Add main cases for float and double.
|
||||
// TODO(mslekova): Restructure the tests so that the fast optimized calls
|
||||
|
Loading…
Reference in New Issue
Block a user