[turbofan] Introduce a CallFunctionTemplate builtin.
When calling into API callbacks from TurboFan optimized, we can currently only take a fast-path when TurboFan is able to find some information about the receiver in the graph, or when the API callback specifies that it neither requires an access check (aka "accepts any receiver") nor an interface check (aka "compatible receiver check"). This change introduces a new CallFunctionTemplate builtin that sits in front of the CallApiCallback builtin and does both the access as well as the interface check as necessary (and raises appropriate exceptions). This way TurboFan can still call into the API callback via the fast-path even without ahead knowledge about the receiver, which is significantly faster than the generic call machinery for API callbacks. On the test case from the Angular team[1], the interesting metrics improve from DOM_mono: 0.273 ms DOM_mega: 0.571 ms DOM_call: 0.649 ms to DOM_mono: 0.264 ms DOM_mega: 0.572 ms DOM_call: 0.368 ms so the DOM_call is only about **1.4 times slower** than the DOM_mono and about **1.5 times faster** than the DOM_mega case (compared to **2.4 times slower**). Execution time in the DOM_call was reduced by around **~45%**. Currently this new code path is limited to TurboFan optimized code, but the idea is to eventually migrate the API calls from baseline to also use the new CSA functionality, but there are lot's of subleties to take into account, so starting with small changes to get coverage for the basic building blocks. [1]: https://mhevery.github.io/perf-tests/DOM-megamorphic.html Bug: v8:8820 Change-Id: Ie1029cf182ce05a6e519fd9a9d4fa825db8adb4c Cq-Include-Trybots: luci.chromium.try:linux-blink-rel Reviewed-on: https://chromium-review.googlesource.com/c/1470129 Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#59598}
This commit is contained in:
parent
a08a150cc8
commit
a2d9924c42
@ -99,6 +99,14 @@ void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallFunctionTemplateDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// r1 : function template info
|
||||
// r2 : number of arguments (on the stack, not including receiver)
|
||||
Register registers[] = {r1, r2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallWithSpreadDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// r0 : number of arguments (on the stack, not including receiver)
|
||||
|
@ -99,6 +99,14 @@ void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallFunctionTemplateDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// x1 : function template info
|
||||
// x2 : number of arguments (on the stack, not including receiver)
|
||||
Register registers[] = {x1, x2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallWithSpreadDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// x0 : number of arguments (on the stack, not including receiver)
|
||||
|
@ -9,12 +9,17 @@
|
||||
#include "src/globals.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/objects/api-callbacks.h"
|
||||
#include "src/objects/arguments.h"
|
||||
#include "src/objects/property-cell.h"
|
||||
#include "src/objects/templates.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
using TNode = compiler::TNode<T>;
|
||||
|
||||
void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
|
||||
MacroAssembler* masm) {
|
||||
Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined);
|
||||
@ -373,5 +378,253 @@ TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) {
|
||||
CallOrConstructWithSpread(target, new_target, spread, args_count, context);
|
||||
}
|
||||
|
||||
TNode<JSReceiver> CallOrConstructBuiltinsAssembler::GetCompatibleReceiver(
|
||||
TNode<JSReceiver> receiver, TNode<HeapObject> signature,
|
||||
TNode<Context> context) {
|
||||
// Walk up the hidden prototype chain to find the compatible holder
|
||||
// for the {signature}, starting with the {receiver} itself.
|
||||
//
|
||||
// Be careful, these loops are hand-tuned for (close to) ideal CSA
|
||||
// code generation. Especially the sharing of the {var_template}
|
||||
// below is intentional (even though it reads a bit funny in the
|
||||
// first loop).
|
||||
TVARIABLE(HeapObject, var_holder, receiver);
|
||||
Label holder_loop(this, &var_holder), holder_found(this, &var_holder),
|
||||
holder_next(this, Label::kDeferred);
|
||||
Goto(&holder_loop);
|
||||
BIND(&holder_loop);
|
||||
{
|
||||
// Find the template to compare against the {signature}. We don't
|
||||
// bother checking that the template is a FunctionTemplateInfo here,
|
||||
// but instead do that as part of the template loop below. The only
|
||||
// thing we care about is that the template is actually a HeapObject.
|
||||
TNode<HeapObject> holder = var_holder.value();
|
||||
TVARIABLE(HeapObject, var_template, LoadMap(holder));
|
||||
Label template_map_loop(this, &var_template),
|
||||
template_loop(this, &var_template),
|
||||
template_from_closure(this, &var_template);
|
||||
Goto(&template_map_loop);
|
||||
BIND(&template_map_loop);
|
||||
{
|
||||
// Load the constructor field from the current map (in the
|
||||
// {var_template} variable), and see if that is a HeapObject.
|
||||
// If it's a Smi then it is non-instance prototype on some
|
||||
// initial map, which cannot be the case for API instances.
|
||||
TNode<Object> constructor = LoadObjectField(
|
||||
var_template.value(), Map::kConstructorOrBackPointerOffset);
|
||||
GotoIf(TaggedIsSmi(constructor), &holder_next);
|
||||
|
||||
// Now there are three cases for {constructor} that we care
|
||||
// about here:
|
||||
//
|
||||
// 1. {constructor} is a JSFunction, and we can load the template
|
||||
// from its SharedFunctionInfo::function_data field (which
|
||||
// may not actually be a FunctionTemplateInfo).
|
||||
// 2. {constructor} is a Map, in which case it's not a constructor
|
||||
// but a back-pointer and we follow that.
|
||||
// 3. {constructor} is a FunctionTemplateInfo (or some other
|
||||
// HeapObject), in which case we can directly use that for
|
||||
// the template loop below (non-FunctionTemplateInfo objects
|
||||
// will be ruled out there).
|
||||
//
|
||||
var_template = CAST(constructor);
|
||||
TNode<Int32T> template_type = LoadInstanceType(var_template.value());
|
||||
GotoIf(InstanceTypeEqual(template_type, JS_FUNCTION_TYPE),
|
||||
&template_from_closure);
|
||||
Branch(InstanceTypeEqual(template_type, MAP_TYPE), &template_map_loop,
|
||||
&template_loop);
|
||||
}
|
||||
|
||||
BIND(&template_from_closure);
|
||||
{
|
||||
// The first case from above, where we load the template from the
|
||||
// SharedFunctionInfo of the closure. We only check that the
|
||||
// SharedFunctionInfo::function_data is a HeapObject and blindly
|
||||
// use that as a template, since a non-FunctionTemplateInfo objects
|
||||
// will be ruled out automatically by the template loop below.
|
||||
TNode<SharedFunctionInfo> template_shared =
|
||||
LoadObjectField<SharedFunctionInfo>(
|
||||
var_template.value(), JSFunction::kSharedFunctionInfoOffset);
|
||||
TNode<Object> template_data = LoadObjectField(
|
||||
template_shared, SharedFunctionInfo::kFunctionDataOffset);
|
||||
GotoIf(TaggedIsSmi(template_data), &holder_next);
|
||||
var_template = CAST(template_data);
|
||||
Goto(&template_loop);
|
||||
}
|
||||
|
||||
BIND(&template_loop);
|
||||
{
|
||||
// This loop compares the template to the expected {signature},
|
||||
// following the chain of parent templates until it hits the
|
||||
// end, in which case we continue with the next holder (the
|
||||
// hidden prototype) if there's any.
|
||||
TNode<HeapObject> current = var_template.value();
|
||||
GotoIf(WordEqual(current, signature), &holder_found);
|
||||
|
||||
GotoIfNot(IsFunctionTemplateInfoMap(LoadMap(current)), &holder_next);
|
||||
|
||||
TNode<HeapObject> current_rare = LoadObjectField<HeapObject>(
|
||||
current, FunctionTemplateInfo::kFunctionTemplateRareDataOffset);
|
||||
GotoIf(IsUndefined(current_rare), &holder_next);
|
||||
var_template = LoadObjectField<HeapObject>(
|
||||
current_rare, FunctionTemplateRareData::kParentTemplateOffset);
|
||||
Goto(&template_loop);
|
||||
}
|
||||
|
||||
BIND(&holder_next);
|
||||
{
|
||||
// Continue with the hidden prototype of the {holder} if it
|
||||
// has one, or throw an illegal invocation exception, since
|
||||
// the receiver did not pass the {signature} check.
|
||||
TNode<Map> holder_map = LoadMap(holder);
|
||||
var_holder = LoadMapPrototype(holder_map);
|
||||
GotoIf(IsSetWord32(LoadMapBitField3(holder_map),
|
||||
Map::HasHiddenPrototypeBit::kMask),
|
||||
&holder_loop);
|
||||
ThrowTypeError(context, MessageTemplate::kIllegalInvocation);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&holder_found);
|
||||
return CAST(var_holder.value());
|
||||
}
|
||||
|
||||
// This calls an API callback by passing a {FunctionTemplateInfo},
|
||||
// does appropriate access and compatible receiver checks.
|
||||
void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
|
||||
CallFunctionTemplateMode mode,
|
||||
TNode<FunctionTemplateInfo> function_template_info, TNode<IntPtrT> argc,
|
||||
TNode<Context> context) {
|
||||
CodeStubArguments args(this, argc);
|
||||
Label throw_illegal_invocation(this, Label::kDeferred);
|
||||
|
||||
// For API callbacks we need to call ToObject on the receiver.
|
||||
// And in case the receiver is a JSObject already, we might
|
||||
// need to perform access checks in the current {context},
|
||||
// depending on whether the "needs access check" bit is
|
||||
// set on the receiver _and_ the {function_template_info}
|
||||
// doesn't have the "accepts any receiver" bit set.
|
||||
TVARIABLE(Object, var_receiver, args.GetReceiver());
|
||||
if (mode == CallFunctionTemplateMode::kCheckCompatibleReceiver) {
|
||||
// We are only interested to see that receiver is compatible
|
||||
// for the {function_template_info}, and don't need to bother
|
||||
// doing any access checks. So ensure that the receiver is
|
||||
// actually a JSReceiver.
|
||||
var_receiver = ToObject_Inline(context, var_receiver.value());
|
||||
} else {
|
||||
Label receiver_is_primitive(this, Label::kDeferred),
|
||||
receiver_needs_access_check(this, &var_receiver, Label::kDeferred),
|
||||
receiver_done(this);
|
||||
|
||||
// Check if the receiver needs to be converted, or if it's already
|
||||
// a JSReceiver, see if the "needs access check" bit is set _and_
|
||||
// the {function_template_info} doesn't just accept any receiver.
|
||||
GotoIf(TaggedIsSmi(var_receiver.value()), &receiver_is_primitive);
|
||||
TNode<Map> receiver_map = LoadMap(CAST(var_receiver.value()));
|
||||
GotoIfNot(IsJSReceiverMap(receiver_map), &receiver_is_primitive);
|
||||
GotoIfNot(
|
||||
IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)),
|
||||
&receiver_done);
|
||||
TNode<WordT> function_template_info_flags = LoadAndUntagObjectField(
|
||||
function_template_info, FunctionTemplateInfo::kFlagOffset);
|
||||
Branch(IsSetWord(function_template_info_flags,
|
||||
1 << FunctionTemplateInfo::kAcceptAnyReceiver),
|
||||
&receiver_done, &receiver_needs_access_check);
|
||||
|
||||
BIND(&receiver_is_primitive);
|
||||
{
|
||||
// Convert primitives to wrapper objects as necessary. In case
|
||||
// null or undefined were passed, we need to do the access check
|
||||
// on the global proxy here.
|
||||
var_receiver = ToObject(context, var_receiver.value());
|
||||
args.SetReceiver(var_receiver.value());
|
||||
GotoIfNot(IsSetWord32<Map::IsAccessCheckNeededBit>(
|
||||
LoadMapBitField(LoadMap(CAST(var_receiver.value())))),
|
||||
&receiver_done);
|
||||
TNode<WordT> function_template_info_flags = LoadAndUntagObjectField(
|
||||
function_template_info, FunctionTemplateInfo::kFlagOffset);
|
||||
Branch(IsSetWord(function_template_info_flags,
|
||||
1 << FunctionTemplateInfo::kAcceptAnyReceiver),
|
||||
&receiver_done, &receiver_needs_access_check);
|
||||
}
|
||||
|
||||
BIND(&receiver_needs_access_check);
|
||||
{
|
||||
CallRuntime(Runtime::kAccessCheck, context, var_receiver.value());
|
||||
Goto(&receiver_done);
|
||||
}
|
||||
|
||||
BIND(&receiver_done);
|
||||
}
|
||||
TNode<JSReceiver> receiver = CAST(var_receiver.value());
|
||||
|
||||
// Figure out the API holder for the {receiver} depending on the
|
||||
// {mode} and the signature on the {function_template_info}.
|
||||
TNode<JSReceiver> holder;
|
||||
if (mode == CallFunctionTemplateMode::kCheckAccess) {
|
||||
// We did the access check (including the ToObject) above, so
|
||||
// {receiver} is a JSReceiver at this point, and we don't need
|
||||
// to perform any "compatible receiver check", so {holder} is
|
||||
// actually the {receiver}.
|
||||
holder = receiver;
|
||||
} else {
|
||||
// If the {function_template_info} doesn't specify any signature, we
|
||||
// just use the receiver as the holder for the API callback, otherwise
|
||||
// we need to look for a compatible holder in the receiver's hidden
|
||||
// prototype chain.
|
||||
TNode<HeapObject> signature = LoadObjectField<HeapObject>(
|
||||
function_template_info, FunctionTemplateInfo::kSignatureOffset);
|
||||
holder = Select<JSReceiver>(
|
||||
IsUndefined(signature), // --
|
||||
[&]() { return receiver; },
|
||||
[&]() { return GetCompatibleReceiver(receiver, signature, context); });
|
||||
}
|
||||
|
||||
// Perform the actual API callback invocation via CallApiCallback.
|
||||
TNode<CallHandlerInfo> call_handler_info = LoadObjectField<CallHandlerInfo>(
|
||||
function_template_info, FunctionTemplateInfo::kCallCodeOffset);
|
||||
TNode<Foreign> foreign = LoadObjectField<Foreign>(
|
||||
call_handler_info, CallHandlerInfo::kJsCallbackOffset);
|
||||
TNode<RawPtrT> callback =
|
||||
LoadObjectField<RawPtrT>(foreign, Foreign::kForeignAddressOffset);
|
||||
TNode<Object> call_data =
|
||||
LoadObjectField<Object>(call_handler_info, CallHandlerInfo::kDataOffset);
|
||||
TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc,
|
||||
call_data, holder);
|
||||
}
|
||||
|
||||
TF_BUILTIN(CallFunctionTemplate_CheckAccess, CallOrConstructBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<FunctionTemplateInfo> function_template_info =
|
||||
CAST(Parameter(Descriptor::kFunctionTemplateInfo));
|
||||
TNode<IntPtrT> argc =
|
||||
UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
|
||||
CallFunctionTemplate(CallFunctionTemplateMode::kCheckAccess,
|
||||
function_template_info, argc, context);
|
||||
}
|
||||
|
||||
TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver,
|
||||
CallOrConstructBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<FunctionTemplateInfo> function_template_info =
|
||||
CAST(Parameter(Descriptor::kFunctionTemplateInfo));
|
||||
TNode<IntPtrT> argc =
|
||||
UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
|
||||
CallFunctionTemplate(CallFunctionTemplateMode::kCheckCompatibleReceiver,
|
||||
function_template_info, argc, context);
|
||||
}
|
||||
|
||||
TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver,
|
||||
CallOrConstructBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<FunctionTemplateInfo> function_template_info =
|
||||
CAST(Parameter(Descriptor::kFunctionTemplateInfo));
|
||||
TNode<IntPtrT> argc =
|
||||
UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
|
||||
CallFunctionTemplate(
|
||||
CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver,
|
||||
function_template_info, argc, context);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -28,6 +28,21 @@ class CallOrConstructBuiltinsAssembler : public CodeStubAssembler {
|
||||
void CallOrConstructWithSpread(TNode<Object> target, TNode<Object> new_target,
|
||||
TNode<Object> spread, TNode<Int32T> args_count,
|
||||
TNode<Context> context);
|
||||
|
||||
enum class CallFunctionTemplateMode : uint8_t {
|
||||
kCheckAccess,
|
||||
kCheckCompatibleReceiver,
|
||||
kCheckAccessAndCompatibleReceiver,
|
||||
};
|
||||
|
||||
void CallFunctionTemplate(CallFunctionTemplateMode mode,
|
||||
TNode<FunctionTemplateInfo> function_template_info,
|
||||
TNode<IntPtrT> argc, TNode<Context> context);
|
||||
|
||||
private:
|
||||
TNode<JSReceiver> GetCompatibleReceiver(TNode<JSReceiver> receiver,
|
||||
TNode<HeapObject> signature,
|
||||
TNode<Context> context);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -61,6 +61,12 @@ namespace internal {
|
||||
TFC(CallWithArrayLike, CallWithArrayLike, 1) \
|
||||
ASM(CallForwardVarargs, CallForwardVarargs) \
|
||||
ASM(CallFunctionForwardVarargs, CallForwardVarargs) \
|
||||
/* Call an API callback via a {FunctionTemplateInfo}, doing appropriate */ \
|
||||
/* access and compatible receiver checks. */ \
|
||||
TFC(CallFunctionTemplate_CheckAccess, CallFunctionTemplate, 1) \
|
||||
TFC(CallFunctionTemplate_CheckCompatibleReceiver, CallFunctionTemplate, 1) \
|
||||
TFC(CallFunctionTemplate_CheckAccessAndCompatibleReceiver, \
|
||||
CallFunctionTemplate, 1) \
|
||||
\
|
||||
/* Construct */ \
|
||||
/* ES6 section 9.2.2 [[Construct]] ( argumentsList, newTarget) */ \
|
||||
|
@ -2956,8 +2956,27 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
|
||||
receiver, global_proxy, effect, control);
|
||||
} else {
|
||||
// We cannot do a fast API call in this case.
|
||||
return NoChange();
|
||||
// We don't have enough information to eliminate the access check
|
||||
// and/or the compatible receiver check, so use the generic builtin
|
||||
// that does those checks dynamically. This is still significantly
|
||||
// faster than the generic call sequence.
|
||||
Builtins::Name builtin_name =
|
||||
!function_template_info->accept_any_receiver()
|
||||
? (function_template_info->signature()->IsUndefined(isolate())
|
||||
? Builtins::kCallFunctionTemplate_CheckAccess
|
||||
: Builtins::
|
||||
kCallFunctionTemplate_CheckAccessAndCompatibleReceiver)
|
||||
: Builtins::kCallFunctionTemplate_CheckCompatibleReceiver;
|
||||
Callable callable = Builtins::CallableFor(isolate(), builtin_name);
|
||||
auto call_descriptor = Linkage::GetStubCallDescriptor(
|
||||
graph()->zone(), callable.descriptor(),
|
||||
argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState);
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
jsgraph()->HeapConstant(callable.code()));
|
||||
node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc));
|
||||
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
// TODO(turbofan): Consider introducing a JSCallApiCallback operator for
|
||||
@ -2969,8 +2988,7 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||
auto call_descriptor = Linkage::GetStubCallDescriptor(
|
||||
graph()->zone(), cid,
|
||||
cid.GetStackParameterCount() + argc + 1 /* implicit receiver */,
|
||||
graph()->zone(), cid, argc + 1 /* implicit receiver */,
|
||||
CallDescriptor::kNeedsFrameState);
|
||||
ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
|
||||
ExternalReference function_reference = ExternalReference::Create(
|
||||
|
@ -100,6 +100,14 @@ void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallFunctionTemplateDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// edx : function template info
|
||||
// ecx : number of arguments (on the stack, not including receiver)
|
||||
Register registers[] = {edx, ecx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallWithSpreadDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// eax : number of arguments (on the stack, not including receiver)
|
||||
|
@ -31,6 +31,7 @@ namespace internal {
|
||||
V(BigIntToWasmI64) \
|
||||
V(BinaryOp) \
|
||||
V(CallForwardVarargs) \
|
||||
V(CallFunctionTemplate) \
|
||||
V(CallTrampoline) \
|
||||
V(CallVarargs) \
|
||||
V(CallWithArrayLike) \
|
||||
@ -793,6 +794,14 @@ class CallForwardVarargsDescriptor : public CallInterfaceDescriptor {
|
||||
DECLARE_DESCRIPTOR(CallForwardVarargsDescriptor, CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class CallFunctionTemplateDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kFunctionTemplateInfo, kArgumentsCount)
|
||||
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kFunctionTemplateInfo
|
||||
MachineType::IntPtr()) // kArgumentsCount
|
||||
DECLARE_DESCRIPTOR(CallFunctionTemplateDescriptor, CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class CallWithSpreadDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kTarget, kArgumentsCount, kSpread)
|
||||
|
@ -224,13 +224,6 @@ class FunctionTemplateInfo : public TemplateInfo {
|
||||
static MaybeHandle<Name> TryGetCachedPropertyName(Isolate* isolate,
|
||||
Handle<Object> getter);
|
||||
|
||||
private:
|
||||
static inline FunctionTemplateRareData EnsureFunctionTemplateRareData(
|
||||
Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info);
|
||||
|
||||
static FunctionTemplateRareData AllocateFunctionTemplateRareData(
|
||||
Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info);
|
||||
|
||||
// Bit position in the flag, from least significant bit position.
|
||||
static const int kHiddenPrototypeBit = 0;
|
||||
static const int kUndetectableBit = 1;
|
||||
@ -240,6 +233,13 @@ class FunctionTemplateInfo : public TemplateInfo {
|
||||
static const int kDoNotCacheBit = 5;
|
||||
static const int kAcceptAnyReceiver = 6;
|
||||
|
||||
private:
|
||||
static inline FunctionTemplateRareData EnsureFunctionTemplateRareData(
|
||||
Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info);
|
||||
|
||||
static FunctionTemplateRareData AllocateFunctionTemplateRareData(
|
||||
Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info);
|
||||
|
||||
OBJECT_CONSTRUCTORS(FunctionTemplateInfo, TemplateInfo);
|
||||
};
|
||||
|
||||
|
@ -27,6 +27,17 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_AccessCheck) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
if (!isolate->MayAccess(handle(isolate->context(), isolate), object)) {
|
||||
isolate->ReportFailedAccessCheck(object);
|
||||
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
|
||||
}
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CheckIsBootstrapping) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
|
@ -205,6 +205,7 @@ namespace internal {
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#define FOR_EACH_INTRINSIC_INTERNAL(F, I) \
|
||||
F(AccessCheck, 1, 1) \
|
||||
F(AllocateInNewSpace, 1, 1) \
|
||||
F(AllocateInTargetSpace, 2, 1) \
|
||||
F(AllocateSeqOneByteString, 1, 1) \
|
||||
|
@ -101,6 +101,14 @@ void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallFunctionTemplateDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// rdx: the function template info
|
||||
// rcx: number of arguments (on the stack, not including receiver)
|
||||
Register registers[] = {rdx, rcx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void CallWithSpreadDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// rax : number of arguments (on the stack, not including receiver)
|
||||
|
Loading…
Reference in New Issue
Block a user