[fastcall] Store multiple CFunction overloads in FunctionTemplateInfo

In order to support Fast API calls with overloads, store a FixedArray
of c-function addresses and a FixedArray of the corresponding
FunctionInfo*. For now keep using only the first function in the array.

Bug: v8:11739
Change-Id: If23381aa9d04c5cd830043951da9c53836a36328
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2876592
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#74643}
This commit is contained in:
Paolo Severini 2021-05-14 16:14:14 -07:00 committed by V8 LUCI CQ
parent 26ee13b4f0
commit ad4eab00e7
14 changed files with 172 additions and 69 deletions

View File

@ -6506,7 +6506,7 @@ class V8_EXPORT FunctionTemplate : public Template {
Local<Signature> signature = Local<Signature>(), int length = 0,
ConstructorBehavior behavior = ConstructorBehavior::kAllow,
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
const std::vector<const CFunction*>& c_function_overloads = {});
const MemorySpan<const CFunction>& c_function_overloads = {});
/**
* Creates a function template backed/cached by a private property.
@ -6539,7 +6539,7 @@ class V8_EXPORT FunctionTemplate : public Template {
void SetCallHandler(
FunctionCallback callback, Local<Value> data = Local<Value>(),
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
const std::vector<const CFunction*>& c_function_overloads = {});
const MemorySpan<const CFunction>& c_function_overloads = {});
/** Set the predefined length property for the FunctionTemplate. */
void SetLength(int length);

View File

