[turbofan] Avoid allocating rest parameters for spread calls.
We already had an optimization to turn Function.prototype.apply with arguments object, i.e. function foo() { return bar.apply(this, arguments); } into a special operator JSCallForwardVarargs, which avoids the allocation and deconstruction of the arguments object, but just passes along the incoming parameters. We can do the same for rest parameters and spread calls/constructs, i.e. class A extends B { constructor(...args) { super(...args); } } or function foo(...args) { return bar(1, 2, 3, ...args); } where we basically pass along the parameters (plus maybe additional statically known parameters). For this, we introduce a new JSConstructForwardVarargs operator and generalize the CallForwardVarargs builtins that are backing this. BUG=v8:6407,v8:6278,v8:6344 R=jarin@chromium.org Review-Url: https://codereview.chromium.org/2890023004 Cr-Commit-Position: refs/heads/master@{#45388}
This commit is contained in:
parent
8038e5cac4
commit
bfa319e5d3
@ -157,9 +157,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// r0 : number of arguments
|
||||
// r2 : start index (to support rest parameters)
|
||||
// r1 : the target to call
|
||||
Register registers[] = {r1, r2};
|
||||
Register registers[] = {r1, r0, r2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// r0 : number of arguments
|
||||
// r3 : the new target
|
||||
// r2 : start index (to support rest parameters)
|
||||
// r1 : the target to call
|
||||
Register registers[] = {r1, r3, r0, r2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
@ -177,8 +177,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// x1: target
|
||||
// x0: number of arguments
|
||||
// x2: start index (to supported rest parameters)
|
||||
Register registers[] = {x1, x2};
|
||||
Register registers[] = {x1, x0, x2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// x3: new target
|
||||
// x1: target
|
||||
// x0: number of arguments
|
||||
// x2: start index (to supported rest parameters)
|
||||
Register registers[] = {x1, x3, x0, x2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
@ -2153,54 +2153,54 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r1 : the target to call (can be any Object)
|
||||
// -- r2 : start index (to support rest parameters)
|
||||
// -- lr : return address.
|
||||
// -- sp[0] : thisArgument
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
// -- r3 : the new.target (for [[Construct]] calls)
|
||||
// -- r1 : the target to call (can be any Object)
|
||||
// -- r2 : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
Label arguments_adaptor, arguments_done;
|
||||
__ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ ldr(ip, MemOperand(r3, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ ldr(ip, MemOperand(r4, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ cmp(ip, Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ b(eq, &arguments_adaptor);
|
||||
{
|
||||
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r0, FieldMemOperand(
|
||||
r0, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(r3, fp);
|
||||
__ ldr(r5, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ ldr(r5, FieldMemOperand(r5, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ ldr(r5, FieldMemOperand(
|
||||
r5, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(r4, fp);
|
||||
}
|
||||
__ b(&arguments_done);
|
||||
__ bind(&arguments_adaptor);
|
||||
{
|
||||
// Load the length from the ArgumentsAdaptorFrame.
|
||||
__ ldr(r0, MemOperand(r3, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ ldr(r5, MemOperand(r4, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done, stack_overflow;
|
||||
__ SmiUntag(r0);
|
||||
__ sub(r0, r0, r2, SetCC);
|
||||
__ b(le, &stack_empty);
|
||||
Label stack_done, stack_overflow;
|
||||
__ SmiUntag(r5);
|
||||
__ sub(r5, r5, r2, SetCC);
|
||||
__ b(le, &stack_done);
|
||||
{
|
||||
// Check for stack overflow.
|
||||
Generate_StackOverflowCheck(masm, r0, r2, &stack_overflow);
|
||||
Generate_StackOverflowCheck(masm, r5, r2, &stack_overflow);
|
||||
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ add(r3, r3, Operand(kPointerSize));
|
||||
__ mov(r2, r0);
|
||||
__ add(r4, r4, Operand(kPointerSize));
|
||||
__ add(r0, r0, r5);
|
||||
__ bind(&loop);
|
||||
{
|
||||
__ ldr(ip, MemOperand(r3, r2, LSL, kPointerSizeLog2));
|
||||
__ ldr(ip, MemOperand(r4, r5, LSL, kPointerSizeLog2));
|
||||
__ push(ip);
|
||||
__ sub(r2, r2, Operand(1), SetCC);
|
||||
__ sub(r5, r5, Operand(1), SetCC);
|
||||
__ b(ne, &loop);
|
||||
}
|
||||
}
|
||||
@ -2208,13 +2208,9 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
__ b(&stack_done);
|
||||
__ bind(&stack_overflow);
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ mov(r0, Operand(0));
|
||||
}
|
||||
__ bind(&stack_done);
|
||||
|
||||
// Tail-call to the {code} handler.
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
@ -2286,54 +2286,54 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- x1 : the target to call (can be any Object)
|
||||
// -- x2 : start index (to support rest parameters)
|
||||
// -- lr : return address.
|
||||
// -- sp[0] : thisArgument
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
// -- x3 : the new.target (for [[Construct]] calls)
|
||||
// -- x1 : the target to call (can be any Object)
|
||||
// -- x2 : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
Label arguments_adaptor, arguments_done;
|
||||
__ Ldr(x3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ldr(x4, MemOperand(x3, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Ldr(x5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ldr(x4, MemOperand(x5, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Cmp(x4, StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
__ B(eq, &arguments_adaptor);
|
||||
{
|
||||
__ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ Ldr(x0, FieldMemOperand(x0, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldrsw(x0, FieldMemOperand(
|
||||
x0, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ Mov(x3, fp);
|
||||
__ Ldr(x6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ Ldr(x6, FieldMemOperand(x6, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Ldrsw(x6, FieldMemOperand(
|
||||
x6, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ Mov(x5, fp);
|
||||
}
|
||||
__ B(&arguments_done);
|
||||
__ Bind(&arguments_adaptor);
|
||||
{
|
||||
// Just load the length from ArgumentsAdaptorFrame.
|
||||
__ Ldrsw(x0, UntagSmiMemOperand(
|
||||
x3, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ Ldrsw(x6, UntagSmiMemOperand(
|
||||
x5, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ Bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done, stack_overflow;
|
||||
__ Subs(x0, x0, x2);
|
||||
__ B(le, &stack_empty);
|
||||
Label stack_done, stack_overflow;
|
||||
__ Subs(x6, x6, x2);
|
||||
__ B(le, &stack_done);
|
||||
{
|
||||
// Check for stack overflow.
|
||||
Generate_StackOverflowCheck(masm, x0, x2, &stack_overflow);
|
||||
Generate_StackOverflowCheck(masm, x6, x2, &stack_overflow);
|
||||
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ Add(x3, x3, kPointerSize);
|
||||
__ Mov(x2, x0);
|
||||
__ Add(x5, x5, kPointerSize);
|
||||
__ Add(x0, x0, x6);
|
||||
__ bind(&loop);
|
||||
{
|
||||
__ Ldr(x4, MemOperand(x3, x2, LSL, kPointerSizeLog2));
|
||||
__ Ldr(x4, MemOperand(x5, x6, LSL, kPointerSizeLog2));
|
||||
__ Push(x4);
|
||||
__ Subs(x2, x2, 1);
|
||||
__ Subs(x6, x6, 1);
|
||||
__ B(ne, &loop);
|
||||
}
|
||||
}
|
||||
@ -2341,11 +2341,6 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
__ B(&stack_done);
|
||||
__ Bind(&stack_overflow);
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ Bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ Mov(x0, 0);
|
||||
}
|
||||
__ Bind(&stack_done);
|
||||
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
|
@ -83,12 +83,11 @@ void Builtins::Generate_TailCall_ReceiverIsAny(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm) {
|
||||
Generate_CallForwardVarargs(masm, masm->isolate()->builtins()->Call());
|
||||
Generate_ForwardVarargs(masm, masm->isolate()->builtins()->Call());
|
||||
}
|
||||
|
||||
void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
|
||||
Generate_CallForwardVarargs(masm,
|
||||
masm->isolate()->builtins()->CallFunction());
|
||||
Generate_ForwardVarargs(masm, masm->isolate()->builtins()->CallFunction());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -17,6 +17,15 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
void Builtins::Generate_ConstructForwardVarargs(MacroAssembler* masm) {
|
||||
Generate_ForwardVarargs(masm, masm->isolate()->builtins()->Construct());
|
||||
}
|
||||
|
||||
void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
|
||||
Generate_ForwardVarargs(masm,
|
||||
masm->isolate()->builtins()->ConstructFunction());
|
||||
}
|
||||
|
||||
typedef compiler::Node Node;
|
||||
|
||||
Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
|
||||
|
@ -90,6 +90,8 @@ namespace internal {
|
||||
/* ES6 section 7.3.13 Construct (F, [argumentsList], [newTarget]) */ \
|
||||
ASM(Construct) \
|
||||
ASM(ConstructWithSpread) \
|
||||
ASM(ConstructForwardVarargs) \
|
||||
ASM(ConstructFunctionForwardVarargs) \
|
||||
ASM(JSConstructStubApi) \
|
||||
ASM(JSConstructStubGenericRestrictedReturn) \
|
||||
ASM(JSConstructStubGenericUnrestrictedReturn) \
|
||||
|
@ -124,8 +124,8 @@ class Builtins {
|
||||
|
||||
static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
|
||||
TailCallMode tail_call_mode);
|
||||
static void Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code);
|
||||
|
||||
static void Generate_ForwardVarargs(MacroAssembler* masm, Handle<Code> code);
|
||||
|
||||
static void Generate_InterpreterPushArgsThenCallImpl(
|
||||
MacroAssembler* masm, ConvertReceiverMode receiver_mode,
|
||||
|
@ -2309,15 +2309,18 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- edi : the target to call (can be any Object)
|
||||
// -- ecx : start index (to support rest parameters)
|
||||
// -- esp[0] : return address.
|
||||
// -- esp[4] : thisArgument
|
||||
// -- eax : the number of arguments (not including the receiver)
|
||||
// -- edi : the target to call (can be any Object)
|
||||
// -- edx : the new target (for [[Construct]] calls)
|
||||
// -- ecx : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Preserve new.target (in case of [[Construct]]).
|
||||
__ movd(xmm0, edx);
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
Label arguments_adaptor, arguments_done;
|
||||
__ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
|
||||
@ -2325,24 +2328,24 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ j(equal, &arguments_adaptor, Label::kNear);
|
||||
{
|
||||
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(eax,
|
||||
FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(edx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ mov(edx, FieldOperand(edx, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ mov(edx,
|
||||
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(ebx, ebp);
|
||||
}
|
||||
__ jmp(&arguments_done, Label::kNear);
|
||||
__ bind(&arguments_adaptor);
|
||||
{
|
||||
// Just load the length from the ArgumentsAdaptorFrame.
|
||||
__ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ mov(edx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done;
|
||||
__ SmiUntag(eax);
|
||||
__ sub(eax, ecx);
|
||||
__ j(less_equal, &stack_empty);
|
||||
Label stack_done;
|
||||
__ SmiUntag(edx);
|
||||
__ sub(edx, ecx);
|
||||
__ j(less_equal, &stack_done);
|
||||
{
|
||||
// Check for stack overflow.
|
||||
{
|
||||
@ -2357,7 +2360,7 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
__ add(ecx, esp);
|
||||
__ sar(ecx, kPointerSizeLog2);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(ecx, eax);
|
||||
__ cmp(ecx, edx);
|
||||
__ j(greater, &done, Label::kNear); // Signed comparison.
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ bind(&done);
|
||||
@ -2366,25 +2369,23 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ mov(ecx, eax);
|
||||
__ pop(edx);
|
||||
__ add(eax, edx);
|
||||
__ PopReturnAddressTo(ecx);
|
||||
__ bind(&loop);
|
||||
{
|
||||
__ Push(Operand(ebx, ecx, times_pointer_size, 1 * kPointerSize));
|
||||
__ dec(ecx);
|
||||
__ Push(Operand(ebx, edx, times_pointer_size, 1 * kPointerSize));
|
||||
__ dec(edx);
|
||||
__ j(not_zero, &loop);
|
||||
}
|
||||
__ push(edx);
|
||||
__ PushReturnAddressFrom(ecx);
|
||||
}
|
||||
}
|
||||
__ jmp(&stack_done, Label::kNear);
|
||||
__ bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ Move(eax, Immediate(0));
|
||||
}
|
||||
__ bind(&stack_done);
|
||||
|
||||
// Restore new.target (in case of [[Construct]]).
|
||||
__ movd(edx, xmm0);
|
||||
|
||||
// Tail-call to the {code} handler.
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
@ -2168,68 +2168,64 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : the target to call (can be any Object)
|
||||
// -- a2 : start index (to support rest parameters)
|
||||
// -- ra : return address.
|
||||
// -- sp[0] : thisArgument
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a3 : the new.target (for [[Construct]] calls)
|
||||
// -- a1 : the target to call (can be any Object)
|
||||
// -- a2 : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
Label arguments_adaptor, arguments_done;
|
||||
__ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ lw(a0, MemOperand(a3, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&arguments_adaptor, eq, a0,
|
||||
__ lw(t3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ lw(t2, MemOperand(t3, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&arguments_adaptor, eq, t2,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
{
|
||||
__ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ lw(a0, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(a0,
|
||||
FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(a3, fp);
|
||||
__ lw(t2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ lw(t2, FieldMemOperand(t2, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ lw(t2,
|
||||
FieldMemOperand(t2, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(t3, fp);
|
||||
}
|
||||
__ Branch(&arguments_done);
|
||||
__ bind(&arguments_adaptor);
|
||||
{
|
||||
// Just get the length from the ArgumentsAdaptorFrame.
|
||||
__ lw(a0, MemOperand(a3, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ lw(t2, MemOperand(t3, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done, stack_overflow;
|
||||
__ SmiUntag(a0);
|
||||
__ Subu(a0, a0, a2);
|
||||
__ Branch(&stack_empty, le, a0, Operand(zero_reg));
|
||||
Label stack_done, stack_overflow;
|
||||
__ SmiUntag(t2);
|
||||
__ Subu(t2, t2, a2);
|
||||
__ Branch(&stack_done, le, t2, Operand(zero_reg));
|
||||
{
|
||||
// Check for stack overflow.
|
||||
Generate_StackOverflowCheck(masm, a0, t0, t1, &stack_overflow);
|
||||
Generate_StackOverflowCheck(masm, t2, t0, t1, &stack_overflow);
|
||||
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ mov(a2, a0);
|
||||
__ Addu(a0, a0, t2);
|
||||
__ bind(&loop);
|
||||
{
|
||||
__ Lsa(at, a3, a2, kPointerSizeLog2);
|
||||
__ Lsa(at, t3, t2, kPointerSizeLog2);
|
||||
__ lw(at, MemOperand(at, 1 * kPointerSize));
|
||||
__ push(at);
|
||||
__ Subu(a2, a2, Operand(1));
|
||||
__ Branch(&loop, ne, a2, Operand(zero_reg));
|
||||
__ Subu(t2, t2, Operand(1));
|
||||
__ Branch(&loop, ne, t2, Operand(zero_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
__ Branch(&stack_done);
|
||||
__ bind(&stack_overflow);
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ li(a0, Operand(0));
|
||||
}
|
||||
__ bind(&stack_done);
|
||||
|
||||
// Tail-call to the {code} handler.
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
@ -2209,68 +2209,64 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : the target to call (can be any Object)
|
||||
// -- a2 : start index (to support rest parameters)
|
||||
// -- ra : return address.
|
||||
// -- sp[0] : thisArgument
|
||||
// -- a0 : the number of arguments (not including the receiver)
|
||||
// -- a3 : the new.target (for [[Construct]] calls)
|
||||
// -- a1 : the target to call (can be any Object)
|
||||
// -- a2 : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
Label arguments_adaptor, arguments_done;
|
||||
__ Ld(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ld(a0, MemOperand(a3, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&arguments_adaptor, eq, a0,
|
||||
__ Ld(a6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
|
||||
__ Ld(a7, MemOperand(a6, CommonFrameConstants::kContextOrFrameTypeOffset));
|
||||
__ Branch(&arguments_adaptor, eq, a7,
|
||||
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
{
|
||||
__ Ld(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ Ld(a0, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Lw(a0,
|
||||
FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(a3, fp);
|
||||
__ Ld(a7, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ Ld(a7, FieldMemOperand(a7, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ Lw(a7,
|
||||
FieldMemOperand(a7, SharedFunctionInfo::kFormalParameterCountOffset));
|
||||
__ mov(a6, fp);
|
||||
}
|
||||
__ Branch(&arguments_done);
|
||||
__ bind(&arguments_adaptor);
|
||||
{
|
||||
// Just get the length from the ArgumentsAdaptorFrame.
|
||||
__ Lw(a0, UntagSmiMemOperand(
|
||||
a3, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
__ Lw(a7, UntagSmiMemOperand(
|
||||
a6, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done, stack_overflow;
|
||||
__ Subu(a0, a0, a2);
|
||||
__ Branch(&stack_empty, le, a0, Operand(zero_reg));
|
||||
Label stack_done, stack_overflow;
|
||||
__ Subu(a7, a7, a2);
|
||||
__ Branch(&stack_done, le, a7, Operand(zero_reg));
|
||||
{
|
||||
// Check for stack overflow.
|
||||
Generate_StackOverflowCheck(masm, a0, a4, a5, &stack_overflow);
|
||||
Generate_StackOverflowCheck(masm, a7, a4, a5, &stack_overflow);
|
||||
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ mov(a2, a0);
|
||||
__ Daddu(a0, a0, a7);
|
||||
__ bind(&loop);
|
||||
{
|
||||
__ Dlsa(at, a3, a2, kPointerSizeLog2);
|
||||
__ Dlsa(at, a6, a7, kPointerSizeLog2);
|
||||
__ Ld(at, MemOperand(at, 1 * kPointerSize));
|
||||
__ push(at);
|
||||
__ Subu(a2, a2, Operand(1));
|
||||
__ Branch(&loop, ne, a2, Operand(zero_reg));
|
||||
__ Subu(a7, a7, Operand(1));
|
||||
__ Branch(&loop, ne, a7, Operand(zero_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
__ Branch(&stack_done);
|
||||
__ bind(&stack_overflow);
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ mov(a0, zero_reg);
|
||||
}
|
||||
__ bind(&stack_done);
|
||||
|
||||
// Tail-call to the {code} handler.
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
@ -2416,13 +2416,13 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
|
||||
Handle<Code> code) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rdi : the target to call (can be any Object)
|
||||
// -- rcx : start index (to support rest parameters)
|
||||
// -- rsp[0] : return address.
|
||||
// -- rsp[8] : thisArgument
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
// -- rdx : the new target (for [[Construct]] calls)
|
||||
// -- rdi : the target to call (can be any Object)
|
||||
// -- rcx : start index (to support rest parameters)
|
||||
// -----------------------------------
|
||||
|
||||
// Check if we have an arguments adaptor frame below the function frame.
|
||||
@ -2432,52 +2432,48 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
|
||||
Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
|
||||
__ j(equal, &arguments_adaptor, Label::kNear);
|
||||
{
|
||||
__ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ movp(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ movp(r8, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ movp(r8, FieldOperand(r8, JSFunction::kSharedFunctionInfoOffset));
|
||||
__ LoadSharedFunctionInfoSpecialField(
|
||||
rax, rax, SharedFunctionInfo::kFormalParameterCountOffset);
|
||||
r8, r8, SharedFunctionInfo::kFormalParameterCountOffset);
|
||||
__ movp(rbx, rbp);
|
||||
}
|
||||
__ jmp(&arguments_done, Label::kNear);
|
||||
__ bind(&arguments_adaptor);
|
||||
{
|
||||
__ SmiToInteger32(
|
||||
rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
r8, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||
}
|
||||
__ bind(&arguments_done);
|
||||
|
||||
Label stack_empty, stack_done, stack_overflow;
|
||||
__ subl(rax, rcx);
|
||||
__ j(less_equal, &stack_empty);
|
||||
Label stack_done, stack_overflow;
|
||||
__ subl(r8, rcx);
|
||||
__ j(less_equal, &stack_done);
|
||||
{
|
||||
// Check for stack overflow.
|
||||
Generate_StackOverflowCheck(masm, rax, rcx, &stack_overflow, Label::kNear);
|
||||
Generate_StackOverflowCheck(masm, r8, rcx, &stack_overflow, Label::kNear);
|
||||
|
||||
// Forward the arguments from the caller frame.
|
||||
{
|
||||
Label loop;
|
||||
__ movl(rcx, rax);
|
||||
__ Pop(r8);
|
||||
__ addl(rax, r8);
|
||||
__ PopReturnAddressTo(rcx);
|
||||
__ bind(&loop);
|
||||
{
|
||||
StackArgumentsAccessor args(rbx, rcx, ARGUMENTS_DONT_CONTAIN_RECEIVER);
|
||||
StackArgumentsAccessor args(rbx, r8, ARGUMENTS_DONT_CONTAIN_RECEIVER);
|
||||
__ Push(args.GetArgumentOperand(0));
|
||||
__ decl(rcx);
|
||||
__ decl(r8);
|
||||
__ j(not_zero, &loop);
|
||||
}
|
||||
__ Push(r8);
|
||||
__ PushReturnAddressFrom(rcx);
|
||||
}
|
||||
}
|
||||
__ jmp(&stack_done, Label::kNear);
|
||||
__ bind(&stack_overflow);
|
||||
__ TailCallRuntime(Runtime::kThrowStackOverflow);
|
||||
__ bind(&stack_empty);
|
||||
{
|
||||
// We just pass the receiver, which is already on the stack.
|
||||
__ Set(rax, 0);
|
||||
}
|
||||
__ bind(&stack_done);
|
||||
|
||||
// Tail-call to the {code} handler.
|
||||
__ Jump(code, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
@ -457,6 +457,18 @@ Callable CodeFactory::ConstructFunction(Isolate* isolate) {
|
||||
ConstructTrampolineDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::ConstructForwardVarargs(Isolate* isolate) {
|
||||
return Callable(isolate->builtins()->ConstructForwardVarargs(),
|
||||
ConstructForwardVarargsDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::ConstructFunctionForwardVarargs(Isolate* isolate) {
|
||||
return Callable(isolate->builtins()->ConstructFunctionForwardVarargs(),
|
||||
ConstructForwardVarargsDescriptor(isolate));
|
||||
}
|
||||
|
||||
// static
|
||||
Callable CodeFactory::InterpreterPushArgsThenCall(
|
||||
Isolate* isolate, ConvertReceiverMode receiver_mode,
|
||||
|
@ -167,6 +167,8 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
static Callable Construct(Isolate* isolate);
|
||||
static Callable ConstructWithSpread(Isolate* isolate);
|
||||
static Callable ConstructFunction(Isolate* isolate);
|
||||
static Callable ConstructForwardVarargs(Isolate* isolate);
|
||||
static Callable ConstructFunctionForwardVarargs(Isolate* isolate);
|
||||
static Callable CreateIterResultObject(Isolate* isolate);
|
||||
static Callable HasProperty(Isolate* isolate);
|
||||
static Callable ForInFilter(Isolate* isolate);
|
||||
|
@ -124,7 +124,8 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
||||
Node* arg_array = NodeProperties::GetValueInput(node, 3);
|
||||
if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
|
||||
for (Edge edge : arg_array->use_edges()) {
|
||||
Node* user = edge.from();
|
||||
Node* const user = edge.from();
|
||||
if (user == node) continue;
|
||||
// Ignore uses as frame state's locals or parameters.
|
||||
if (user->opcode() == IrOpcode::kStateValues) continue;
|
||||
// Ignore uses as frame state's accumulator.
|
||||
@ -133,7 +134,6 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
||||
continue;
|
||||
}
|
||||
if (!NodeProperties::IsValueEdge(edge)) continue;
|
||||
if (edge.from() == node) continue;
|
||||
return NoChange();
|
||||
}
|
||||
// Check if the arguments can be handled in the fast case (i.e. we don't
|
||||
@ -173,7 +173,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
||||
node->RemoveInput(0); // Function.prototype.apply
|
||||
node->RemoveInput(2); // arguments
|
||||
NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
|
||||
start_index, p.tail_call_mode()));
|
||||
2, start_index, p.tail_call_mode()));
|
||||
return Changed(node);
|
||||
}
|
||||
// Get to the actual frame state from which to extract the arguments;
|
||||
@ -454,50 +454,81 @@ Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) {
|
||||
// of spread (except for value uses in frame states).
|
||||
if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
|
||||
for (Edge edge : spread->use_edges()) {
|
||||
if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
|
||||
Node* const user = edge.from();
|
||||
if (user == node) continue;
|
||||
// Ignore uses as frame state's locals or parameters.
|
||||
if (user->opcode() == IrOpcode::kStateValues) continue;
|
||||
// Ignore uses as frame state's accumulator.
|
||||
if (user->opcode() == IrOpcode::kFrameState && user->InputAt(2) == spread) {
|
||||
continue;
|
||||
}
|
||||
if (!NodeProperties::IsValueEdge(edge)) continue;
|
||||
if (edge.from() == node) continue;
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// Get to the actual frame state from which to extract the arguments;
|
||||
// we can only optimize this in case the {node} was already inlined into
|
||||
// some other function (and same for the {spread}).
|
||||
CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
|
||||
CreateArgumentsType const type = CreateArgumentsTypeOf(spread->op());
|
||||
Node* frame_state = NodeProperties::GetFrameStateInput(spread);
|
||||
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
|
||||
int start_index = 0;
|
||||
// Determine the formal parameter count;
|
||||
Handle<SharedFunctionInfo> shared;
|
||||
if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
||||
int formal_parameter_count = shared->internal_formal_parameter_count();
|
||||
if (type == CreateArgumentsType::kMappedArguments) {
|
||||
// Mapped arguments (sloppy mode) that are aliased can only be handled
|
||||
// here if there's no side-effect between the {node} and the {arg_array}.
|
||||
// TODO(turbofan): Further relax this constraint.
|
||||
if (formal_parameter_count != 0) {
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
while (effect != spread) {
|
||||
if (effect->op()->EffectInputCount() != 1 ||
|
||||
!(effect->op()->properties() & Operator::kNoWrite)) {
|
||||
return NoChange();
|
||||
}
|
||||
effect = NodeProperties::GetEffectInput(effect);
|
||||
}
|
||||
}
|
||||
} else if (type == CreateArgumentsType::kRestParameter) {
|
||||
start_index = formal_parameter_count;
|
||||
|
||||
// Only check the array iterator protector when we have a rest object.
|
||||
if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
|
||||
}
|
||||
|
||||
// Install appropriate code dependencies.
|
||||
dependencies()->AssumeMapStable(
|
||||
isolate()->initial_array_iterator_prototype_map());
|
||||
if (type == CreateArgumentsType::kRestParameter) {
|
||||
dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
|
||||
}
|
||||
// Remove the spread input from the {node}.
|
||||
node->RemoveInput(arity--);
|
||||
// Check if are spreading to inlined arguments or to the arguments of
|
||||
// the outermost function.
|
||||
Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
|
||||
if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
|
||||
if (outer_state->opcode() != IrOpcode::kFrameState) {
|
||||
Operator const* op =
|
||||
(node->opcode() == IrOpcode::kJSCallWithSpread)
|
||||
? javascript()->CallForwardVarargs(arity + 1, start_index,
|
||||
TailCallMode::kDisallow)
|
||||
: javascript()->ConstructForwardVarargs(arity + 2, start_index);
|
||||
NodeProperties::ChangeOp(node, op);
|
||||
return Changed(node);
|
||||
}
|
||||
// Get to the actual frame state from which to extract the arguments;
|
||||
// we can only optimize this in case the {node} was already inlined into
|
||||
// some other function (and same for the {arg_array}).
|
||||
FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
|
||||
if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
|
||||
// Need to take the parameters from the arguments adaptor.
|
||||
frame_state = outer_state;
|
||||
}
|
||||
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
|
||||
int start_index = 0;
|
||||
if (type == CreateArgumentsType::kMappedArguments) {
|
||||
// Mapped arguments (sloppy mode) cannot be handled if they are aliased.
|
||||
Handle<SharedFunctionInfo> shared;
|
||||
if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
||||
if (shared->internal_formal_parameter_count() != 0) return NoChange();
|
||||
} else if (type == CreateArgumentsType::kRestParameter) {
|
||||
Handle<SharedFunctionInfo> shared;
|
||||
if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
||||
start_index = shared->internal_formal_parameter_count();
|
||||
|
||||
// Only check the array iterator protector when we have a rest object.
|
||||
if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
|
||||
// Add a code dependency on the array iterator protector.
|
||||
dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
|
||||
}
|
||||
|
||||
dependencies()->AssumeMapStable(
|
||||
isolate()->initial_array_iterator_prototype_map());
|
||||
|
||||
node->RemoveInput(arity--);
|
||||
|
||||
// Add the actual parameters to the {node}, skipping the receiver.
|
||||
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
|
||||
for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
|
||||
for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
|
||||
node->InsertInput(graph()->zone(), static_cast<int>(++arity),
|
||||
parameters->InputAt(i));
|
||||
}
|
||||
|
@ -523,6 +523,28 @@ void JSGenericLowering::LowerJSCreateScriptContext(Node* node) {
|
||||
ReplaceWithRuntimeCall(node, Runtime::kNewScriptContext);
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSConstructForwardVarargs(Node* node) {
|
||||
ConstructForwardVarargsParameters p =
|
||||
ConstructForwardVarargsParametersOf(node->op());
|
||||
int const arg_count = static_cast<int>(p.arity() - 2);
|
||||
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
|
||||
Callable callable = CodeFactory::ConstructForwardVarargs(isolate());
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
|
||||
Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
||||
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
|
||||
Node* start_index = jsgraph()->Uint32Constant(p.start_index());
|
||||
Node* new_target = node->InputAt(arg_count + 1);
|
||||
Node* receiver = jsgraph()->UndefinedConstant();
|
||||
node->RemoveInput(arg_count + 1); // Drop new target.
|
||||
node->InsertInput(zone(), 0, stub_code);
|
||||
node->InsertInput(zone(), 2, new_target);
|
||||
node->InsertInput(zone(), 3, stub_arity);
|
||||
node->InsertInput(zone(), 4, start_index);
|
||||
node->InsertInput(zone(), 5, receiver);
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSConstruct(Node* node) {
|
||||
ConstructParameters const& p = ConstructParametersOf(node->op());
|
||||
int const arg_count = static_cast<int>(p.arity() - 2);
|
||||
@ -563,17 +585,20 @@ void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
|
||||
|
||||
void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
|
||||
CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
|
||||
Callable callable = CodeFactory::CallForwardVarargs(isolate());
|
||||
int const arg_count = static_cast<int>(p.arity() - 2);
|
||||
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
|
||||
Callable callable = CodeFactory::CallForwardVarargs(isolate());
|
||||
if (p.tail_call_mode() == TailCallMode::kAllow) {
|
||||
flags |= CallDescriptor::kSupportsTailCalls;
|
||||
}
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), zone(), callable.descriptor(), 1, flags);
|
||||
isolate(), zone(), callable.descriptor(), arg_count + 1, flags);
|
||||
Node* stub_code = jsgraph()->HeapConstant(callable.code());
|
||||
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
|
||||
Node* start_index = jsgraph()->Uint32Constant(p.start_index());
|
||||
node->InsertInput(zone(), 0, stub_code);
|
||||
node->InsertInput(zone(), 2, start_index);
|
||||
node->InsertInput(zone(), 2, stub_arity);
|
||||
node->InsertInput(zone(), 3, start_index);
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,17 @@ ToBooleanHints ToBooleanHintsOf(Operator const* op) {
|
||||
return OpParameter<ToBooleanHints>(op);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
ConstructForwardVarargsParameters const& p) {
|
||||
return os << p.arity() << ", " << p.start_index();
|
||||
}
|
||||
|
||||
ConstructForwardVarargsParameters const& ConstructForwardVarargsParametersOf(
|
||||
Operator const* op) {
|
||||
DCHECK_EQ(IrOpcode::kJSConstructForwardVarargs, op->opcode());
|
||||
return OpParameter<ConstructForwardVarargsParameters>(op);
|
||||
}
|
||||
|
||||
bool operator==(ConstructParameters const& lhs,
|
||||
ConstructParameters const& rhs) {
|
||||
return lhs.arity() == rhs.arity() && lhs.frequency() == rhs.frequency() &&
|
||||
@ -118,7 +129,8 @@ const CallParameters& CallParametersOf(const Operator* op) {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
CallForwardVarargsParameters const& p) {
|
||||
return os << p.start_index() << ", " << p.tail_call_mode();
|
||||
return os << p.arity() << ", " << p.start_index() << ", "
|
||||
<< p.tail_call_mode();
|
||||
}
|
||||
|
||||
CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
|
||||
@ -743,12 +755,12 @@ const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::CallForwardVarargs(
|
||||
uint32_t start_index, TailCallMode tail_call_mode) {
|
||||
CallForwardVarargsParameters parameters(start_index, tail_call_mode);
|
||||
size_t arity, uint32_t start_index, TailCallMode tail_call_mode) {
|
||||
CallForwardVarargsParameters parameters(arity, start_index, tail_call_mode);
|
||||
return new (zone()) Operator1<CallForwardVarargsParameters>( // --
|
||||
IrOpcode::kJSCallForwardVarargs, Operator::kNoProperties, // opcode
|
||||
"JSCallForwardVarargs", // name
|
||||
2, 1, 1, 1, 1, 2, // counts
|
||||
parameters.arity(), 1, 1, 1, 1, 2, // counts
|
||||
parameters); // parameter
|
||||
}
|
||||
|
||||
@ -798,6 +810,16 @@ const Operator* JSOperatorBuilder::CallRuntime(const Runtime::Function* f,
|
||||
parameters); // parameter
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::ConstructForwardVarargs(
|
||||
size_t arity, uint32_t start_index) {
|
||||
ConstructForwardVarargsParameters parameters(arity, start_index);
|
||||
return new (zone()) Operator1<ConstructForwardVarargsParameters>( // --
|
||||
IrOpcode::kJSConstructForwardVarargs, Operator::kNoProperties, // opcode
|
||||
"JSConstructForwardVarargs", // name
|
||||
parameters.arity(), 1, 1, 1, 1, 2, // counts
|
||||
parameters); // parameter
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::Construct(uint32_t arity,
|
||||
CallFrequency frequency,
|
||||
VectorSlotPair const& feedback) {
|
||||
|
@ -90,6 +90,40 @@ ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
|
||||
// The ToBooleanHints are used as parameter by JSToBoolean operators.
|
||||
ToBooleanHints ToBooleanHintsOf(Operator const* op);
|
||||
|
||||
// Defines the flags for a JavaScript call forwarding parameters. This
|
||||
// is used as parameter by JSConstructForwardVarargs operators.
|
||||
class ConstructForwardVarargsParameters final {
|
||||
public:
|
||||
ConstructForwardVarargsParameters(size_t arity, uint32_t start_index)
|
||||
: bit_field_(ArityField::encode(arity) |
|
||||
StartIndexField::encode(start_index)) {}
|
||||
|
||||
size_t arity() const { return ArityField::decode(bit_field_); }
|
||||
uint32_t start_index() const { return StartIndexField::decode(bit_field_); }
|
||||
|
||||
bool operator==(ConstructForwardVarargsParameters const& that) const {
|
||||
return this->bit_field_ == that.bit_field_;
|
||||
}
|
||||
bool operator!=(ConstructForwardVarargsParameters const& that) const {
|
||||
return !(*this == that);
|
||||
}
|
||||
|
||||
private:
|
||||
friend size_t hash_value(ConstructForwardVarargsParameters const& p) {
|
||||
return p.bit_field_;
|
||||
}
|
||||
|
||||
typedef BitField<size_t, 0, 16> ArityField;
|
||||
typedef BitField<uint32_t, 16, 16> StartIndexField;
|
||||
|
||||
uint32_t const bit_field_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&,
|
||||
ConstructForwardVarargsParameters const&);
|
||||
|
||||
ConstructForwardVarargsParameters const& ConstructForwardVarargsParametersOf(
|
||||
Operator const*) WARN_UNUSED_RESULT;
|
||||
|
||||
// Defines the arity and the feedback for a JavaScript constructor call. This is
|
||||
// used as a parameter by JSConstruct operators.
|
||||
@ -146,11 +180,13 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const*);
|
||||
// is used as parameter by JSCallForwardVarargs operators.
|
||||
class CallForwardVarargsParameters final {
|
||||
public:
|
||||
CallForwardVarargsParameters(uint32_t start_index,
|
||||
CallForwardVarargsParameters(size_t arity, uint32_t start_index,
|
||||
TailCallMode tail_call_mode)
|
||||
: bit_field_(StartIndexField::encode(start_index) |
|
||||
: bit_field_(ArityField::encode(arity) |
|
||||
StartIndexField::encode(start_index) |
|
||||
TailCallModeField::encode(tail_call_mode)) {}
|
||||
|
||||
size_t arity() const { return ArityField::decode(bit_field_); }
|
||||
uint32_t start_index() const { return StartIndexField::decode(bit_field_); }
|
||||
TailCallMode tail_call_mode() const {
|
||||
return TailCallModeField::decode(bit_field_);
|
||||
@ -168,8 +204,9 @@ class CallForwardVarargsParameters final {
|
||||
return p.bit_field_;
|
||||
}
|
||||
|
||||
typedef BitField<uint32_t, 0, 30> StartIndexField;
|
||||
typedef BitField<TailCallMode, 31, 1> TailCallModeField;
|
||||
typedef BitField<size_t, 0, 15> ArityField;
|
||||
typedef BitField<uint32_t, 15, 15> StartIndexField;
|
||||
typedef BitField<TailCallMode, 30, 1> TailCallModeField;
|
||||
|
||||
uint32_t const bit_field_;
|
||||
};
|
||||
@ -665,7 +702,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
||||
const Operator* CreateLiteralRegExp(Handle<String> constant_pattern,
|
||||
int literal_flags, int literal_index);
|
||||
|
||||
const Operator* CallForwardVarargs(uint32_t start_index,
|
||||
const Operator* CallForwardVarargs(size_t arity, uint32_t start_index,
|
||||
TailCallMode tail_call_mode);
|
||||
const Operator* Call(
|
||||
size_t arity, CallFrequency frequency = CallFrequency(),
|
||||
@ -676,6 +713,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
||||
const Operator* CallRuntime(Runtime::FunctionId id);
|
||||
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
|
||||
const Operator* CallRuntime(const Runtime::Function* function, size_t arity);
|
||||
|
||||
const Operator* ConstructForwardVarargs(size_t arity, uint32_t start_index);
|
||||
const Operator* Construct(uint32_t arity,
|
||||
CallFrequency frequency = CallFrequency(),
|
||||
VectorSlotPair const& feedback = VectorSlotPair());
|
||||
|
@ -1703,6 +1703,38 @@ bool NeedsArgumentAdaptorFrame(Handle<SharedFunctionInfo> shared, int arity) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Reduction JSTypedLowering::ReduceJSConstructForwardVarargs(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSConstructForwardVarargs, node->opcode());
|
||||
ConstructForwardVarargsParameters p =
|
||||
ConstructForwardVarargsParametersOf(node->op());
|
||||
DCHECK_LE(2u, p.arity());
|
||||
int const arity = static_cast<int>(p.arity() - 2);
|
||||
int const start_index = static_cast<int>(p.start_index());
|
||||
Node* target = NodeProperties::GetValueInput(node, 0);
|
||||
Type* target_type = NodeProperties::GetType(target);
|
||||
Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
|
||||
|
||||
// Check if {target} is a JSFunction.
|
||||
if (target_type->Is(Type::Function())) {
|
||||
// Patch {node} to an indirect call via ConstructFunctionForwardVarargs.
|
||||
Callable callable = CodeFactory::ConstructFunctionForwardVarargs(isolate());
|
||||
node->RemoveInput(arity + 1);
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
jsgraph()->HeapConstant(callable.code()));
|
||||
node->InsertInput(graph()->zone(), 2, new_target);
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(start_index));
|
||||
node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
|
||||
NodeProperties::ChangeOp(
|
||||
node, common()->Call(Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), callable.descriptor(), arity + 1,
|
||||
CallDescriptor::kNeedsFrameState)));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
|
||||
ConstructParameters const& p = ConstructParametersOf(node->op());
|
||||
@ -1781,6 +1813,9 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
|
||||
Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode());
|
||||
CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
|
||||
DCHECK_LE(2u, p.arity());
|
||||
int const arity = static_cast<int>(p.arity() - 2);
|
||||
int const start_index = static_cast<int>(p.start_index());
|
||||
Node* target = NodeProperties::GetValueInput(node, 0);
|
||||
Type* target_type = NodeProperties::GetType(target);
|
||||
|
||||
@ -1796,11 +1831,12 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
|
||||
Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
jsgraph()->HeapConstant(callable.code()));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(p.start_index()));
|
||||
node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(arity));
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(start_index));
|
||||
NodeProperties::ChangeOp(
|
||||
node,
|
||||
common()->Call(Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), callable.descriptor(), 1, flags)));
|
||||
node, common()->Call(Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), callable.descriptor(), arity + 1,
|
||||
flags)));
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
@ -2176,6 +2212,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
|
||||
return ReduceJSStoreModule(node);
|
||||
case IrOpcode::kJSConvertReceiver:
|
||||
return ReduceJSConvertReceiver(node);
|
||||
case IrOpcode::kJSConstructForwardVarargs:
|
||||
return ReduceJSConstructForwardVarargs(node);
|
||||
case IrOpcode::kJSConstruct:
|
||||
return ReduceJSConstruct(node);
|
||||
case IrOpcode::kJSCallForwardVarargs:
|
||||
|
@ -69,6 +69,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
|
||||
Reduction ReduceJSToString(Node* node);
|
||||
Reduction ReduceJSToObject(Node* node);
|
||||
Reduction ReduceJSConvertReceiver(Node* node);
|
||||
Reduction ReduceJSConstructForwardVarargs(Node* node);
|
||||
Reduction ReduceJSConstruct(Node* node);
|
||||
Reduction ReduceJSCallForwardVarargs(Node* node);
|
||||
Reduction ReduceJSCall(Node* node);
|
||||
|
@ -159,6 +159,7 @@
|
||||
V(JSCreateScriptContext)
|
||||
|
||||
#define JS_OTHER_OP_LIST(V) \
|
||||
V(JSConstructForwardVarargs) \
|
||||
V(JSConstruct) \
|
||||
V(JSConstructWithSpread) \
|
||||
V(JSCallForwardVarargs) \
|
||||
|
@ -96,6 +96,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
|
||||
case IrOpcode::kJSToString:
|
||||
|
||||
// Call operations
|
||||
case IrOpcode::kJSConstructForwardVarargs:
|
||||
case IrOpcode::kJSConstruct:
|
||||
case IrOpcode::kJSConstructWithSpread:
|
||||
case IrOpcode::kJSCallForwardVarargs:
|
||||
|
@ -1335,6 +1335,10 @@ Type* Typer::Visitor::TypeJSCreateScriptContext(Node* node) {
|
||||
|
||||
// JS other operators.
|
||||
|
||||
Type* Typer::Visitor::TypeJSConstructForwardVarargs(Node* node) {
|
||||
return Type::Receiver();
|
||||
}
|
||||
|
||||
Type* Typer::Visitor::TypeJSConstruct(Node* node) { return Type::Receiver(); }
|
||||
|
||||
Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) {
|
||||
|
@ -700,6 +700,7 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
break;
|
||||
}
|
||||
|
||||
case IrOpcode::kJSConstructForwardVarargs:
|
||||
case IrOpcode::kJSConstruct:
|
||||
case IrOpcode::kJSConstructWithSpread:
|
||||
case IrOpcode::kJSConvertReceiver:
|
||||
|
@ -156,9 +156,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// eax : number of arguments
|
||||
// ecx : start index (to support rest parameters)
|
||||
// edi : the target to call
|
||||
Register registers[] = {edi, ecx};
|
||||
Register registers[] = {edi, eax, ecx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// eax : number of arguments
|
||||
// edx : the new target
|
||||
// ecx : start index (to support rest parameters)
|
||||
// edi : the target to call
|
||||
Register registers[] = {edi, edx, eax, ecx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
@ -447,8 +447,18 @@ void CallTrampolineDescriptor::InitializePlatformIndependent(
|
||||
|
||||
void CallForwardVarargsDescriptor::InitializePlatformIndependent(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// kTarget, kStartIndex
|
||||
// kTarget, kActualArgumentsCount, kStartIndex
|
||||
MachineType machine_types[] = {MachineType::AnyTagged(), MachineType::Int32(),
|
||||
MachineType::Int32()};
|
||||
data->InitializePlatformIndependent(arraysize(machine_types), 0,
|
||||
machine_types);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformIndependent(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// kTarget, kNewTarget, kActualArgumentsCount, kStartIndex
|
||||
MachineType machine_types[] = {MachineType::AnyTagged(),
|
||||
MachineType::AnyTagged(), MachineType::Int32(),
|
||||
MachineType::Int32()};
|
||||
data->InitializePlatformIndependent(arraysize(machine_types), 0,
|
||||
machine_types);
|
||||
|
@ -48,6 +48,7 @@ class PlatformInterfaceDescriptor;
|
||||
V(CallConstruct) \
|
||||
V(CallTrampoline) \
|
||||
V(ConstructStub) \
|
||||
V(ConstructForwardVarargs) \
|
||||
V(ConstructTrampoline) \
|
||||
V(TransitionElementsKind) \
|
||||
V(AllocateHeapNumber) \
|
||||
@ -575,11 +576,18 @@ class CallTrampolineDescriptor : public CallInterfaceDescriptor {
|
||||
|
||||
class CallForwardVarargsDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kTarget, kStartIndex)
|
||||
DEFINE_PARAMETERS(kTarget, kActualArgumentsCount, kStartIndex)
|
||||
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(CallForwardVarargsDescriptor,
|
||||
CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class ConstructForwardVarargsDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kTarget, kNewTarget, kActualArgumentsCount, kStartIndex)
|
||||
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(
|
||||
ConstructForwardVarargsDescriptor, CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class ConstructStubDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kFunction, kNewTarget, kActualArgumentsCount,
|
||||
|
@ -156,8 +156,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// a1: the target to call
|
||||
// a0: number of arguments
|
||||
// a2: start index (to support rest parameters)
|
||||
Register registers[] = {a1, a2};
|
||||
Register registers[] = {a1, a0, a2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// a1: the target to call
|
||||
// a3: new target
|
||||
// a0: number of arguments
|
||||
// a2: start index (to support rest parameters)
|
||||
Register registers[] = {a1, a3, a0, a2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
@ -156,8 +156,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// a1: the target to call
|
||||
// a0: number of arguments
|
||||
// a2: start index (to support rest parameters)
|
||||
Register registers[] = {a1, a2};
|
||||
Register registers[] = {a1, a0, a2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// a1: the target to call
|
||||
// a3: new target
|
||||
// a0: number of arguments
|
||||
// a2: start index (to support rest parameters)
|
||||
Register registers[] = {a1, a3, a0, a2};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
@ -155,9 +155,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
|
||||
|
||||
void CallForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// rax : number of arguments
|
||||
// rcx : start index (to support rest parameters)
|
||||
// rdi : the target to call
|
||||
Register registers[] = {rdi, rcx};
|
||||
Register registers[] = {rdi, rax, rcx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void ConstructForwardVarargsDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
// rax : number of arguments
|
||||
// rdx : the new target
|
||||
// rcx : start index (to support rest parameters)
|
||||
// rdi : the target to call
|
||||
Register registers[] = {rdi, rdx, rax, rcx};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user