[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:
Maya Lekova 2020-07-15 13:54:33 +02:00 committed by Commit Bot
parent 1905c05c10
commit ca1e6573e4
10 changed files with 645 additions and 243 deletions

View File

@ -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...>());
}
};
};

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

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