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") { ...@@ -2517,6 +2517,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/effect-control-linearizer.h", "src/compiler/effect-control-linearizer.h",
"src/compiler/escape-analysis-reducer.h", "src/compiler/escape-analysis-reducer.h",
"src/compiler/escape-analysis.h", "src/compiler/escape-analysis.h",
"src/compiler/fast-api-calls.h",
"src/compiler/feedback-source.h", "src/compiler/feedback-source.h",
"src/compiler/frame-states.h", "src/compiler/frame-states.h",
"src/compiler/frame.h", "src/compiler/frame.h",
...@@ -3517,6 +3518,7 @@ v8_compiler_sources = [ ...@@ -3517,6 +3518,7 @@ v8_compiler_sources = [
"src/compiler/effect-control-linearizer.cc", "src/compiler/effect-control-linearizer.cc",
"src/compiler/escape-analysis-reducer.cc", "src/compiler/escape-analysis-reducer.cc",
"src/compiler/escape-analysis.cc", "src/compiler/escape-analysis.cc",
"src/compiler/fast-api-calls.cc",
"src/compiler/feedback-source.cc", "src/compiler/feedback-source.cc",
"src/compiler/frame-states.cc", "src/compiler/frame-states.cc",
"src/compiler/frame.cc", "src/compiler/frame.cc",
......
...@@ -611,6 +611,16 @@ struct TypeInfoHelper<v8::Local<v8::Array>> { ...@@ -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 <> template <>
struct TypeInfoHelper<FastApiCallbackOptions&> { struct TypeInfoHelper<FastApiCallbackOptions&> {
static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } 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 { ...@@ -895,10 +895,9 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
FastApiCallReducerAssembler( FastApiCallReducerAssembler(
JSCallReducer* reducer, Node* node, JSCallReducer* reducer, Node* node,
const FunctionTemplateInfoRef function_template_info, const FunctionTemplateInfoRef function_template_info,
const ZoneVector<std::pair<Address, const CFunctionInfo*>> const FastApiCallFunctionVector& c_candidate_functions, Node* receiver,
c_candidate_functions, Node* holder, const SharedFunctionInfoRef shared, Node* target,
Node* receiver, Node* holder, const SharedFunctionInfoRef shared, const int arity, Node* effect)
Node* target, const int arity, Node* effect)
: JSCallReducerAssembler(reducer, node), : JSCallReducerAssembler(reducer, node),
c_candidate_functions_(c_candidate_functions), c_candidate_functions_(c_candidate_functions),
function_template_info_(function_template_info), function_template_info_(function_template_info),
...@@ -915,24 +914,17 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler { ...@@ -915,24 +914,17 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
TNode<Object> ReduceFastApiCall() { TNode<Object> ReduceFastApiCall() {
JSCallNode n(node_ptr()); 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 // C arguments include the receiver at index 0. Thus C index 1 corresponds
// to the JS argument 0, etc. // to the JS argument 0, etc.
const int c_argument_count = static_cast<int>( // All functions in c_candidate_functions_ have the same number of
c_candidate_functions_[c_overloads_index].second->ArgumentCount()); // 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); CHECK_GE(c_argument_count, kReceiver);
int cursor = 0; int cursor = 0;
base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ + base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ +
kExtraInputsCount); 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(); inputs[cursor++] = n.receiver();
// TODO(turbofan): Consider refactoring CFunctionInfo to distinguish // TODO(turbofan): Consider refactoring CFunctionInfo to distinguish
...@@ -986,32 +978,29 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler { ...@@ -986,32 +978,29 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
DCHECK_EQ(cursor, c_argument_count + arity_ + kExtraInputsCount); DCHECK_EQ(cursor, c_argument_count + arity_ + kExtraInputsCount);
return FastApiCall(call_descriptor, inputs.begin(), inputs.size(), return FastApiCall(call_descriptor, inputs.begin(), inputs.size());
c_overloads_index);
} }
private: private:
static constexpr int kTarget = 1; static constexpr int kSlowTarget = 1;
static constexpr int kEffectAndControl = 2; static constexpr int kEffectAndControl = 2;
static constexpr int kContextAndFrameState = 2; static constexpr int kContextAndFrameState = 2;
static constexpr int kCallCodeDataAndArgc = 3; static constexpr int kCallCodeDataAndArgc = 3;
static constexpr int kHolder = 1, kReceiver = 1; static constexpr int kHolder = 1, kReceiver = 1;
static constexpr int kExtraInputsCount = static constexpr int kExtraInputsCount =
kTarget * 2 + kEffectAndControl + kContextAndFrameState + kSlowTarget + kEffectAndControl + kContextAndFrameState +
kCallCodeDataAndArgc + kHolder + kReceiver; kCallCodeDataAndArgc + kHolder + kReceiver;
static constexpr int kInlineSize = 12; static constexpr int kInlineSize = 12;
TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs, TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs,
size_t inputs_size, size_t c_overloads_index) { size_t inputs_size) {
return AddNode<Object>( return AddNode<Object>(
graph()->NewNode(simplified()->FastApiCall( graph()->NewNode(simplified()->FastApiCall(c_candidate_functions_,
c_candidate_functions_[c_overloads_index].second, feedback(), descriptor),
feedback(), descriptor),
static_cast<int>(inputs_size), inputs)); static_cast<int>(inputs_size), inputs));
} }
const ZoneVector<std::pair<Address, const CFunctionInfo*>> const FastApiCallFunctionVector c_candidate_functions_;
c_candidate_functions_;
const FunctionTemplateInfoRef function_template_info_; const FunctionTemplateInfoRef function_template_info_;
Node* const receiver_; Node* const receiver_;
Node* const holder_; Node* const holder_;
...@@ -3580,10 +3569,10 @@ bool Has64BitIntegerParamsInSignature(const CFunctionInfo* c_signature) { ...@@ -3580,10 +3569,10 @@ bool Has64BitIntegerParamsInSignature(const CFunctionInfo* c_signature) {
// Returns an array with the indexes of the remaining entries in S, which // Returns an array with the indexes of the remaining entries in S, which
// represents the set of "optimizable" function overloads. // represents the set of "optimizable" function overloads.
ZoneVector<std::pair<Address, const CFunctionInfo*>> CanOptimizeFastCall( FastApiCallFunctionVector CanOptimizeFastCall(
Zone* zone, const FunctionTemplateInfoRef& function_template_info, Zone* zone, const FunctionTemplateInfoRef& function_template_info,
size_t argc) { size_t argc) {
ZoneVector<std::pair<Address, const CFunctionInfo*>> result(zone); FastApiCallFunctionVector result(zone);
if (!FLAG_turbo_fast_api_calls) return result; if (!FLAG_turbo_fast_api_calls) return result;
static constexpr int kReceiver = 1; static constexpr int kReceiver = 1;
...@@ -3798,8 +3787,12 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -3798,8 +3787,12 @@ Reduction JSCallReducer::ReduceCallApiFunction(
return NoChange(); 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); CanOptimizeFastCall(graph()->zone(), function_template_info, argc);
DCHECK_LE(c_candidate_functions.size(), 2);
if (!c_candidate_functions.empty()) { if (!c_candidate_functions.empty()) {
FastApiCallReducerAssembler a(this, node, function_template_info, FastApiCallReducerAssembler a(this, node, function_template_info,
c_candidate_functions, receiver, holder, c_candidate_functions, receiver, holder,
...@@ -3810,6 +3803,8 @@ Reduction JSCallReducer::ReduceCallApiFunction( ...@@ -3810,6 +3803,8 @@ Reduction JSCallReducer::ReduceCallApiFunction(
return Replace(fast_call_subgraph); return Replace(fast_call_subgraph);
} }
// Slow call
CallHandlerInfoRef call_handler_info = *function_template_info.call_code(); CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
CallInterfaceDescriptor cid = call_api_callback.descriptor(); CallInterfaceDescriptor cid = call_api_callback.descriptor();
......
...@@ -418,13 +418,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final ...@@ -418,13 +418,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final
return allocatable_registers_ != 0; 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: private:
friend class Linkage; friend class Linkage;
...@@ -443,7 +436,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final ...@@ -443,7 +436,6 @@ class V8_EXPORT_PRIVATE CallDescriptor final
const Flags flags_; const Flags flags_;
const StackArgumentOrder stack_order_; const StackArgumentOrder stack_order_;
const char* const debug_name_; const char* const debug_name_;
const CFunctionInfo* c_function_info_ = nullptr;
}; };
DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags) DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags)
......
...@@ -1815,8 +1815,11 @@ class RepresentationSelector { ...@@ -1815,8 +1815,11 @@ class RepresentationSelector {
CHECK_EQ(type.GetType(), CTypeInfo::Type::kVoid); CHECK_EQ(type.GetType(), CTypeInfo::Type::kVoid);
return UseInfo::AnyTagged(); return UseInfo::AnyTagged();
} }
case CTypeInfo::SequenceType::kIsTypedArray: {
return UseInfo::AnyTagged();
}
default: { default: {
UNREACHABLE(); // TODO(mslekova): Implement typed arrays. UNREACHABLE(); // TODO(mslekova): Implement array buffers.
} }
} }
} }
...@@ -1827,7 +1830,12 @@ class RepresentationSelector { ...@@ -1827,7 +1830,12 @@ class RepresentationSelector {
void VisitFastApiCall(Node* node, SimplifiedLowering* lowering) { void VisitFastApiCall(Node* node, SimplifiedLowering* lowering) {
FastApiCallParameters const& op_params = FastApiCallParameters const& op_params =
FastApiCallParametersOf(node->op()); 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(); const int c_arg_count = c_signature->ArgumentCount();
CallDescriptor* call_descriptor = op_params.descriptor(); CallDescriptor* call_descriptor = op_params.descriptor();
int js_arg_count = static_cast<int>(call_descriptor->ParameterCount()); int js_arg_count = static_cast<int>(call_descriptor->ParameterCount());
...@@ -1837,28 +1845,21 @@ class RepresentationSelector { ...@@ -1837,28 +1845,21 @@ class RepresentationSelector {
base::SmallVector<UseInfo, kInitialArgumentsCount> arg_use_info( base::SmallVector<UseInfo, kInitialArgumentsCount> arg_use_info(
c_arg_count); c_arg_count);
// The target of the fast call.
ProcessInput<T>(node, 0, UseInfo::Word());
// Propagate representation information from TypeInfo. // Propagate representation information from TypeInfo.
for (int i = 0; i < c_arg_count; i++) { for (int i = 0; i < c_arg_count; i++) {
arg_use_info[i] = UseInfoForFastApiCallArgument( arg_use_info[i] = UseInfoForFastApiCallArgument(
c_signature->ArgumentInfo(i), op_params.feedback()); c_signature->ArgumentInfo(i), op_params.feedback());
ProcessInput<T>(node, i + FastApiCallNode::kFastTargetInputCount, ProcessInput<T>(node, i, arg_use_info[i]);
arg_use_info[i]);
} }
// The call code for the slow call. // The call code for the slow call.
ProcessInput<T>(node, c_arg_count + FastApiCallNode::kFastTargetInputCount, ProcessInput<T>(node, c_arg_count, UseInfo::AnyTagged());
UseInfo::AnyTagged());
for (int i = 1; i <= js_arg_count; i++) { for (int i = 1; i <= js_arg_count; i++) {
ProcessInput<T>(node, ProcessInput<T>(node, c_arg_count + i,
c_arg_count + FastApiCallNode::kFastTargetInputCount + i,
TruncatingUseInfoFromRepresentation( TruncatingUseInfoFromRepresentation(
call_descriptor->GetInputType(i).representation())); call_descriptor->GetInputType(i).representation()));
} }
for (int i = c_arg_count + FastApiCallNode::kFastTargetInputCount + for (int i = c_arg_count + js_arg_count; i < value_input_count; ++i) {
js_arg_count;
i < value_input_count; ++i) {
ProcessInput<T>(node, i, UseInfo::AnyTagged()); ProcessInput<T>(node, i, UseInfo::AnyTagged());
} }
ProcessRemainingInputs<T>(node, value_input_count); ProcessRemainingInputs<T>(node, value_input_count);
......
...@@ -1752,17 +1752,26 @@ FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) { ...@@ -1752,17 +1752,26 @@ FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) {
} }
std::ostream& operator<<(std::ostream& os, FastApiCallParameters const& p) { 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) { 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()); p.descriptor());
} }
bool operator==(FastApiCallParameters const& lhs, bool operator==(FastApiCallParameters const& lhs,
FastApiCallParameters const& rhs) { FastApiCallParameters const& rhs) {
return lhs.signature() == rhs.signature() && return lhs.c_functions() == rhs.c_functions() &&
lhs.feedback() == rhs.feedback() && lhs.feedback() == rhs.feedback() &&
lhs.descriptor() == rhs.descriptor(); lhs.descriptor() == rhs.descriptor();
} }
...@@ -1954,27 +1963,39 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreNonNumberElement( ...@@ -1954,27 +1963,39 @@ const Operator* SimplifiedOperatorBuilder::TransitionAndStoreNonNumberElement(
} }
const Operator* SimplifiedOperatorBuilder::FastApiCall( const Operator* SimplifiedOperatorBuilder::FastApiCall(
const CFunctionInfo* signature, FeedbackSource const& feedback, const FastApiCallFunctionVector& c_functions,
CallDescriptor* descriptor) { 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 = int value_input_count =
(signature->ArgumentCount() + argument_count +
FastApiCallNode::kFastTargetInputCount) + // fast call
static_cast<int>(descriptor->ParameterCount()) + // slow call static_cast<int>(descriptor->ParameterCount()) + // slow call
FastApiCallNode::kEffectAndControlInputCount; FastApiCallNode::kEffectAndControlInputCount;
return zone()->New<Operator1<FastApiCallParameters>>( return zone()->New<Operator1<FastApiCallParameters>>(
IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall", IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall",
value_input_count, 1, 1, 1, 1, 0, value_input_count, 1, 1, 1, 1, 0,
FastApiCallParameters(signature, feedback, descriptor)); FastApiCallParameters(c_functions, feedback, descriptor));
} }
int FastApiCallNode::FastCallExtraInputCount() const { int FastApiCallNode::FastCallExtraInputCount() const {
return kFastTargetInputCount + kEffectAndControlInputCount + const CFunctionInfo* signature = Parameters().c_functions()[0].signature;
(Parameters().signature()->HasOptions() ? 1 : 0); CHECK_NOT_NULL(signature);
return kEffectAndControlInputCount + (signature->HasOptions() ? 1 : 0);
} }
int FastApiCallNode::FastCallArgumentCount() const { int FastApiCallNode::FastCallArgumentCount() const {
FastApiCallParameters p = FastApiCallParametersOf(node()->op()); FastApiCallParameters p = FastApiCallParametersOf(node()->op());
const CFunctionInfo* signature = p.signature(); const CFunctionInfo* signature = p.c_functions()[0].signature;
CHECK_NOT_NULL(signature); CHECK_NOT_NULL(signature);
return signature->ArgumentCount(); return signature->ArgumentCount();
} }
......
...@@ -696,19 +696,33 @@ std::ostream& operator<<(std::ostream&, const NewArgumentsElementsParameters&); ...@@ -696,19 +696,33 @@ std::ostream& operator<<(std::ostream&, const NewArgumentsElementsParameters&);
const NewArgumentsElementsParameters& NewArgumentsElementsParametersOf( const NewArgumentsElementsParameters& NewArgumentsElementsParametersOf(
const Operator*) V8_WARN_UNUSED_RESULT; 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 { class FastApiCallParameters {
public: public:
explicit FastApiCallParameters(const CFunctionInfo* signature, explicit FastApiCallParameters(const FastApiCallFunctionVector& c_functions,
FeedbackSource const& feedback, FeedbackSource const& feedback,
CallDescriptor* descriptor) 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_; } FeedbackSource const& feedback() const { return feedback_; }
CallDescriptor* descriptor() const { return descriptor_; } CallDescriptor* descriptor() const { return descriptor_; }
private: private:
const CFunctionInfo* signature_; // A single FastApiCall node can represent multiple overloaded functions.
const FastApiCallFunctionVector c_functions_;
const FeedbackSource feedback_; const FeedbackSource feedback_;
CallDescriptor* descriptor_; CallDescriptor* descriptor_;
}; };
...@@ -1094,9 +1108,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -1094,9 +1108,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* DateNow(); const Operator* DateNow();
// Represents the inputs necessary to construct a fast and a slow API call. // Represents the inputs necessary to construct a fast and a slow API call.
const Operator* FastApiCall(const CFunctionInfo* signature, const Operator* FastApiCall(
FeedbackSource const& feedback, const FastApiCallFunctionVector& c_candidate_functions,
CallDescriptor* descriptor); FeedbackSource const& feedback, CallDescriptor* descriptor);
private: private:
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
...@@ -1156,19 +1170,15 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase { ...@@ -1156,19 +1170,15 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
return FastApiCallParametersOf(node()->op()); return FastApiCallParametersOf(node()->op());
} }
#define INPUTS(V) \ #define INPUTS(V) V(Receiver, receiver, 0, Object)
V(Target, target, 0, Object) \
V(Receiver, receiver, 1, Object)
INPUTS(DEFINE_INPUT_ACCESSORS) INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS #undef INPUTS
// Besides actual arguments, FastApiCall nodes also take: // Besides actual arguments, FastApiCall nodes also take:
static constexpr int kFastTargetInputCount = 1;
static constexpr int kSlowTargetInputCount = 1; static constexpr int kSlowTargetInputCount = 1;
static constexpr int kFastReceiverInputCount = 1; static constexpr int kFastReceiverInputCount = 1;
static constexpr int kSlowReceiverInputCount = 1; static constexpr int kSlowReceiverInputCount = 1;
static constexpr int kExtraInputCount = static constexpr int kExtraInputCount = kFastReceiverInputCount;
kFastTargetInputCount + kFastReceiverInputCount;
static constexpr int kArityInputCount = 1; static constexpr int kArityInputCount = 1;
static constexpr int kNewTargetInputCount = 1; static constexpr int kNewTargetInputCount = 1;
...@@ -1185,8 +1195,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase { ...@@ -1185,8 +1195,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
// This is the arity fed into FastApiCallArguments. // This is the arity fed into FastApiCallArguments.
static constexpr int ArityForArgc(int c_arg_count, int js_arg_count) { static constexpr int ArityForArgc(int c_arg_count, int js_arg_count) {
return c_arg_count + kFastTargetInputCount + js_arg_count + return c_arg_count + js_arg_count + kEffectAndControlInputCount;
kEffectAndControlInputCount;
} }
int FastCallArgumentCount() const; int FastCallArgumentCount() const;
...@@ -1204,9 +1213,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase { ...@@ -1204,9 +1213,7 @@ class FastApiCallNode final : public SimplifiedNodeWrapperBase {
NodeProperties::GetValueInput(node(), FastCallArgumentIndex(i))); NodeProperties::GetValueInput(node(), FastCallArgumentIndex(i)));
} }
int FirstSlowCallArgumentIndex() const { int FirstSlowCallArgumentIndex() const { return FastCallArgumentCount(); }
return FastCallArgumentCount() + FastApiCallNode::kFastTargetInputCount;
}
int SlowCallArgumentIndex(int i) const { int SlowCallArgumentIndex(int i) const {
return FirstSlowCallArgumentIndex() + i; return FirstSlowCallArgumentIndex() + i;
} }
......
...@@ -1627,9 +1627,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1627,9 +1627,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::BigInt()); CheckTypeIs(node, Type::BigInt());
break; break;
case IrOpcode::kFastApiCall: case IrOpcode::kFastApiCall:
CHECK_GE(value_count, 2); CHECK_GE(value_count, 1);
CheckValueInputIs(node, 0, Type::ExternalPointer()); // callee CheckValueInputIs(node, 0, Type::Any()); // receiver
CheckValueInputIs(node, 1, Type::Any()); // receiver
break; break;
#if V8_ENABLE_WEBASSEMBLY #if V8_ENABLE_WEBASSEMBLY
case IrOpcode::kJSWasmCall: case IrOpcode::kJSWasmCall:
......
...@@ -148,6 +148,12 @@ class FastCApiObject { ...@@ -148,6 +148,12 @@ class FastCApiObject {
isolate->ThrowError("This method expects at least 2 arguments."); isolate->ThrowError("This method expects at least 2 arguments.");
return; 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()) { if (!args[1]->IsArray()) {
isolate->ThrowError("This method expects an array as a second argument."); isolate->ThrowError("This method expects an array as a second argument.");
return; return;
...@@ -176,6 +182,36 @@ class FastCApiObject { ...@@ -176,6 +182,36 @@ class FastCApiObject {
args.GetReturnValue().Set(Number::New(isolate, sum)); 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, static int Add32BitIntFastCallback(v8::Local<v8::Object> receiver,
bool should_fallback, int32_t arg_i32, bool should_fallback, int32_t arg_i32,
uint32_t arg_u32, uint32_t arg_u32,
...@@ -436,6 +472,39 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) { ...@@ -436,6 +472,39 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
signature, 1, ConstructorBehavior::kThrow, signature, 1, ConstructorBehavior::kThrow,
SideEffectType::kHasSideEffect, &add_all_seq_c_func)); 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 add_all_32bit_int_6args_c_func =
CFunction::Make(FastCApiObject::AddAll32BitIntFastCallback_6Args); CFunction::Make(FastCApiObject::AddAll32BitIntFastCallback_6Args);
CFunction add_all_32bit_int_5args_c_func = CFunction add_all_32bit_int_5args_c_func =
......
...@@ -126,8 +126,8 @@ assertEquals(1, fast_c_api.fast_call_count()); ...@@ -126,8 +126,8 @@ assertEquals(1, fast_c_api.fast_call_count());
assertEquals(0, fast_c_api.slow_call_count()); assertEquals(0, fast_c_api.slow_call_count());
// ----------- Test various signature mismatches ----------- // ----------- Test various signature mismatches -----------
function add_32bit_int_mismatch(arg0, arg1, arg2, arg3) { function add_32bit_int_mismatch(arg0, arg1, arg2) {
return fast_c_api.add_32bit_int(arg0, arg1, arg2, arg3); return fast_c_api.add_32bit_int(arg0, arg1, arg2);
} }
%PrepareFunctionForOptimization(add_32bit_int_mismatch); %PrepareFunctionForOptimization(add_32bit_int_mismatch);
...@@ -179,51 +179,62 @@ assertOptimized(add_32bit_int_mismatch); ...@@ -179,51 +179,62 @@ assertOptimized(add_32bit_int_mismatch);
assertEquals(1, fast_c_api.fast_call_count()); assertEquals(1, fast_c_api.fast_call_count());
assertEquals(0, fast_c_api.slow_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_arg1 = -42;
const add_all_32bit_int_arg2 = 45; const add_all_32bit_int_arg2 = 45;
const add_all_32bit_int_arg3 = -12345678; const add_all_32bit_int_arg3 = -12345678;
const add_all_32bit_int_arg4 = 0x1fffffff; const add_all_32bit_int_arg4 = 0x1fffffff;
const add_all_32bit_int_arg5 = 1e6; const add_all_32bit_int_arg5 = 1e6;
const add_all_32bit_int_arg6 = 1e8; 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 + const add_all_32bit_int_result_4args = add_all_32bit_int_arg1 +
add_all_32bit_int_arg4; 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_5args = add_all_32bit_int_result_4args +
const add_all_32bit_int_result_6args = add_all_32bit_int_result_5args + add_all_32bit_int_arg6; add_all_32bit_int_arg5;
const add_all_32bit_int_result_6args = add_all_32bit_int_result_5args +
function overloaded_add_all(should_fallback = false) { add_all_32bit_int_arg6;
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); (function () {
let result_5args = fast_c_api.overloaded_add_all_32bit_int(should_fallback, function overloaded_add_all(should_fallback = false) {
add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3, add_all_32bit_int_arg4, let result_under = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
add_all_32bit_int_arg5); add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
let result_6args = fast_c_api.overloaded_add_all_32bit_int(should_fallback, 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_arg5, add_all_32bit_int_arg6); add_all_32bit_int_arg1, add_all_32bit_int_arg2, add_all_32bit_int_arg3,
let result_over = fast_c_api.overloaded_add_all_32bit_int(should_fallback, 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, let result_6args = fast_c_api.overloaded_add_all_32bit_int(should_fallback,
add_all_32bit_int_arg5, add_all_32bit_int_arg6, 42); 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);
return [result_under, result_5args, result_6args, result_over]; 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,
%PrepareFunctionForOptimization(overloaded_add_all); 42);
let result = overloaded_add_all(); let result_5args_with_undefined = fast_c_api.overloaded_add_all_32bit_int(
assertEquals(add_all_32bit_int_result_4args, result[0]); should_fallback, add_all_32bit_int_arg1, add_all_32bit_int_arg2,
assertEquals(add_all_32bit_int_result_5args, result[1]); add_all_32bit_int_arg3, add_all_32bit_int_arg4, undefined);
assertEquals(add_all_32bit_int_result_6args, result[2]); return [result_under, result_5args, result_6args, result_over,
assertEquals(add_all_32bit_int_result_6args, result[3]); 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(); fast_c_api.reset_counts();
%OptimizeFunctionOnNextCall(overloaded_add_all); %OptimizeFunctionOnNextCall(overloaded_add_all);
result = overloaded_add_all(); result = overloaded_add_all();
assertOptimized(overloaded_add_all); assertOptimized(overloaded_add_all);
// Only the call with less arguments goes falls back to the slow path. // 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(1, fast_c_api.slow_call_count());
assertEquals(add_all_32bit_int_result_4args, result[0]); assertEquals(add_all_32bit_int_result_4args, result[0]);
assertEquals(add_all_32bit_int_result_5args, result[1]); 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[2]);
assertEquals(add_all_32bit_int_result_6args, result[3]); 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) { ...@@ -63,8 +63,7 @@ if (fast_c_api.supports_fp_params) {
} }
function add_all_sequence_mismatch(arg) { function add_all_sequence_mismatch(arg) {
return fast_c_api.add_all_sequence(false /*should_fallback*/, return fast_c_api.add_all_sequence(false /*should_fallback*/, arg);
arg);
} }
%PrepareFunctionForOptimization(add_all_sequence_mismatch); %PrepareFunctionForOptimization(add_all_sequence_mismatch);
...@@ -95,3 +94,106 @@ assertThrows(() => add_all_sequence_mismatch(Symbol())); ...@@ -95,3 +94,106 @@ assertThrows(() => add_all_sequence_mismatch(Symbol()));
assertOptimized(add_all_sequence_mismatch); assertOptimized(add_all_sequence_mismatch);
assertEquals(0, fast_c_api.fast_call_count()); assertEquals(0, fast_c_api.fast_call_count());
assertEquals(1, fast_c_api.slow_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