[fastcall] Resolve CFunction overloads based on type checks at runtime
This CL implements the resolution of function overloads based on run-time checks of the type of arguments passed to the JS function. For the moment, the only supported overload resolution is between JSArrays and TypedArrays. Bug: v8:11739 Change-Id: Iabb79149f021037470a3adf071d1cccb6f00acd1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2987599 Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Commit-Queue: Paolo Severini <paolosev@microsoft.com> Cr-Commit-Position: refs/heads/master@{#75664}
This commit is contained in:
parent
afa6126921
commit
2690d46507
2
BUILD.gn
2
BUILD.gn
@ -2517,6 +2517,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/compiler/effect-control-linearizer.h",
|
||||
"src/compiler/escape-analysis-reducer.h",
|
||||
"src/compiler/escape-analysis.h",
|
||||
"src/compiler/fast-api-calls.h",
|
||||
"src/compiler/feedback-source.h",
|
||||
"src/compiler/frame-states.h",
|
||||
"src/compiler/frame.h",
|
||||
@ -3517,6 +3518,7 @@ v8_compiler_sources = [
|
||||
"src/compiler/effect-control-linearizer.cc",
|
||||
"src/compiler/escape-analysis-reducer.cc",
|
||||
"src/compiler/escape-analysis.cc",
|
||||
"src/compiler/fast-api-calls.cc",
|
||||
"src/compiler/feedback-source.cc",
|
||||
"src/compiler/frame-states.cc",
|
||||
"src/compiler/frame.cc",
|
||||
|
@ -611,6 +611,16 @@ struct TypeInfoHelper<v8::Local<v8::Array>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeInfoHelper<v8::Local<v8::Uint32Array>> {
|
||||
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
|
||||
|
||||
static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; }
|
||||
static constexpr CTypeInfo::SequenceType SequenceType() {
|
||||
return CTypeInfo::SequenceType::kIsTypedArray;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeInfoHelper<FastApiCallbackOptions&> {
|
||||
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "src/common/ptr-compr-inl.h"
|
||||
#include "src/compiler/access-builder.h"
|
||||
#include "src/compiler/compiler-source-position-table.h"
|
||||
#include "src/compiler/fast-api-calls.h"
|
||||
#include "src/compiler/feedback-source.h"
|
||||
#include "src/compiler/graph-assembler.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
@ -198,10 +199,22 @@ class EffectControlLinearizer {
|
||||
Node* LowerLoadMessage(Node* node);
|
||||
Node* AdaptFastCallArgument(Node* node, CTypeInfo arg_type,
|
||||
GraphAssemblerLabel<0>* if_error);
|
||||
|
||||
struct AdaptOverloadedFastCallResult {
|
||||
Node* target_address;
|
||||
Node* argument;
|
||||
};
|
||||
AdaptOverloadedFastCallResult AdaptOverloadedFastCallArgument(
|
||||
Node* node, const FastApiCallFunctionVector& c_functions,
|
||||
const fast_api_call::OverloadsResolutionResult&
|
||||
overloads_resolution_result,
|
||||
GraphAssemblerLabel<0>* if_error);
|
||||
|
||||
Node* WrapFastCall(const CallDescriptor* call_descriptor, int inputs_size,
|
||||
Node** inputs, Node* target,
|
||||
const CFunctionInfo* c_signature, int c_arg_count,
|
||||
Node* stack_slot);
|
||||
Node* GenerateSlowApiCall(Node* node);
|
||||
Node* LowerFastApiCall(Node* node);
|
||||
Node* LowerLoadTypedElement(Node* node);
|
||||
Node* LowerLoadDataViewElement(Node* node);
|
||||
@ -5040,12 +5053,98 @@ Node* EffectControlLinearizer::AdaptFastCallArgument(
|
||||
|
||||
return stack_slot;
|
||||
}
|
||||
case CTypeInfo::SequenceType::kIsTypedArray:
|
||||
// TODO(mslekova): Implement typed arrays.
|
||||
return node;
|
||||
default: {
|
||||
UNREACHABLE(); // TODO(mslekova): Implement typed arrays.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EffectControlLinearizer::AdaptOverloadedFastCallResult
|
||||
EffectControlLinearizer::AdaptOverloadedFastCallArgument(
|
||||
Node* node, const FastApiCallFunctionVector& c_functions,
|
||||
const fast_api_call::OverloadsResolutionResult& overloads_resolution_result,
|
||||
GraphAssemblerLabel<0>* if_error) {
|
||||
static constexpr int kReceiver = 1;
|
||||
|
||||
auto merge = __ MakeLabel(MachineRepresentation::kTagged);
|
||||
|
||||
int kAlign = alignof(uintptr_t);
|
||||
int kSize = sizeof(uintptr_t);
|
||||
Node* stack_slot = __ StackSlot(kSize, kAlign);
|
||||
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
|
||||
kNoWriteBarrier),
|
||||
stack_slot, 0, node);
|
||||
|
||||
for (size_t func_index = 0; func_index < c_functions.size(); func_index++) {
|
||||
const CFunctionInfo* c_signature = c_functions[func_index].signature;
|
||||
CTypeInfo arg_type = c_signature->ArgumentInfo(
|
||||
overloads_resolution_result.distinguishable_arg_index + kReceiver);
|
||||
|
||||
auto next = __ MakeLabel();
|
||||
|
||||
// Check that the value is a HeapObject.
|
||||
Node* value_is_smi = ObjectIsSmi(node);
|
||||
__ GotoIf(value_is_smi, if_error);
|
||||
|
||||
switch (arg_type.GetSequenceType()) {
|
||||
case CTypeInfo::SequenceType::kIsSequence: {
|
||||
CHECK_EQ(arg_type.GetType(), CTypeInfo::Type::kVoid);
|
||||
|
||||
// Check that the value is a JSArray.
|
||||
Node* value_map = __ LoadField(AccessBuilder::ForMap(), node);
|
||||
Node* value_instance_type =
|
||||
__ LoadField(AccessBuilder::ForMapInstanceType(), value_map);
|
||||
Node* value_is_js_array = __ Word32Equal(
|
||||
value_instance_type, __ Int32Constant(JS_ARRAY_TYPE));
|
||||
__ GotoIfNot(value_is_js_array, &next);
|
||||
|
||||
Node* target_address = __ ExternalConstant(
|
||||
ExternalReference::Create(c_functions[func_index].address));
|
||||
__ Goto(&merge, target_address);
|
||||
break;
|
||||
}
|
||||
|
||||
case CTypeInfo::SequenceType::kIsTypedArray: {
|
||||
// Check that the value is a TypedArray with a type that matches the
|
||||
// type declared in the c-function.
|
||||
ElementsKind typed_array_elements_kind =
|
||||
fast_api_call::GetTypedArrayElementsKind(
|
||||
overloads_resolution_result.element_type);
|
||||
|
||||
Node* value_map = __ LoadField(AccessBuilder::ForMap(), node);
|
||||
Node* value_bit_field2 =
|
||||
__ LoadField(AccessBuilder::ForMapBitField2(), value_map);
|
||||
Node* value_elements_kind = __ WordShr(
|
||||
__ WordAnd(value_bit_field2,
|
||||
__ Int32Constant(Map::Bits2::ElementsKindBits::kMask)),
|
||||
__ Int32Constant(Map::Bits2::ElementsKindBits::kShift));
|
||||
Node* is_same_kind = __ Word32Equal(
|
||||
value_elements_kind,
|
||||
__ Int32Constant(GetPackedElementsKind(typed_array_elements_kind)));
|
||||
__ GotoIfNot(is_same_kind, &next);
|
||||
|
||||
Node* target_address = __ ExternalConstant(
|
||||
ExternalReference::Create(c_functions[func_index].address));
|
||||
__ Goto(&merge, target_address);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
__ Bind(&next);
|
||||
}
|
||||
__ Goto(if_error);
|
||||
|
||||
__ Bind(&merge);
|
||||
return {merge.PhiAt(0), stack_slot};
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::WrapFastCall(
|
||||
const CallDescriptor* call_descriptor, int inputs_size, Node** inputs,
|
||||
Node* target, const CFunctionInfo* c_signature, int c_arg_count,
|
||||
@ -5100,10 +5199,39 @@ Node* EffectControlLinearizer::WrapFastCall(
|
||||
return call;
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::GenerateSlowApiCall(Node* node) {
|
||||
FastApiCallNode n(node);
|
||||
FastApiCallParameters const& params = n.Parameters();
|
||||
const CFunctionInfo* c_signature = params.c_functions()[0].signature;
|
||||
const int c_arg_count = c_signature->ArgumentCount();
|
||||
|
||||
Node** const slow_inputs = graph()->zone()->NewArray<Node*>(
|
||||
n.SlowCallArgumentCount() + FastApiCallNode::kEffectAndControlInputCount);
|
||||
|
||||
int fast_call_params = c_arg_count;
|
||||
CHECK_EQ(node->op()->ValueInputCount() - 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_result = __ Call(
|
||||
params.descriptor(), index + FastApiCallNode::kEffectAndControlInputCount,
|
||||
slow_inputs);
|
||||
return slow_call_result;
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
FastApiCallNode n(node);
|
||||
FastApiCallParameters const& params = n.Parameters();
|
||||
const CFunctionInfo* c_signature = params.signature();
|
||||
|
||||
static constexpr int kReceiver = 1;
|
||||
|
||||
const FastApiCallFunctionVector& c_functions = params.c_functions();
|
||||
const CFunctionInfo* c_signature = params.c_functions()[0].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());
|
||||
@ -5149,26 +5277,82 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
CallDescriptor* call_descriptor =
|
||||
Linkage::GetSimplifiedCDescriptor(graph()->zone(), builder.Build());
|
||||
|
||||
call_descriptor->SetCFunctionInfo(c_signature);
|
||||
|
||||
Node** const inputs = graph()->zone()->NewArray<Node*>(
|
||||
c_arg_count + n.FastCallExtraInputCount());
|
||||
inputs[0] = n.target();
|
||||
|
||||
// Hint to fast path.
|
||||
auto if_success = __ MakeLabel();
|
||||
auto if_error = __ MakeDeferredLabel();
|
||||
|
||||
for (int i = FastApiCallNode::kFastTargetInputCount;
|
||||
i < c_arg_count + FastApiCallNode::kFastTargetInputCount; ++i) {
|
||||
Node* value = NodeProperties::GetValueInput(node, i);
|
||||
CTypeInfo type = c_signature->ArgumentInfo(i - 1);
|
||||
inputs[i] = AdaptFastCallArgument(value, type, &if_error);
|
||||
// Overload resolution
|
||||
|
||||
bool generate_fast_call = false;
|
||||
int distinguishable_arg_index = INT_MIN;
|
||||
fast_api_call::OverloadsResolutionResult overloads_resolution_result =
|
||||
fast_api_call::OverloadsResolutionResult::Invalid();
|
||||
|
||||
if (c_functions.size() == 1) {
|
||||
generate_fast_call = true;
|
||||
} else {
|
||||
DCHECK_EQ(c_functions.size(), 2);
|
||||
overloads_resolution_result = fast_api_call::ResolveOverloads(
|
||||
graph()->zone(), c_functions, c_arg_count);
|
||||
if (overloads_resolution_result.is_valid()) {
|
||||
generate_fast_call = true;
|
||||
distinguishable_arg_index =
|
||||
overloads_resolution_result.distinguishable_arg_index;
|
||||
}
|
||||
}
|
||||
|
||||
Node* c_call_result =
|
||||
WrapFastCall(call_descriptor, c_arg_count + n.FastCallExtraInputCount(),
|
||||
inputs, n.target(), c_signature, c_arg_count, stack_slot);
|
||||
if (!generate_fast_call) {
|
||||
// Only generate the slow call.
|
||||
return GenerateSlowApiCall(node);
|
||||
}
|
||||
|
||||
// Generate fast call.
|
||||
|
||||
const int kFastTargetAddressInputIndex = 0;
|
||||
const int kFastTargetAddressInputCount = 1;
|
||||
|
||||
Node** const inputs = graph()->zone()->NewArray<Node*>(
|
||||
kFastTargetAddressInputCount + c_arg_count + n.FastCallExtraInputCount());
|
||||
|
||||
// The inputs to {Call} node for the fast call look like:
|
||||
// [fast callee, receiver, ... C arguments, [optional Options], effect,
|
||||
// control].
|
||||
//
|
||||
// The first input node represents the target address for the fast call.
|
||||
// If the function is not overloaded (c_functions.size() == 1) this is the
|
||||
// address associated to the first and only element in the c_functions vector.
|
||||
// If there are multiple overloads the value of this input will be set later
|
||||
// with a Phi node created by AdaptOverloadedFastCallArgument.
|
||||
inputs[kFastTargetAddressInputIndex] =
|
||||
(c_functions.size() == 1) ? __ ExternalConstant(ExternalReference::Create(
|
||||
c_functions[0].address))
|
||||
: nullptr;
|
||||
|
||||
for (int i = 0; i < c_arg_count; ++i) {
|
||||
Node* value = NodeProperties::GetValueInput(node, i);
|
||||
|
||||
if (i == distinguishable_arg_index + kReceiver) {
|
||||
// This only happens when the FastApiCall node represents multiple
|
||||
// overloaded functions and {i} is the index of the distinguishable
|
||||
// argument.
|
||||
AdaptOverloadedFastCallResult nodes = AdaptOverloadedFastCallArgument(
|
||||
value, c_functions, overloads_resolution_result, &if_error);
|
||||
inputs[i + kFastTargetAddressInputCount] = nodes.argument;
|
||||
|
||||
// Replace the target address node with a Phi node that represents the
|
||||
// choice between the target addreseses of overloaded functions.
|
||||
inputs[kFastTargetAddressInputIndex] = nodes.target_address;
|
||||
} else {
|
||||
CTypeInfo type = c_signature->ArgumentInfo(i);
|
||||
inputs[i + kFastTargetAddressInputCount] =
|
||||
AdaptFastCallArgument(value, type, &if_error);
|
||||
}
|
||||
}
|
||||
DCHECK_NOT_NULL(inputs[0]);
|
||||
|
||||
Node* c_call_result = WrapFastCall(
|
||||
call_descriptor, c_arg_count + n.FastCallExtraInputCount() + 1, inputs,
|
||||
inputs[0], c_signature, c_arg_count, stack_slot);
|
||||
|
||||
Node* fast_call_result;
|
||||
switch (c_signature->ReturnInfo().GetType()) {
|
||||
@ -5220,22 +5404,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
|
||||
// Generate direct slow call.
|
||||
__ Bind(&if_error);
|
||||
{
|
||||
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_result = __ Call(
|
||||
params.descriptor(),
|
||||
index + FastApiCallNode::kEffectAndControlInputCount, slow_inputs);
|
||||
Node* slow_call_result = GenerateSlowApiCall(node);
|
||||
__ Goto(&merge, slow_call_result);
|
||||
}
|
||||
|
||||
|
84
src/compiler/fast-api-calls.cc
Normal file
84
src/compiler/fast-api-calls.cc
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/compiler/fast-api-calls.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
namespace fast_api_call {
|
||||
|
||||
ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type) {
|
||||
switch (type) {
|
||||
case CTypeInfo::Type::kInt32:
|
||||
return INT32_ELEMENTS;
|
||||
case CTypeInfo::Type::kUint32:
|
||||
return UINT32_ELEMENTS;
|
||||
case CTypeInfo::Type::kInt64:
|
||||
return BIGINT64_ELEMENTS;
|
||||
case CTypeInfo::Type::kUint64:
|
||||
return BIGUINT64_ELEMENTS;
|
||||
case CTypeInfo::Type::kFloat32:
|
||||
return FLOAT32_ELEMENTS;
|
||||
case CTypeInfo::Type::kFloat64:
|
||||
return FLOAT64_ELEMENTS;
|
||||
case CTypeInfo::Type::kVoid:
|
||||
case CTypeInfo::Type::kBool:
|
||||
case CTypeInfo::Type::kV8Value:
|
||||
case CTypeInfo::Type::kApiObject:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OverloadsResolutionResult ResolveOverloads(
|
||||
Zone* zone, const FastApiCallFunctionVector& candidates,
|
||||
unsigned int arg_count) {
|
||||
DCHECK_GT(arg_count, 0);
|
||||
|
||||
static constexpr int kReceiver = 1;
|
||||
|
||||
// Only the case of the overload resolution of two functions, one with a
|
||||
// JSArray param and the other with a typed array param is currently
|
||||
// supported.
|
||||
DCHECK_EQ(candidates.size(), 2);
|
||||
|
||||
for (unsigned int arg_index = 0; arg_index < arg_count - kReceiver;
|
||||
arg_index++) {
|
||||
int index_of_func_with_js_array_arg = -1;
|
||||
int index_of_func_with_typed_array_arg = -1;
|
||||
CTypeInfo::Type element_type = CTypeInfo::Type::kVoid;
|
||||
|
||||
for (size_t i = 0; i < candidates.size(); i++) {
|
||||
const CTypeInfo& type_info =
|
||||
candidates[i].signature->ArgumentInfo(arg_index + kReceiver);
|
||||
CTypeInfo::SequenceType sequence_type = type_info.GetSequenceType();
|
||||
|
||||
if (sequence_type == CTypeInfo::SequenceType::kIsSequence) {
|
||||
DCHECK_LT(index_of_func_with_js_array_arg, 0);
|
||||
index_of_func_with_js_array_arg = static_cast<int>(i);
|
||||
} else if (sequence_type == CTypeInfo::SequenceType::kIsTypedArray) {
|
||||
DCHECK_LT(index_of_func_with_typed_array_arg, 0);
|
||||
index_of_func_with_typed_array_arg = static_cast<int>(i);
|
||||
element_type = type_info.GetType();
|
||||
} else {
|
||||
DCHECK_LT(index_of_func_with_js_array_arg, 0);
|
||||
DCHECK_LT(index_of_func_with_typed_array_arg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (index_of_func_with_js_array_arg >= 0 &&
|
||||
index_of_func_with_typed_array_arg >= 0) {
|
||||
return {static_cast<int>(arg_index), element_type};
|
||||
}
|
||||
}
|
||||
|
||||
// No overload found with a JSArray and a typed array as i-th argument.
|
||||
return OverloadsResolutionResult::Invalid();
|
||||
}
|
||||
|
||||
} // namespace fast_api_call
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
50
src/compiler/fast-api-calls.h
Normal file
50
src/compiler/fast-api-calls.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_COMPILER_FAST_API_CALLS_H_
|
||||
#define V8_COMPILER_FAST_API_CALLS_H_
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/compiler/graph-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
namespace fast_api_call {
|
||||
|
||||
struct OverloadsResolutionResult {
|
||||
static OverloadsResolutionResult Invalid() {
|
||||
return OverloadsResolutionResult(-1, CTypeInfo::Type::kVoid);
|
||||
}
|
||||
|
||||
OverloadsResolutionResult(int distinguishable_arg_index_,
|
||||
CTypeInfo::Type element_type_)
|
||||
: distinguishable_arg_index(distinguishable_arg_index_),
|
||||
element_type(element_type_) {
|
||||
DCHECK(distinguishable_arg_index_ < 0 ||
|
||||
element_type_ != CTypeInfo::Type::kVoid);
|
||||
}
|
||||
|
||||
bool is_valid() const { return distinguishable_arg_index >= 0; }
|
||||
|
||||
// The index of the distinguishable overload argument. Only the case where the
|
||||
// types of this argument is a JSArray vs a TypedArray is supported.
|
||||
int distinguishable_arg_index;
|
||||
|
||||
// The element type in the typed array argument.
|
||||
CTypeInfo::Type element_type;
|
||||
};
|
||||
|
||||
ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type);
|
||||
|
||||
OverloadsResolutionResult ResolveOverloads(
|
||||
Zone* zone, const FastApiCallFunctionVector& candidates,
|
||||
unsigned int arg_count);
|
||||
|
||||
} // namespace fast_api_call
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_FAST_API_CALLS_H_
|
@ -895,10 +895,9 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||
FastApiCallReducerAssembler(
|
||||
JSCallReducer* reducer, Node* node,
|
||||
const FunctionTemplateInfoRef function_template_info,
|
||||
const ZoneVector<std::pair<Address, const CFunctionInfo*>>
|
||||
c_candidate_functions,
|
||||
Node* receiver, Node* holder, const SharedFunctionInfoRef shared,
|
||||
Node* target, const int arity, Node* effect)
|
||||
const FastApiCallFunctionVector& c_candidate_functions, Node* receiver,
|
||||
Node* holder, const SharedFunctionInfoRef shared, Node* target,
|
||||
const int arity, Node* effect)
|
||||
: JSCallReducerAssembler(reducer, node),
|
||||
c_candidate_functions_(c_candidate_functions),
|
||||
function_template_info_(function_template_info),
|
||||
@ -915,24 +914,17 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||
TNode<Object> ReduceFastApiCall() {
|
||||
JSCallNode n(node_ptr());
|
||||
|
||||
// Multiple function overloads not supported yet, always call the first
|
||||
// overload with the same arity.
|
||||
const size_t c_overloads_index = 0;
|
||||
|
||||
// C arguments include the receiver at index 0. Thus C index 1 corresponds
|
||||
// to the JS argument 0, etc.
|
||||
const int c_argument_count = static_cast<int>(
|
||||
c_candidate_functions_[c_overloads_index].second->ArgumentCount());
|
||||
// All functions in c_candidate_functions_ have the same number of
|
||||
// arguments, so extract c_argument_count from the first function.
|
||||
const int c_argument_count =
|
||||
static_cast<int>(c_candidate_functions_[0].signature->ArgumentCount());
|
||||
CHECK_GE(c_argument_count, kReceiver);
|
||||
|
||||
int cursor = 0;
|
||||
base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ +
|
||||
kExtraInputsCount);
|
||||
// Multiple function overloads not supported yet, always call the first
|
||||
// overload.
|
||||
inputs[cursor++] = ExternalConstant(ExternalReference::Create(
|
||||
c_candidate_functions_[c_overloads_index].first));
|
||||
|
||||
inputs[cursor++] = n.receiver();
|
||||
|
||||
// TODO(turbofan): Consider refactoring CFunctionInfo to distinguish
|
||||
@ -986,32 +978,29 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||
|
||||
DCHECK_EQ(cursor, c_argument_count + arity_ + kExtraInputsCount);
|
||||
|
||||
return FastApiCall(call_descriptor, inputs.begin(), inputs.size(),
|
||||
c_overloads_index);
|
||||
return FastApiCall(call_descriptor, inputs.begin(), inputs.size());
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kTarget = 1;
|
||||
static constexpr int kSlowTarget = 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 +
|
||||
kSlowTarget + kEffectAndControl + kContextAndFrameState +
|
||||
kCallCodeDataAndArgc + kHolder + kReceiver;
|
||||
static constexpr int kInlineSize = 12;
|
||||
|
||||
TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs,
|
||||
size_t inputs_size, size_t c_overloads_index) {
|
||||
size_t inputs_size) {
|
||||
return AddNode<Object>(
|
||||
graph()->NewNode(simplified()->FastApiCall(
|
||||
c_candidate_functions_[c_overloads_index].second,
|
||||
graph()->NewNode(simplified()->FastApiCall(c_candidate_functions_,
|
||||
feedback(), descriptor),
|
||||
static_cast<int>(inputs_size), inputs));
|
||||
}
|
||||
|
||||
const ZoneVector<std::pair<Address, const CFunctionInfo*>>
|
||||
c_candidate_functions_;
|
||||
const FastApiCallFunctionVector c_candidate_functions_;
|
||||
const FunctionTemplateInfoRef function_template_info_;
|
||||
Node* const receiver_;
|
||||
Node* const holder_;
|
||||
@ -3580,10 +3569,10 @@ bool Has64BitIntegerParamsInSignature(const CFunctionInfo* c_signature) {
|
||||
// Returns an array with the indexes of the remaining entries in S, which
|
||||
// represents the set of "optimizable" function overloads.
|
||||
|
||||
ZoneVector<std::pair<Address, const CFunctionInfo*>> CanOptimizeFastCall(
|
||||
FastApiCallFunctionVector CanOptimizeFastCall(
|
||||
Zone* zone, const FunctionTemplateInfoRef& function_template_info,
|
||||
size_t argc) {
|
||||
ZoneVector<std::pair<Address, const CFunctionInfo*>> result(zone);
|
||||
FastApiCallFunctionVector result(zone);
|
||||
if (!FLAG_turbo_fast_api_calls) return result;
|
||||
|
||||
static constexpr int kReceiver = 1;
|
||||
@ -3798,8 +3787,12 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
ZoneVector<std::pair<Address, const CFunctionInfo*>> c_candidate_functions =
|
||||
// Handles overloaded functions.
|
||||
|
||||
FastApiCallFunctionVector c_candidate_functions =
|
||||
CanOptimizeFastCall(graph()->zone(), function_template_info, argc);
|
||||
DCHECK_LE(c_candidate_functions.size(), 2);
|
||||
|
||||
if (!c_candidate_functions.empty()) {
|
||||
FastApiCallReducerAssembler a(this, node, function_template_info,
|
||||
c_candidate_functions, receiver, holder,
|
||||
@ -3810,6 +3803,8 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
return Replace(fast_call_subgraph);
|
||||
}
|
||||
|
||||
// Slow call
|
||||
|
||||
CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
|
||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||
|
@ -418,13 +418,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
||||
return allocatable_registers_ != 0;
|
||||
}
|
||||
|
||||
// Stores the signature information for a fast API call - C++ functions
|
||||
// that can be called directly from TurboFan.
|
||||
void SetCFunctionInfo(const CFunctionInfo* c_function_info) {
|
||||
c_function_info_ = c_function_info;
|
||||
}
|
||||
const CFunctionInfo* GetCFunctionInfo() const { return c_function_info_; }
|
||||
|
||||
private:
|
||||
friend class Linkage;
|
||||
|
||||
@ -443,7 +436,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
||||
const Flags flags_;
|
||||
const StackArgumentOrder stack_order_;
|
||||
const char* const debug_name_;
|
||||
const CFunctionInfo* c_function_info_ = nullptr;
|
||||
};
|
||||
|
||||
DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags)
|
||||
|
@ -1815,8 +1815,11 @@ class RepresentationSelector {
|
||||
CHECK_EQ(type.GetType(), CTypeInfo::Type::kVoid);
|
||||
return UseInfo::AnyTagged();
|
||||
}
|
||||
case CTypeInfo::SequenceType::kIsTypedArray: {
|
||||
return UseInfo::AnyTagged();
|
||||
}
|
||||
default: {
|
||||
UNREACHABLE(); // TODO(mslekova): Implement typed arrays.
|
||||
UNREACHABLE(); // TODO(mslekova): Implement array buffers.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1827,7 +1830,12 @@ class RepresentationSelector {
|
||||
void VisitFastApiCall(Node* node, SimplifiedLowering* lowering) {
|
||||
FastApiCallParameters const& op_params =
|
||||
FastApiCallParametersOf(node->op());
|
||||
const CFunctionInfo* c_signature = op_params.signature();
|
||||
// We only consider the first function signature here. In case of function
|
||||
// overloads, we only support the case of two functions that differ for one
|
||||
// argument, which must be a JSArray in one function and a TypedArray in the
|
||||
// other function, and both JSArrays and TypedArrays have the same UseInfo
|
||||
// UseInfo::AnyTagged(). All the other argument types must match.
|
||||
const CFunctionInfo* c_signature = op_params.c_functions()[0].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());
|
||||
@ -1837,28 +1845,21 @@ class RepresentationSelector {
|
||||
|
||||
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), op_params.feedback());
|
||||
ProcessInput<T>(node, i + FastApiCallNode::kFastTargetInputCount,
|
||||
arg_use_info[i]);
|
||||
ProcessInput<T>(node, i, arg_use_info[i]);
|
||||
}
|
||||
|
||||
// The call code for the slow call.
|
||||
ProcessInput<T>(node, c_arg_count + FastApiCallNode::kFastTargetInputCount,
|
||||
UseInfo::AnyTagged());
|
||||
ProcessInput<T>(node, c_arg_count, UseInfo::AnyTagged());
|
||||
for (int i = 1; i <= js_arg_count; i++) {
|
||||
ProcessInput<T>(node,
|
||||
c_arg_count + FastApiCallNode::kFastTargetInputCount + i,
|
||||
ProcessInput<T>(node, c_arg_count + i,
|
||||
TruncatingUseInfoFromRepresentation(
|
||||
call_descriptor->GetInputType(i).representation()));
|
||||
}
|
||||
for (int i = c_arg_count + FastApiCallNode::kFastTargetInputCount +
|
||||
js_arg_count;
|
||||
i < value_input_count; ++i) {
|
||||
for (int i = c_arg_count + js_arg_count; i < value_input_count; ++i) {
|
||||
ProcessInput<T>(node, i, UseInfo::AnyTagged());
|
||||
}
|
||||
ProcessRemainingInputs<T>(node, value_input_count);
|
||||
|
@ -1752,17 +1752,26 @@ FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FastApiCallParameters const& p) {
|
||||
return os << p.signature() << ", " << p.feedback() << ", " << p.descriptor();
|
||||
const auto& c_functions = p.c_functions();
|
||||
for (size_t i = 0; i < c_functions.size(); i++) {
|
||||
os << c_functions[i].address << ":" << c_functions[i].signature << ", ";
|
||||
}
|
||||
return os << p.feedback() << ", " << p.descriptor();
|
||||
}
|
||||
|
||||
size_t hash_value(FastApiCallParameters const& p) {
|
||||
return base::hash_combine(p.signature(), FeedbackSource::Hash()(p.feedback()),
|
||||
const auto& c_functions = p.c_functions();
|
||||
size_t hash = 0;
|
||||
for (size_t i = 0; i < c_functions.size(); i++) {
|
||||
hash = base::hash_combine(c_functions[i].address, c_functions[i].signature);
|
||||
}
|
||||
return base::hash_combine(hash, FeedbackSource::Hash()(p.feedback()),
|
||||
p.descriptor());
|
||||
}
|
||||
|
||||
bool operator==(FastApiCallParameters const& lhs,
|
||||
FastApiCallParameters const& rhs) {
|
||||
return lhs.signature() == rhs.signature() &&
|
||||
return lhs.c_functions() == rhs.c_functions() &&
|
||||
lhs.feedback() == rhs.feedback() &&
|
||||
lhs.descriptor() == rhs.descriptor();
|
||||
}
|
||||
@ -1954,27 +1963,39 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreNonNumberElement(
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::FastApiCall(
|
||||
const CFunctionInfo* signature, FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor) {
|
||||
const FastApiCallFunctionVector& c_functions,
|
||||
FeedbackSource const& feedback, CallDescriptor* descriptor) {
|
||||
DCHECK(!c_functions.empty());
|
||||
|
||||
// All function overloads have the same number of arguments and options.
|
||||
const CFunctionInfo* signature = c_functions[0].signature;
|
||||
const int argument_count = signature->ArgumentCount();
|
||||
for (size_t i = 1; i < c_functions.size(); i++) {
|
||||
CHECK_NOT_NULL(c_functions[i].signature);
|
||||
DCHECK_EQ(c_functions[i].signature->ArgumentCount(), argument_count);
|
||||
DCHECK_EQ(c_functions[i].signature->HasOptions(),
|
||||
c_functions[0].signature->HasOptions());
|
||||
}
|
||||
|
||||
int value_input_count =
|
||||
(signature->ArgumentCount() +
|
||||
FastApiCallNode::kFastTargetInputCount) + // fast call
|
||||
argument_count +
|
||||
static_cast<int>(descriptor->ParameterCount()) + // slow call
|
||||
FastApiCallNode::kEffectAndControlInputCount;
|
||||
return zone()->New<Operator1<FastApiCallParameters>>(
|
||||
IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall",
|
||||
value_input_count, 1, 1, 1, 1, 0,
|
||||
FastApiCallParameters(signature, feedback, descriptor));
|
||||
FastApiCallParameters(c_functions, feedback, descriptor));
|
||||
}
|
||||
|
||||
int FastApiCallNode::FastCallExtraInputCount() const {
|
||||
return kFastTargetInputCount + kEffectAndControlInputCount +
|
||||
(Parameters().signature()->HasOptions() ? 1 : 0);
|
||||
const CFunctionInfo* signature = Parameters().c_functions()[0].signature;
|
||||
CHECK_NOT_NULL(signature);
|
||||
return kEffectAndControlInputCount + (signature->HasOptions() ? 1 : 0);
|
||||
}
|
||||
|
||||
int FastApiCallNode::FastCallArgumentCount() const {
|
||||
FastApiCallParameters p = FastApiCallParametersOf(node()->op());
|
||||
const CFunctionInfo* signature = p.signature();
|
||||
const CFunctionInfo* signature = p.c_functions()[0].signature;
|
||||
CHECK_NOT_NULL(signature);
|
||||
return signature->ArgumentCount();
|
||||
}
|
||||
|
@ -696,19 +696,33 @@ std::ostream& operator<<(std::ostream&, const NewArgumentsElementsParameters&);
|
||||
const NewArgumentsElementsParameters& NewArgumentsElementsParametersOf(
|
||||
const Operator*) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
struct FastApiCallFunction {
|
||||
Address address;
|
||||
const CFunctionInfo* signature;
|
||||
|
||||
bool operator==(const FastApiCallFunction& rhs) const {
|
||||
return address == rhs.address && signature == rhs.signature;
|
||||
}
|
||||
};
|
||||
typedef ZoneVector<FastApiCallFunction> FastApiCallFunctionVector;
|
||||
|
||||
class FastApiCallParameters {
|
||||
public:
|
||||
explicit FastApiCallParameters(const CFunctionInfo* signature,
|
||||
explicit FastApiCallParameters(const FastApiCallFunctionVector& c_functions,
|
||||
FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor)
|
||||
: signature_(signature), feedback_(feedback), descriptor_(descriptor) {}
|
||||
: c_functions_(c_functions),
|
||||
feedback_(feedback),
|
||||
descriptor_(descriptor) {}
|
||||
|
||||
const CFunctionInfo* signature() const { return signature_; }
|
||||
const FastApiCallFunctionVector& c_functions() const { return c_functions_; }
|
||||
FeedbackSource const& feedback() const { return feedback_; }
|
||||
CallDescriptor* descriptor() const { return descriptor_; }
|
||||
|
||||
private:
|
||||
const CFunctionInfo* signature_;
|
||||
// A single FastApiCall node can represent multiple overloaded functions.
|
||||
const FastApiCallFunctionVector c_functions_;
|
||||
|
||||
const FeedbackSource feedback_;
|
||||
CallDescriptor* descriptor_;
|
||||
};
|
||||
@ -1094,9 +1108,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
const Operator* DateNow();
|
||||
|
||||
// Represents the inputs necessary to construct a fast and a slow API call.
|
||||
const Operator* FastApiCall(const CFunctionInfo* signature,
|
||||
FeedbackSource const& feedback,
|
||||
CallDescriptor* descriptor);
|
||||
const Operator* FastApiCall(
|
||||
const FastApiCallFunctionVector& c_candidate_functions,
|
||||
FeedbackSource const& feedback, CallDescriptor* descriptor);
|
||||
|
||||
private:
|
||||
Zone* zone() const { return zone_; }
|
||||
@ -1156,19 +1170,15 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
|
||||
return FastApiCallParametersOf(node()->op());
|
||||
}
|
||||
|
||||
#define INPUTS(V) \
|
||||
V(Target, target, 0, Object) \
|
||||
V(Receiver, receiver, 1, Object)
|
||||
#define INPUTS(V) V(Receiver, receiver, 0, 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 kExtraInputCount = kFastReceiverInputCount;
|
||||
|
||||
static constexpr int kArityInputCount = 1;
|
||||
static constexpr int kNewTargetInputCount = 1;
|
||||
@ -1185,8 +1195,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
|
||||
|
||||
// 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;
|
||||
return c_arg_count + js_arg_count + kEffectAndControlInputCount;
|
||||
}
|
||||
|
||||
int FastCallArgumentCount() const;
|
||||
@ -1204,9 +1213,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
|
||||
NodeProperties::GetValueInput(node(), FastCallArgumentIndex(i)));
|
||||
}
|
||||
|
||||
int FirstSlowCallArgumentIndex() const {
|
||||
return FastCallArgumentCount() + FastApiCallNode::kFastTargetInputCount;
|
||||
}
|
||||
int FirstSlowCallArgumentIndex() const { return FastCallArgumentCount(); }
|
||||
int SlowCallArgumentIndex(int i) const {
|
||||
return FirstSlowCallArgumentIndex() + i;
|
||||
}
|
||||
|
@ -1627,9 +1627,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckTypeIs(node, Type::BigInt());
|
||||
break;
|
||||
case IrOpcode::kFastApiCall:
|
||||
CHECK_GE(value_count, 2);
|
||||
CheckValueInputIs(node, 0, Type::ExternalPointer()); // callee
|
||||
CheckValueInputIs(node, 1, Type::Any()); // receiver
|
||||
CHECK_GE(value_count, 1);
|
||||
CheckValueInputIs(node, 0, Type::Any()); // receiver
|
||||
break;
|
||||
#if V8_ENABLE_WEBASSEMBLY
|
||||
case IrOpcode::kJSWasmCall:
|
||||
|
@ -148,6 +148,12 @@ class FastCApiObject {
|
||||
isolate->ThrowError("This method expects at least 2 arguments.");
|
||||
return;
|
||||
}
|
||||
if (args[1]->IsTypedArray()) {
|
||||
// Not supported yet.
|
||||
Type dummy_result = 0;
|
||||
args.GetReturnValue().Set(Number::New(isolate, dummy_result));
|
||||
return;
|
||||
}
|
||||
if (!args[1]->IsArray()) {
|
||||
isolate->ThrowError("This method expects an array as a second argument.");
|
||||
return;
|
||||
@ -176,6 +182,36 @@ class FastCApiObject {
|
||||
args.GetReturnValue().Set(Number::New(isolate, sum));
|
||||
}
|
||||
|
||||
// TODO(mslekova) - The typed array param should be a
|
||||
// {size_t length, uint32_t* data}
|
||||
static Type AddAllTypedArrayFastCallback(Local<Object> receiver,
|
||||
bool should_fallback,
|
||||
Local<Uint32Array> typed_array_arg,
|
||||
FastApiCallbackOptions& options) {
|
||||
FastCApiObject* self = UnwrapObject(receiver);
|
||||
CHECK_SELF_OR_FALLBACK(0);
|
||||
self->fast_call_count_++;
|
||||
|
||||
if (should_fallback) {
|
||||
options.fallback = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not implemented.
|
||||
return 0;
|
||||
}
|
||||
static void AddAllTypedArraySlowCallback(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
// Not implemented.
|
||||
}
|
||||
|
||||
static int32_t AddAllIntInvalidCallback(Local<Object> receiver,
|
||||
bool should_fallback, int32_t arg_i32,
|
||||
FastApiCallbackOptions& options) {
|
||||
// This should never be called
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static int Add32BitIntFastCallback(v8::Local<v8::Object> receiver,
|
||||
bool should_fallback, int32_t arg_i32,
|
||||
uint32_t arg_u32,
|
||||
@ -436,6 +472,39 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
|
||||
signature, 1, ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, &add_all_seq_c_func));
|
||||
|
||||
CFunction add_all_typed_array_c_func =
|
||||
CFunction::Make(FastCApiObject::AddAllTypedArrayFastCallback);
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "add_all_typed_array",
|
||||
FunctionTemplate::New(
|
||||
isolate, FastCApiObject::AddAllTypedArraySlowCallback,
|
||||
Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, &add_all_typed_array_c_func));
|
||||
|
||||
const CFunction add_all_overloads[] = {
|
||||
add_all_typed_array_c_func,
|
||||
add_all_seq_c_func,
|
||||
};
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "add_all_overload",
|
||||
FunctionTemplate::NewWithCFunctionOverloads(
|
||||
isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(),
|
||||
signature, 1, ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, {add_all_overloads, 2}));
|
||||
|
||||
CFunction add_all_int_invalid_func =
|
||||
CFunction::Make(FastCApiObject::AddAllIntInvalidCallback);
|
||||
const CFunction add_all_invalid_overloads[] = {
|
||||
add_all_int_invalid_func,
|
||||
add_all_seq_c_func,
|
||||
};
|
||||
api_obj_ctor->PrototypeTemplate()->Set(
|
||||
isolate, "add_all_invalid_overload",
|
||||
FunctionTemplate::NewWithCFunctionOverloads(
|
||||
isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(),
|
||||
signature, 1, ConstructorBehavior::kThrow,
|
||||
SideEffectType::kHasSideEffect, {add_all_invalid_overloads, 2}));
|
||||
|
||||
CFunction add_all_32bit_int_6args_c_func =
|
||||
CFunction::Make(FastCApiObject::AddAll32BitIntFastCallback_6Args);
|
||||
CFunction add_all_32bit_int_5args_c_func =
|
||||
|
@ -126,8 +126,8 @@ assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
|
||||
// ----------- Test various signature mismatches -----------
|
||||
function add_32bit_int_mismatch(arg0, arg1, arg2, arg3) {
|
||||
return fast_c_api.add_32bit_int(arg0, arg1, arg2, arg3);
|
||||
function add_32bit_int_mismatch(arg0, arg1, arg2) {
|
||||
return fast_c_api.add_32bit_int(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_32bit_int_mismatch);
|
||||
@ -179,32 +179,40 @@ assertOptimized(add_32bit_int_mismatch);
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
assertEquals(0, fast_c_api.slow_call_count());
|
||||
|
||||
// Test function overloads
|
||||
// Test function overloads with different arity.
|
||||
const add_all_32bit_int_arg1 = -42;
|
||||
const add_all_32bit_int_arg2 = 45;
|
||||
const add_all_32bit_int_arg3 = -12345678;
|
||||
const add_all_32bit_int_arg4 = 0x1fffffff;
|
||||
const add_all_32bit_int_arg5 = 1e6;
|
||||
const add_all_32bit_int_arg6 = 1e8;
|
||||
const add_all_32bit_int_result_4args = add_all_32bit_int_arg1 + add_all_32bit_int_arg2 + add_all_32bit_int_arg3 +
|
||||
add_all_32bit_int_arg4;
|
||||
const add_all_32bit_int_result_5args = add_all_32bit_int_result_4args + add_all_32bit_int_arg5;
|
||||
const add_all_32bit_int_result_6args = add_all_32bit_int_result_5args + add_all_32bit_int_arg6;
|
||||
const add_all_32bit_int_result_4args = add_all_32bit_int_arg1 +
|
||||
add_all_32bit_int_arg2 + add_all_32bit_int_arg3 + add_all_32bit_int_arg4;
|
||||
const add_all_32bit_int_result_5args = add_all_32bit_int_result_4args +
|
||||
add_all_32bit_int_arg5;
|
||||
const add_all_32bit_int_result_6args = add_all_32bit_int_result_5args +
|
||||
add_all_32bit_int_arg6;
|
||||
|
||||
(function () {
|
||||
function overloaded_add_all(should_fallback = false) {
|
||||
let result_under = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3, add_all_32bit_int_arg4);
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
|
||||
add_all_32bit_int_arg4);
|
||||
let result_5args = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3, add_all_32bit_int_arg4,
|
||||
add_all_32bit_int_arg5);
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
|
||||
add_all_32bit_int_arg4, add_all_32bit_int_arg5);
|
||||
let result_6args = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3, add_all_32bit_int_arg4,
|
||||
add_all_32bit_int_arg5, add_all_32bit_int_arg6);
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
|
||||
add_all_32bit_int_arg4, add_all_32bit_int_arg5, add_all_32bit_int_arg6);
|
||||
let result_over = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3, add_all_32bit_int_arg4,
|
||||
add_all_32bit_int_arg5, add_all_32bit_int_arg6, 42);
|
||||
|
||||
return [result_under, result_5args, result_6args, result_over];
|
||||
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
|
||||
add_all_32bit_int_arg4, add_all_32bit_int_arg5, add_all_32bit_int_arg6,
|
||||
42);
|
||||
let result_5args_with_undefined = fast_c_api.overloaded_add_all_32bit_int(
|
||||
should_fallback, add_all_32bit_int_arg1, add_all_32bit_int_arg2,
|
||||
add_all_32bit_int_arg3, add_all_32bit_int_arg4, undefined);
|
||||
return [result_under, result_5args, result_6args, result_over,
|
||||
result_5args_with_undefined];
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_add_all);
|
||||
@ -213,6 +221,7 @@ assertEquals(add_all_32bit_int_result_4args, result[0]);
|
||||
assertEquals(add_all_32bit_int_result_5args, result[1]);
|
||||
assertEquals(add_all_32bit_int_result_6args, result[2]);
|
||||
assertEquals(add_all_32bit_int_result_6args, result[3]);
|
||||
assertEquals(add_all_32bit_int_result_4args, result[4]);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_add_all);
|
||||
@ -220,10 +229,12 @@ result = overloaded_add_all();
|
||||
assertOptimized(overloaded_add_all);
|
||||
|
||||
// Only the call with less arguments goes falls back to the slow path.
|
||||
assertEquals(3, fast_c_api.fast_call_count());
|
||||
assertEquals(4, fast_c_api.fast_call_count());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
|
||||
assertEquals(add_all_32bit_int_result_4args, result[0]);
|
||||
assertEquals(add_all_32bit_int_result_5args, result[1]);
|
||||
assertEquals(add_all_32bit_int_result_6args, result[2]);
|
||||
assertEquals(add_all_32bit_int_result_6args, result[3]);
|
||||
assertEquals(add_all_32bit_int_result_4args, result[4]);
|
||||
})();
|
||||
|
@ -63,8 +63,7 @@ if (fast_c_api.supports_fp_params) {
|
||||
}
|
||||
|
||||
function add_all_sequence_mismatch(arg) {
|
||||
return fast_c_api.add_all_sequence(false /*should_fallback*/,
|
||||
arg);
|
||||
return fast_c_api.add_all_sequence(false /*should_fallback*/, arg);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(add_all_sequence_mismatch);
|
||||
@ -95,3 +94,106 @@ assertThrows(() => add_all_sequence_mismatch(Symbol()));
|
||||
assertOptimized(add_all_sequence_mismatch);
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
assertEquals(1, fast_c_api.slow_call_count());
|
||||
|
||||
|
||||
//----------- Test function overloads with same arity. -----------
|
||||
//Only overloads between JSArray and TypedArray are supported
|
||||
|
||||
// Test with TypedArray.
|
||||
(function () {
|
||||
function overloaded_test(should_fallback = false) {
|
||||
let typed_array = new Uint32Array([1, 2, 3]);
|
||||
return fast_c_api.add_all_overload(false /* should_fallback */,
|
||||
typed_array);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_test);
|
||||
let result = overloaded_test();
|
||||
assertEquals(0, result);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_test);
|
||||
result = overloaded_test();
|
||||
assertEquals(0, result);
|
||||
assertOptimized(overloaded_test);
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
})();
|
||||
|
||||
// Mismatched TypedArray.
|
||||
(function () {
|
||||
function overloaded_test(should_fallback = false) {
|
||||
let typed_array = new Float32Array([1.1, 2.2, 3.3]);
|
||||
return fast_c_api.add_all_overload(false /* should_fallback */,
|
||||
typed_array);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_test);
|
||||
let result = overloaded_test();
|
||||
assertEquals(0, result);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_test);
|
||||
result = overloaded_test();
|
||||
assertEquals(0, result);
|
||||
assertOptimized(overloaded_test);
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
})();
|
||||
|
||||
// Test with JSArray.
|
||||
(function () {
|
||||
function overloaded_test(should_fallback = false) {
|
||||
let js_array = [26, -6, 42];
|
||||
return fast_c_api.add_all_overload(false /* should_fallback */, js_array);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_test);
|
||||
let result = overloaded_test();
|
||||
assertEquals(62, result);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_test);
|
||||
result = overloaded_test();
|
||||
assertEquals(62, result);
|
||||
assertOptimized(overloaded_test);
|
||||
assertEquals(1, fast_c_api.fast_call_count());
|
||||
})();
|
||||
|
||||
// Test function overloads with undefined.
|
||||
(function () {
|
||||
function overloaded_test(should_fallback = false) {
|
||||
return fast_c_api.add_all_overload(false /* should_fallback */, undefined);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_test);
|
||||
assertThrows(() => overloaded_test());
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_test);
|
||||
assertThrows(() => overloaded_test());
|
||||
assertOptimized(overloaded_test);
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
})();
|
||||
|
||||
// Test function with invalid overloads.
|
||||
(function () {
|
||||
function overloaded_test(should_fallback = false) {
|
||||
return fast_c_api.add_all_invalid_overload(false /* should_fallback */,
|
||||
[26, -6, 42]);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(overloaded_test);
|
||||
result = overloaded_test();
|
||||
assertEquals(62, result);
|
||||
|
||||
fast_c_api.reset_counts();
|
||||
%OptimizeFunctionOnNextCall(overloaded_test);
|
||||
result = overloaded_test();
|
||||
assertEquals(62, result);
|
||||
// Here we deopt because with this invalid overload:
|
||||
// - add_all_int_invalid_func(Receiver, Bool, Int32, Options)
|
||||
// - add_all_seq_c_func(Receiver, Bool, JSArray, Options)
|
||||
// we expect that a number will be passed as 3rd argument
|
||||
// (SimplifiedLowering takes the type from the first overloaded function).
|
||||
assertUnoptimized(overloaded_test);
|
||||
assertEquals(0, fast_c_api.fast_call_count());
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user