Commit 2690d465 authored by Paolo Severini's avatar Paolo Severini Committed by V8 LUCI CQ

[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/+/2987599Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#75664}
parent afa61269
......@@ -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; }
......
This diff is collapsed.
// 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
// 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,
feedback(), descriptor),
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,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;
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);
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]);
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);
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];
}
%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]);
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(3, 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(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[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());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment