From 2690d46507027ffa5ae55efb4f5669b99cf14b6e Mon Sep 17 00:00:00 2001 From: Paolo Severini Date: Wed, 7 Jul 2021 17:48:40 -0700 Subject: [PATCH] [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 Reviewed-by: Maya Lekova Reviewed-by: Camillo Bruni Commit-Queue: Paolo Severini Cr-Commit-Position: refs/heads/master@{#75664} --- BUILD.gn | 2 + include/v8-fast-api-calls.h | 10 + src/compiler/effect-control-linearizer.cc | 233 +++++++++++++++++--- src/compiler/fast-api-calls.cc | 84 +++++++ src/compiler/fast-api-calls.h | 50 +++++ src/compiler/js-call-reducer.cc | 51 ++--- src/compiler/linkage.h | 8 - src/compiler/simplified-lowering.cc | 27 +-- src/compiler/simplified-operator.cc | 43 +++- src/compiler/simplified-operator.h | 43 ++-- src/compiler/verifier.cc | 5 +- src/d8/d8-test.cc | 69 ++++++ test/mjsunit/compiler/fast-api-calls.js | 89 ++++---- test/mjsunit/compiler/fast-api-sequences.js | 106 ++++++++- 14 files changed, 666 insertions(+), 154 deletions(-) create mode 100644 src/compiler/fast-api-calls.cc create mode 100644 src/compiler/fast-api-calls.h diff --git a/BUILD.gn b/BUILD.gn index 578fc50cf9..328a652e39 100644 --- a/BUILD.gn +++ b/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", diff --git a/include/v8-fast-api-calls.h b/include/v8-fast-api-calls.h index bdc4417c0b..0cb765431b 100644 --- a/include/v8-fast-api-calls.h +++ b/include/v8-fast-api-calls.h @@ -611,6 +611,16 @@ struct TypeInfoHelper> { } }; +template <> +struct TypeInfoHelper> { + 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 { static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index 2f97f9d9da..fe2774f55e 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -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( + 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(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( - 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( + 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( - 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); } diff --git a/src/compiler/fast-api-calls.cc b/src/compiler/fast-api-calls.cc new file mode 100644 index 0000000000..608fce8606 --- /dev/null +++ b/src/compiler/fast-api-calls.cc @@ -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(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(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(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 diff --git a/src/compiler/fast-api-calls.h b/src/compiler/fast-api-calls.h new file mode 100644 index 0000000000..fd6832e089 --- /dev/null +++ b/src/compiler/fast-api-calls.h @@ -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_ diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index 4b92f10177..c08f91a19f 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -895,10 +895,9 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler { FastApiCallReducerAssembler( JSCallReducer* reducer, Node* node, const FunctionTemplateInfoRef function_template_info, - const ZoneVector> - 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 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( - 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(c_candidate_functions_[0].signature->ArgumentCount()); CHECK_GE(c_argument_count, kReceiver); int cursor = 0; base::SmallVector 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 FastApiCall(CallDescriptor* descriptor, Node** inputs, - size_t inputs_size, size_t c_overloads_index) { + size_t inputs_size) { return AddNode( - graph()->NewNode(simplified()->FastApiCall( - c_candidate_functions_[c_overloads_index].second, - feedback(), descriptor), + graph()->NewNode(simplified()->FastApiCall(c_candidate_functions_, + feedback(), descriptor), static_cast(inputs_size), inputs)); } - const ZoneVector> - 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> CanOptimizeFastCall( +FastApiCallFunctionVector CanOptimizeFastCall( Zone* zone, const FunctionTemplateInfoRef& function_template_info, size_t argc) { - ZoneVector> 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> 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(); diff --git a/src/compiler/linkage.h b/src/compiler/linkage.h index a81e8409ee..8b33444b29 100644 --- a/src/compiler/linkage.h +++ b/src/compiler/linkage.h @@ -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) diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 22e6904d0c..a8e527f9d4 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -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(call_descriptor->ParameterCount()); @@ -1837,28 +1845,21 @@ class RepresentationSelector { base::SmallVector arg_use_info( c_arg_count); - // The target of the fast call. - ProcessInput(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(node, i + FastApiCallNode::kFastTargetInputCount, - arg_use_info[i]); + ProcessInput(node, i, arg_use_info[i]); } // The call code for the slow call. - ProcessInput(node, c_arg_count + FastApiCallNode::kFastTargetInputCount, - UseInfo::AnyTagged()); + ProcessInput(node, c_arg_count, UseInfo::AnyTagged()); for (int i = 1; i <= js_arg_count; i++) { - ProcessInput(node, - c_arg_count + FastApiCallNode::kFastTargetInputCount + i, + ProcessInput(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(node, i, UseInfo::AnyTagged()); } ProcessRemainingInputs(node, value_input_count); diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 51ecff41db..9c4f8f083a 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -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(descriptor->ParameterCount()) + // slow call FastApiCallNode::kEffectAndControlInputCount; return zone()->New>( 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(); } diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index d8aa18206f..d7a5901448 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -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 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; } diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 39132bc17b..248d2fb0ba 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -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: diff --git a/src/d8/d8-test.cc b/src/d8/d8-test.cc index 15d08fa473..395fc61c34 100644 --- a/src/d8/d8-test.cc +++ b/src/d8/d8-test.cc @@ -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 receiver, + bool should_fallback, + Local 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& args) { + // Not implemented. + } + + static int32_t AddAllIntInvalidCallback(Local receiver, + bool should_fallback, int32_t arg_i32, + FastApiCallbackOptions& options) { + // This should never be called + UNREACHABLE(); + } + static int Add32BitIntFastCallback(v8::Local receiver, bool should_fallback, int32_t arg_i32, uint32_t arg_u32, @@ -436,6 +472,39 @@ Local 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(), 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(), + 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(), + 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 = diff --git a/test/mjsunit/compiler/fast-api-calls.js b/test/mjsunit/compiler/fast-api-calls.js index e34777466b..c6dab813cf 100644 --- a/test/mjsunit/compiler/fast-api-calls.js +++ b/test/mjsunit/compiler/fast-api-calls.js @@ -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,51 +179,62 @@ 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 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); - 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); - 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); - 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); +(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); + 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); + 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); + 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); + 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]; + } - return [result_under, result_5args, result_6args, result_over]; -} + %PrepareFunctionForOptimization(overloaded_add_all); + let result = overloaded_add_all(); + 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]); -%PrepareFunctionForOptimization(overloaded_add_all); -let result = overloaded_add_all(); -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]); + fast_c_api.reset_counts(); + %OptimizeFunctionOnNextCall(overloaded_add_all); + result = overloaded_add_all(); + assertOptimized(overloaded_add_all); -fast_c_api.reset_counts(); -%OptimizeFunctionOnNextCall(overloaded_add_all); -result = overloaded_add_all(); -assertOptimized(overloaded_add_all); + // Only the call with less arguments goes falls back to the slow path. + assertEquals(4, fast_c_api.fast_call_count()); + assertEquals(1, fast_c_api.slow_call_count()); -// Only the call with less arguments goes falls back to the slow path. -assertEquals(3, 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[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]); +})(); diff --git a/test/mjsunit/compiler/fast-api-sequences.js b/test/mjsunit/compiler/fast-api-sequences.js index f31d352ac0..8d960de1b1 100644 --- a/test/mjsunit/compiler/fast-api-sequences.js +++ b/test/mjsunit/compiler/fast-api-sequences.js @@ -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()); +})();