@ -1218,7 +1218,7 @@ static Local<FunctionTemplate> FunctionTemplateNew(
bool do_not_cache,
v8::Local<Private> cached_property_name = v8::Local<Private>(),
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
const std::vector<const CFunction*>& c_function_overloads = {}) {
const MemorySpan<const CFunction>& c_function_overloads = {}) {
i::Handle<i::Struct> struct_obj = isolate->factory()->NewStruct(
i::FUNCTION_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);
i::Handle<i::FunctionTemplateInfo> obj =
@ -1260,20 +1260,15 @@ Local<FunctionTemplate> FunctionTemplate::New(
return FunctionTemplateNew(
i_isolate, callback, data, signature, length, behavior, false,
Local<Private>(), side_effect_type,
c_function ? std::vector<const CFunction*>{c_function}
: std::vector<const CFunction*>{});
c_function ? MemorySpan<const CFunction>{c_function, 1}
: MemorySpan<const CFunction>{});
}
Local<FunctionTemplate> FunctionTemplate::NewWithCFunctionOverloads(
Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
SideEffectType side_effect_type,
const std::vector<const CFunction*>& c_function_overloads) {
// Multiple overloads not supported yet.
Utils::ApiCheck(c_function_overloads.size() == 1,
"v8::FunctionTemplate::NewWithCFunctionOverloads",
"Function overloads not supported yet.");
const MemorySpan<const CFunction>& c_function_overloads) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
LOG_API(i_isolate, FunctionTemplate, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
@ -1313,7 +1308,7 @@ Local<AccessorSignature> AccessorSignature::New(
void FunctionTemplate::SetCallHandler(
FunctionCallback callback, v8::Local<Value> data,
SideEffectType side_effect_type,
const std::vector<const CFunction*>& c_function_overloads) {
const MemorySpan<const CFunction>& c_function_overloads) {
auto info = Utils::OpenHandle(this);
EnsureNotPublished(info, "v8::FunctionTemplate::SetCallHandler");
i::Isolate* isolate = info->GetIsolate();
@ -1327,22 +1322,28 @@ void FunctionTemplate::SetCallHandler(
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
}
obj->set_data(*Utils::OpenHandle(*data));
// Blink passes CFunction's constructed with the default constructor
// for non-fast calls, so we should check the address too.
if (!c_function_overloads.empty()) {
// Multiple overloads not supported yet.
Utils::ApiCheck(c_function_overloads.size() == 1,
"v8::FunctionTemplate::SetCallHandler",
"Function overloads not supported yet.");
const CFunction* c_function = c_function_overloads[0];
if (c_function != nullptr && c_function->GetAddress()) {
i::FunctionTemplateInfo::SetCFunction(
isolate, info,
i::handle(*FromCData(isolate, c_function->GetAddress()), isolate));
i::FunctionTemplateInfo::SetCSignature(
isolate, info,
i::handle(*FromCData(isolate, c_function->GetTypeInfo()), isolate));
if (c_function_overloads.size() > 0) {
// Stores the data for a sequence of CFunction overloads into a single
// FixedArray, as [address_0, signature_0, ... address_n-1, signature_n-1].
i::Handle<i::FixedArray> function_overloads =
isolate->factory()->NewFixedArray(static_cast<int>(
c_function_overloads.size() *
i::FunctionTemplateInfo::kFunctionOverloadEntrySize));
int function_count = static_cast<int>(c_function_overloads.size());
for (int i = 0; i < function_count; i++) {
const CFunction& c_function = c_function_overloads.data()[i];
i::Handle<i::Object> address =
FromCData(isolate, c_function.GetAddress());
function_overloads->set(
i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i, *address);
i::Handle<i::Object> signature =
FromCData(isolate, c_function.GetTypeInfo());
function_overloads->set(
i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i + 1,
*signature);
}
i::FunctionTemplateInfo::SetCFunctionOverloads(isolate, info,
function_overloads);
}
info->set_call_code(*obj, kReleaseStore);
}

View File

@ -234,8 +234,10 @@ class FunctionTemplateInfoData : public HeapObjectData {
void SerializeCallCode(JSHeapBroker* broker);
ObjectData* call_code() const { return call_code_; }
Address c_function() const { return c_function_; }
const CFunctionInfo* c_signature() const { return c_signature_; }
ZoneVector<Address> c_functions() const { return c_functions_; }
ZoneVector<const CFunctionInfo*> c_signatures() const {
return c_signatures_;
}
KnownReceiversMap& known_receivers() { return known_receivers_; }
private:
@ -244,8 +246,8 @@ class FunctionTemplateInfoData : public HeapObjectData {
bool has_call_code_ = false;
ObjectData* call_code_ = nullptr;
const Address c_function_;
const CFunctionInfo* const c_signature_;
ZoneVector<Address> c_functions_;
ZoneVector<const CFunctionInfo*> c_signatures_;
KnownReceiversMap known_receivers_;
};
@ -265,15 +267,50 @@ class CallHandlerInfoData : public HeapObjectData {
ObjectData* data_ = nullptr;
};
namespace {
ZoneVector<Address> GetCFunctions(FixedArray function_overloads, Zone* zone) {
const int len = function_overloads.length() /
FunctionTemplateInfo::kFunctionOverloadEntrySize;
ZoneVector<Address> c_functions = ZoneVector<Address>(len, zone);
for (int i = 0; i < len; i++) {
c_functions[i] = v8::ToCData<Address>(function_overloads.get(
FunctionTemplateInfo::kFunctionOverloadEntrySize * i));
}
return c_functions;
}
ZoneVector<const CFunctionInfo*> GetCSignatures(FixedArray function_overloads,
Zone* zone) {
const int len = function_overloads.length() /
FunctionTemplateInfo::kFunctionOverloadEntrySize;
ZoneVector<const CFunctionInfo*> c_signatures =
ZoneVector<const CFunctionInfo*>(len, zone);
for (int i = 0; i < len; i++) {
c_signatures[i] = v8::ToCData<const CFunctionInfo*>(function_overloads.get(
FunctionTemplateInfo::kFunctionOverloadEntrySize * i + 1));
}
return c_signatures;
}
} // namespace
FunctionTemplateInfoData::FunctionTemplateInfoData(
JSHeapBroker* broker, ObjectData** storage,
Handle<FunctionTemplateInfo> object)
: HeapObjectData(broker, storage, object),
c_function_(v8::ToCData<Address>(object->GetCFunction())),
c_signature_(v8::ToCData<CFunctionInfo*>(object->GetCSignature())),
c_functions_(broker->zone()),
c_signatures_(broker->zone()),
known_receivers_(broker->zone()) {
DCHECK(!broker->is_concurrent_inlining());
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
FixedArray function_overloads_array =
FixedArray::cast(function_template_info->GetCFunctionOverloads());
c_functions_ = GetCFunctions(function_overloads_array, broker->zone());
c_signatures_ = GetCSignatures(function_overloads_array, broker->zone());
is_signature_undefined_ =
function_template_info->signature().IsUndefined(broker->isolate());
accept_any_receiver_ = function_template_info->accept_any_receiver();
@ -3693,18 +3730,20 @@ Address CallHandlerInfoRef::callback() const {
return HeapObjectRef::data()->AsCallHandlerInfo()->callback();
}
Address FunctionTemplateInfoRef::c_function() const {
ZoneVector<Address> FunctionTemplateInfoRef::c_functions() const {
if (data_->should_access_heap()) {
return v8::ToCData<Address>(object()->GetCFunction());
return GetCFunctions(FixedArray::cast(object()->GetCFunctionOverloads()),
broker()->zone());
}
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_function();
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_functions();
}
const CFunctionInfo* FunctionTemplateInfoRef::c_signature() const {
ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const {
if (data_->should_access_heap()) {
return v8::ToCData<CFunctionInfo*>(object()->GetCSignature());
return GetCSignatures(FixedArray::cast(object()->GetCFunctionOverloads()),
broker()->zone());
}
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signature();
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signatures();
}
bool StringRef::IsSeqString() const {

View File

@ -738,8 +738,8 @@ class FunctionTemplateInfoRef : public HeapObjectRef {
void SerializeCallCode();
base::Optional<CallHandlerInfoRef> call_code() const;
Address c_function() const;
const CFunctionInfo* c_signature() const;
ZoneVector<Address> c_functions() const;
ZoneVector<const CFunctionInfo*> c_signatures() const;
HolderLookupResult LookupHolderOfExpectedType(
MapRef receiver_map,

View File

@ -890,8 +890,8 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
Node* holder, const SharedFunctionInfoRef shared, Node* target,
const int arity, Node* effect)
: JSCallReducerAssembler(reducer, node),
c_function_(function_template_info.c_function()),
c_signature_(function_template_info.c_signature()),
c_functions_(function_template_info.c_functions()),
c_signatures_(function_template_info.c_signatures()),
function_template_info_(function_template_info),
receiver_(receiver),
holder_(holder),
@ -899,8 +899,8 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
target_(target),
arity_(arity) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
DCHECK_NE(c_function_, kNullAddress);
CHECK_NOT_NULL(c_signature_);
CHECK_GT(c_functions_.size(), 0);
CHECK_GT(c_signatures_.size(), 0);
InitializeEffectControl(effect, NodeProperties::GetControlInput(node));
}
@ -909,13 +909,16 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
// 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_signature_->ArgumentCount());
static_cast<int>(c_signatures_[0]->ArgumentCount());
CHECK_GE(c_argument_count, kReceiver);
int cursor = 0;
base::SmallVector<Node*, kInlineSize> inputs(c_argument_count + arity_ +
kExtraInputsCount);
inputs[cursor++] = ExternalConstant(ExternalReference::Create(c_function_));
// Multiple function overloads not supported yet, always call the first
// overload.
inputs[cursor++] =
ExternalConstant(ExternalReference::Create(c_functions_[0]));
inputs[cursor++] = n.receiver();
@ -987,12 +990,12 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
TNode<Object> FastApiCall(CallDescriptor* descriptor, Node** inputs,
size_t inputs_size) {
return AddNode<Object>(graph()->NewNode(
simplified()->FastApiCall(c_signature_, feedback(), descriptor),
simplified()->FastApiCall(c_signatures_[0], feedback(), descriptor),
static_cast<int>(inputs_size), inputs));
}
const Address c_function_;
const CFunctionInfo* const c_signature_;
const ZoneVector<Address> c_functions_;
const ZoneVector<const CFunctionInfo*> c_signatures_;
const FunctionTemplateInfoRef function_template_info_;
Node* const receiver_;
Node* const holder_;
@ -3539,11 +3542,13 @@ bool Has64BitIntegerParamsInSignature(const CFunctionInfo* c_signature) {
bool CanOptimizeFastCall(
const FunctionTemplateInfoRef& function_template_info) {
const CFunctionInfo* c_signature = function_template_info.c_signature();
if (function_template_info.c_functions().empty()) return false;
bool optimize_to_fast_call =
FLAG_turbo_fast_api_calls &&
function_template_info.c_function() != kNullAddress;
// Multiple function overloads not supported yet, always call the first
// overload.
const CFunctionInfo* c_signature = function_template_info.c_signatures()[0];
bool optimize_to_fast_call = FLAG_turbo_fast_api_calls;
#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
optimize_to_fast_call =
optimize_to_fast_call && !HasFPParamsInSignature(c_signature);

View File

@ -55,6 +55,14 @@ class FastCApiObject {
static_cast<double>(arg_i64) + static_cast<double>(arg_u64) +
static_cast<double>(arg_f32) + arg_f64;
}
static double AddAllFastCallback_5Args(Local<Object> receiver,
bool should_fallback, int32_t arg_i32,
uint32_t arg_u32, int64_t arg_i64,
uint64_t arg_u64, float arg_f32,
FastApiCallbackOptions& options) {
return AddAllFastCallback(receiver, should_fallback, arg_i32, arg_u32,
arg_i64, arg_u64, arg_f32, 0, options);
}
static void AddAllSlowCallback(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
@ -283,12 +291,16 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
SideEffectType::kHasSideEffect, &add_all_c_func));
// To test function overloads.
CFunction add_all_5args_c_func =
CFunction::Make(FastCApiObject::AddAllFastCallback_5Args);
const CFunction c_function_overloads[] = {add_all_c_func,
add_all_5args_c_func};
api_obj_ctor->PrototypeTemplate()->Set(
isolate, "overloaded_add_all",
FunctionTemplate::NewWithCFunctionOverloads(
isolate, FastCApiObject::AddAllSlowCallback, Local<Value>(),
signature, 1, ConstructorBehavior::kThrow,
SideEffectType::kHasSideEffect, {&add_all_c_func}));
SideEffectType::kHasSideEffect, {c_function_overloads, 2}));
CFunction add_32bit_int_c_func =
CFunction::Make(FastCApiObject::Add32BitIntFastCallback);

View File

@ -1746,6 +1746,12 @@ void StackFrameInfo::StackFrameInfoVerify(Isolate* isolate) {
#endif // V8_ENABLE_WEBASSEMBLY
}
void FunctionTemplateRareData::FunctionTemplateRareDataVerify(
Isolate* isolate) {
CHECK(c_function_overloads().IsFixedArray() ||
c_function_overloads().IsUndefined(isolate));
}
#endif // VERIFY_HEAP
#ifdef DEBUG

View File

@ -897,6 +897,18 @@ Handle<SwissNameDictionary> FactoryBase<Impl>::NewSwissNameDictionary(
SwissNameDictionary::CapacityFor(at_least_space_for), allocation);
}
template <typename Impl>
Handle<FunctionTemplateRareData>
FactoryBase<Impl>::NewFunctionTemplateRareData() {
auto function_template_rare_data =
NewStructInternal<FunctionTemplateRareData>(
FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
DisallowGarbageCollection no_gc;
function_template_rare_data.set_c_function_overloads(
*impl()->empty_fixed_array(), SKIP_WRITE_BARRIER);
return handle(function_template_rare_data, isolate());
}
// Instantiate FactoryBase for the two variants we want.
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) FactoryBase<Factory>;
template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)

View File

@ -228,6 +228,8 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase
Handle<SwissNameDictionary> NewSwissNameDictionaryWithCapacity(
int capacity, AllocationType allocation);
Handle<FunctionTemplateRareData> NewFunctionTemplateRareData();
protected:
// Allocate memory for an uninitialized array (e.g., a FixedArray or similar).
HeapObject AllocateRawArray(int size, AllocationType allocation);

View File

@ -2274,9 +2274,10 @@ void ExistingCodeLogger::LogExistingFunction(
CALL_CODE_EVENT_HANDLER(CallbackEvent(fun_name, entry_point))
// Fast API function.
Address c_function = v8::ToCData<Address>(fun_data->GetCFunction());
if (c_function != kNullAddress) {
CALL_CODE_EVENT_HANDLER(CallbackEvent(fun_name, c_function))
int c_functions_count = fun_data->GetCFunctionsCount();
for (int i = 0; i < c_functions_count; i++) {
CALL_CODE_EVENT_HANDLER(
CallbackEvent(fun_name, fun_data->GetCFunction(i)))
}
}
}

View File

@ -1374,12 +1374,8 @@ bool FunctionTemplateInfo::IsLeafTemplateForApiObject(Object object) const {
FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData(
Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info) {
DCHECK(function_template_info->rare_data(kAcquireLoad).IsUndefined(isolate));
Handle<Struct> struct_obj = isolate->factory()->NewStruct(
FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
Handle<FunctionTemplateRareData> rare_data =
i::Handle<FunctionTemplateRareData>::cast(struct_obj);
rare_data->set_c_function(Smi(0));
rare_data->set_c_signature(Smi(0));
isolate->factory()->NewFunctionTemplateRareData();
function_template_info->set_rare_data(*rare_data, kReleaseStore);
return *rare_data;
}
@ -6661,6 +6657,25 @@ base::Optional<Name> FunctionTemplateInfo::TryGetCachedPropertyName(
return Name::cast(maybe_name);
}
int FunctionTemplateInfo::GetCFunctionsCount() const {
i::DisallowHeapAllocation no_gc;
return FixedArray::cast(GetCFunctionOverloads()).length() /
kFunctionOverloadEntrySize;
}
Address FunctionTemplateInfo::GetCFunction(int index) const {
i::DisallowHeapAllocation no_gc;
return v8::ToCData<Address>(FixedArray::cast(GetCFunctionOverloads())
.get(index * kFunctionOverloadEntrySize));
}
const CFunctionInfo* FunctionTemplateInfo::GetCSignature(int index) const {
i::DisallowHeapAllocation no_gc;
return v8::ToCData<CFunctionInfo*>(
FixedArray::cast(GetCFunctionOverloads())
.get(index * kFunctionOverloadEntrySize + 1));
}
Address Smi::LexicographicCompare(Isolate* isolate, Smi x, Smi y) {
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);

View File

@ -76,8 +76,8 @@ RARE_ACCESSORS(instance_template, InstanceTemplate, HeapObject, undefined)
RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, HeapObject,
undefined)
RARE_ACCESSORS(access_check_info, AccessCheckInfo, HeapObject, undefined)
RARE_ACCESSORS(c_function, CFunction, Object, Smi(0))
RARE_ACCESSORS(c_signature, CSignature, Object, Smi(0))
RARE_ACCESSORS(c_function_overloads, CFunctionOverloads, FixedArray,
GetReadOnlyRoots(cage_base).empty_fixed_array())
#undef RARE_ACCESSORS
bool TemplateInfo::should_cache() const {

View File

@ -12,6 +12,9 @@
#include "src/objects/object-macros.h"
namespace v8 {
class CFunctionInfo;
namespace internal {
#include "torque-generated/src/objects/templates-tq.inc"
@ -45,6 +48,7 @@ class FunctionTemplateRareData
: public TorqueGeneratedFunctionTemplateRareData<FunctionTemplateRareData,
Struct> {
public:
DECL_VERIFIER(FunctionTemplateRareData)
TQ_OBJECT_CONSTRUCTORS(FunctionTemplateRareData)
};
@ -93,8 +97,7 @@ class FunctionTemplateInfo
DECL_RARE_ACCESSORS(access_check_info, AccessCheckInfo, HeapObject)
DECL_RARE_ACCESSORS(c_function, CFunction, Object)
DECL_RARE_ACCESSORS(c_signature, CSignature, Object)
DECL_RARE_ACCESSORS(c_function_overloads, CFunctionOverloads, FixedArray)
#undef DECL_RARE_ACCESSORS
// Begin flag bits ---------------------
@ -152,6 +155,14 @@ class FunctionTemplateInfo
// Helper function for cached accessors.
static base::Optional<Name> TryGetCachedPropertyName(Isolate* isolate,
Object getter);
// Fast API overloads.
int GetCFunctionsCount() const;
Address GetCFunction(int index) const;
const CFunctionInfo* GetCSignature(int index) const;
// CFunction data for a set of overloads is stored into a FixedArray, as
// [address_0, signature_0, ... address_n-1, signature_n-1].
static const int kFunctionOverloadEntrySize = 2;
// Bit position in the flag, from least significant bit position.
DEFINE_TORQUE_GENERATED_FUNCTION_TEMPLATE_INFO_FLAGS()

View File

@ -24,8 +24,7 @@ extern class FunctionTemplateRareData extends Struct {
instance_template: ObjectTemplateInfo|Undefined;
instance_call_handler: CallHandlerInfo|Undefined;
access_check_info: AccessCheckInfo|Undefined;
c_function: Foreign|Zero;
c_signature: Foreign|Zero;
c_function_overloads: FixedArray;
}
bitfield struct FunctionTemplateInfoFlags extends uint31 {