[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:
bmeurer 2017-05-18 00:32:22 -07:00 committed by Commit bot
parent 8038e5cac4
commit bfa319e5d3
30 changed files with 476 additions and 225 deletions

View File

@ -157,9 +157,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// r0 : number of arguments
// r2 : start index (to support rest parameters) // r2 : start index (to support rest parameters)
// r1 : the target to call // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }

View File

@ -177,8 +177,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// x1: target // x1: target
// x0: number of arguments
// x2: start index (to supported rest parameters) // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }

View File

@ -2153,54 +2153,54 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- r1 : the target to call (can be any Object) // -- r0 : the number of arguments (not including the receiver)
// -- r2 : start index (to support rest parameters) // -- r3 : the new.target (for [[Construct]] calls)
// -- lr : return address. // -- r1 : the target to call (can be any Object)
// -- sp[0] : thisArgument // -- r2 : start index (to support rest parameters)
// ----------------------------------- // -----------------------------------
// Check if we have an arguments adaptor frame below the function frame. // Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done; Label arguments_adaptor, arguments_done;
__ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ ldr(ip, MemOperand(r3, CommonFrameConstants::kContextOrFrameTypeOffset)); __ ldr(ip, MemOperand(r4, CommonFrameConstants::kContextOrFrameTypeOffset));
__ cmp(ip, Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); __ cmp(ip, Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
__ b(eq, &arguments_adaptor); __ b(eq, &arguments_adaptor);
{ {
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ ldr(r5, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset)); __ ldr(r5, FieldMemOperand(r5, JSFunction::kSharedFunctionInfoOffset));
__ ldr(r0, FieldMemOperand( __ ldr(r5, FieldMemOperand(
r0, SharedFunctionInfo::kFormalParameterCountOffset)); r5, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(r3, fp); __ mov(r4, fp);
} }
__ b(&arguments_done); __ b(&arguments_done);
__ bind(&arguments_adaptor); __ bind(&arguments_adaptor);
{ {
// Load the length from the ArgumentsAdaptorFrame. // Load the length from the ArgumentsAdaptorFrame.
__ ldr(r0, MemOperand(r3, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ ldr(r5, MemOperand(r4, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ bind(&arguments_done); __ bind(&arguments_done);
Label stack_empty, stack_done, stack_overflow; Label stack_done, stack_overflow;
__ SmiUntag(r0); __ SmiUntag(r5);
__ sub(r0, r0, r2, SetCC); __ sub(r5, r5, r2, SetCC);
__ b(le, &stack_empty); __ b(le, &stack_done);
{ {
// Check for stack overflow. // 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. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ add(r3, r3, Operand(kPointerSize)); __ add(r4, r4, Operand(kPointerSize));
__ mov(r2, r0); __ add(r0, r0, r5);
__ bind(&loop); __ bind(&loop);
{ {
__ ldr(ip, MemOperand(r3, r2, LSL, kPointerSizeLog2)); __ ldr(ip, MemOperand(r4, r5, LSL, kPointerSizeLog2));
__ push(ip); __ push(ip);
__ sub(r2, r2, Operand(1), SetCC); __ sub(r5, r5, Operand(1), SetCC);
__ b(ne, &loop); __ b(ne, &loop);
} }
} }
@ -2208,13 +2208,9 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
__ b(&stack_done); __ b(&stack_done);
__ bind(&stack_overflow); __ bind(&stack_overflow);
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ bind(&stack_empty);
{
// We just pass the receiver, which is already on the stack.
__ mov(r0, Operand(0));
}
__ bind(&stack_done); __ bind(&stack_done);
// Tail-call to the {code} handler.
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
} }

View File

@ -2286,54 +2286,54 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- x1 : the target to call (can be any Object) // -- x0 : the number of arguments (not including the receiver)
// -- x2 : start index (to support rest parameters) // -- x3 : the new.target (for [[Construct]] calls)
// -- lr : return address. // -- x1 : the target to call (can be any Object)
// -- sp[0] : thisArgument // -- x2 : start index (to support rest parameters)
// ----------------------------------- // -----------------------------------
// Check if we have an arguments adaptor frame below the function frame. // Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done; Label arguments_adaptor, arguments_done;
__ Ldr(x3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ Ldr(x5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ Ldr(x4, MemOperand(x3, CommonFrameConstants::kContextOrFrameTypeOffset)); __ Ldr(x4, MemOperand(x5, CommonFrameConstants::kContextOrFrameTypeOffset));
__ Cmp(x4, StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)); __ Cmp(x4, StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR));
__ B(eq, &arguments_adaptor); __ B(eq, &arguments_adaptor);
{ {
__ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ Ldr(x6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Ldr(x0, FieldMemOperand(x0, JSFunction::kSharedFunctionInfoOffset)); __ Ldr(x6, FieldMemOperand(x6, JSFunction::kSharedFunctionInfoOffset));
__ Ldrsw(x0, FieldMemOperand( __ Ldrsw(x6, FieldMemOperand(
x0, SharedFunctionInfo::kFormalParameterCountOffset)); x6, SharedFunctionInfo::kFormalParameterCountOffset));
__ Mov(x3, fp); __ Mov(x5, fp);
} }
__ B(&arguments_done); __ B(&arguments_done);
__ Bind(&arguments_adaptor); __ Bind(&arguments_adaptor);
{ {
// Just load the length from ArgumentsAdaptorFrame. // Just load the length from ArgumentsAdaptorFrame.
__ Ldrsw(x0, UntagSmiMemOperand( __ Ldrsw(x6, UntagSmiMemOperand(
x3, ArgumentsAdaptorFrameConstants::kLengthOffset)); x5, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ Bind(&arguments_done); __ Bind(&arguments_done);
Label stack_empty, stack_done, stack_overflow; Label stack_done, stack_overflow;
__ Subs(x0, x0, x2); __ Subs(x6, x6, x2);
__ B(le, &stack_empty); __ B(le, &stack_done);
{ {
// Check for stack overflow. // 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. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ Add(x3, x3, kPointerSize); __ Add(x5, x5, kPointerSize);
__ Mov(x2, x0); __ Add(x0, x0, x6);
__ bind(&loop); __ bind(&loop);
{ {
__ Ldr(x4, MemOperand(x3, x2, LSL, kPointerSizeLog2)); __ Ldr(x4, MemOperand(x5, x6, LSL, kPointerSizeLog2));
__ Push(x4); __ Push(x4);
__ Subs(x2, x2, 1); __ Subs(x6, x6, 1);
__ B(ne, &loop); __ B(ne, &loop);
} }
} }
@ -2341,11 +2341,6 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
__ B(&stack_done); __ B(&stack_done);
__ Bind(&stack_overflow); __ Bind(&stack_overflow);
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ Bind(&stack_empty);
{
// We just pass the receiver, which is already on the stack.
__ Mov(x0, 0);
}
__ Bind(&stack_done); __ Bind(&stack_done);
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);

View File

@ -83,12 +83,11 @@ void Builtins::Generate_TailCall_ReceiverIsAny(MacroAssembler* masm) {
} }
void Builtins::Generate_CallForwardVarargs(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) { void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
Generate_CallForwardVarargs(masm, Generate_ForwardVarargs(masm, masm->isolate()->builtins()->CallFunction());
masm->isolate()->builtins()->CallFunction());
} }
} // namespace internal } // namespace internal

View File

@ -17,6 +17,15 @@
namespace v8 { namespace v8 {
namespace internal { 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; typedef compiler::Node Node;
Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info, Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,

View File

@ -90,6 +90,8 @@ namespace internal {
/* ES6 section 7.3.13 Construct (F, [argumentsList], [newTarget]) */ \ /* ES6 section 7.3.13 Construct (F, [argumentsList], [newTarget]) */ \
ASM(Construct) \ ASM(Construct) \
ASM(ConstructWithSpread) \ ASM(ConstructWithSpread) \
ASM(ConstructForwardVarargs) \
ASM(ConstructFunctionForwardVarargs) \
ASM(JSConstructStubApi) \ ASM(JSConstructStubApi) \
ASM(JSConstructStubGenericRestrictedReturn) \ ASM(JSConstructStubGenericRestrictedReturn) \
ASM(JSConstructStubGenericUnrestrictedReturn) \ ASM(JSConstructStubGenericUnrestrictedReturn) \

View File

@ -124,8 +124,8 @@ class Builtins {
static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode, static void Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
TailCallMode tail_call_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( static void Generate_InterpreterPushArgsThenCallImpl(
MacroAssembler* masm, ConvertReceiverMode receiver_mode, MacroAssembler* masm, ConvertReceiverMode receiver_mode,

View File

@ -2309,15 +2309,18 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- edi : the target to call (can be any Object) // -- eax : the number of arguments (not including the receiver)
// -- ecx : start index (to support rest parameters) // -- edi : the target to call (can be any Object)
// -- esp[0] : return address. // -- edx : the new target (for [[Construct]] calls)
// -- esp[4] : thisArgument // -- 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. // Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done; Label arguments_adaptor, arguments_done;
__ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
@ -2325,24 +2328,24 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
__ j(equal, &arguments_adaptor, Label::kNear); __ j(equal, &arguments_adaptor, Label::kNear);
{ {
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ mov(edx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); __ mov(edx, FieldOperand(edx, JSFunction::kSharedFunctionInfoOffset));
__ mov(eax, __ mov(edx,
FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset)); FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(ebx, ebp); __ mov(ebx, ebp);
} }
__ jmp(&arguments_done, Label::kNear); __ jmp(&arguments_done, Label::kNear);
__ bind(&arguments_adaptor); __ bind(&arguments_adaptor);
{ {
// Just load the length from the ArgumentsAdaptorFrame. // Just load the length from the ArgumentsAdaptorFrame.
__ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ mov(edx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ bind(&arguments_done); __ bind(&arguments_done);
Label stack_empty, stack_done; Label stack_done;
__ SmiUntag(eax); __ SmiUntag(edx);
__ sub(eax, ecx); __ sub(edx, ecx);
__ j(less_equal, &stack_empty); __ j(less_equal, &stack_done);
{ {
// Check for stack overflow. // Check for stack overflow.
{ {
@ -2357,7 +2360,7 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
__ add(ecx, esp); __ add(ecx, esp);
__ sar(ecx, kPointerSizeLog2); __ sar(ecx, kPointerSizeLog2);
// Check if the arguments will overflow the stack. // Check if the arguments will overflow the stack.
__ cmp(ecx, eax); __ cmp(ecx, edx);
__ j(greater, &done, Label::kNear); // Signed comparison. __ j(greater, &done, Label::kNear); // Signed comparison.
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ bind(&done); __ bind(&done);
@ -2366,25 +2369,23 @@ void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm,
// Forward the arguments from the caller frame. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ mov(ecx, eax); __ add(eax, edx);
__ pop(edx); __ PopReturnAddressTo(ecx);
__ bind(&loop); __ bind(&loop);
{ {
__ Push(Operand(ebx, ecx, times_pointer_size, 1 * kPointerSize)); __ Push(Operand(ebx, edx, times_pointer_size, 1 * kPointerSize));
__ dec(ecx); __ dec(edx);
__ j(not_zero, &loop); __ 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); __ bind(&stack_done);
// Restore new.target (in case of [[Construct]]).
__ movd(edx, xmm0);
// Tail-call to the {code} handler.
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
} }

View File

@ -2168,68 +2168,64 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- a1 : the target to call (can be any Object) // -- a0 : the number of arguments (not including the receiver)
// -- a2 : start index (to support rest parameters) // -- a3 : the new.target (for [[Construct]] calls)
// -- ra : return address. // -- a1 : the target to call (can be any Object)
// -- sp[0] : thisArgument // -- a2 : start index (to support rest parameters)
// ----------------------------------- // -----------------------------------
// Check if we have an arguments adaptor frame below the function frame. // Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done; Label arguments_adaptor, arguments_done;
__ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ lw(t3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ lw(a0, MemOperand(a3, CommonFrameConstants::kContextOrFrameTypeOffset)); __ lw(t2, MemOperand(t3, CommonFrameConstants::kContextOrFrameTypeOffset));
__ Branch(&arguments_adaptor, eq, a0, __ Branch(&arguments_adaptor, eq, t2,
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
{ {
__ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ lw(t2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ lw(a0, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset)); __ lw(t2, FieldMemOperand(t2, JSFunction::kSharedFunctionInfoOffset));
__ lw(a0, __ lw(t2,
FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset)); FieldMemOperand(t2, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(a3, fp); __ mov(t3, fp);
} }
__ Branch(&arguments_done); __ Branch(&arguments_done);
__ bind(&arguments_adaptor); __ bind(&arguments_adaptor);
{ {
// Just get the length from the ArgumentsAdaptorFrame. // Just get the length from the ArgumentsAdaptorFrame.
__ lw(a0, MemOperand(a3, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ lw(t2, MemOperand(t3, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ bind(&arguments_done); __ bind(&arguments_done);
Label stack_empty, stack_done, stack_overflow; Label stack_done, stack_overflow;
__ SmiUntag(a0); __ SmiUntag(t2);
__ Subu(a0, a0, a2); __ Subu(t2, t2, a2);
__ Branch(&stack_empty, le, a0, Operand(zero_reg)); __ Branch(&stack_done, le, t2, Operand(zero_reg));
{ {
// Check for stack overflow. // 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. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ mov(a2, a0); __ Addu(a0, a0, t2);
__ bind(&loop); __ bind(&loop);
{ {
__ Lsa(at, a3, a2, kPointerSizeLog2); __ Lsa(at, t3, t2, kPointerSizeLog2);
__ lw(at, MemOperand(at, 1 * kPointerSize)); __ lw(at, MemOperand(at, 1 * kPointerSize));
__ push(at); __ push(at);
__ Subu(a2, a2, Operand(1)); __ Subu(t2, t2, Operand(1));
__ Branch(&loop, ne, a2, Operand(zero_reg)); __ Branch(&loop, ne, t2, Operand(zero_reg));
} }
} }
} }
__ Branch(&stack_done); __ Branch(&stack_done);
__ bind(&stack_overflow); __ bind(&stack_overflow);
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ bind(&stack_empty);
{
// We just pass the receiver, which is already on the stack.
__ li(a0, Operand(0));
}
__ bind(&stack_done); __ bind(&stack_done);
// Tail-call to the {code} handler.
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
} }

View File

@ -2209,68 +2209,64 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- a1 : the target to call (can be any Object) // -- a0 : the number of arguments (not including the receiver)
// -- a2 : start index (to support rest parameters) // -- a3 : the new.target (for [[Construct]] calls)
// -- ra : return address. // -- a1 : the target to call (can be any Object)
// -- sp[0] : thisArgument // -- a2 : start index (to support rest parameters)
// ----------------------------------- // -----------------------------------
// Check if we have an arguments adaptor frame below the function frame. // Check if we have an arguments adaptor frame below the function frame.
Label arguments_adaptor, arguments_done; Label arguments_adaptor, arguments_done;
__ Ld(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ Ld(a6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ Ld(a0, MemOperand(a3, CommonFrameConstants::kContextOrFrameTypeOffset)); __ Ld(a7, MemOperand(a6, CommonFrameConstants::kContextOrFrameTypeOffset));
__ Branch(&arguments_adaptor, eq, a0, __ Branch(&arguments_adaptor, eq, a7,
Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); Operand(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
{ {
__ Ld(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ Ld(a7, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Ld(a0, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset)); __ Ld(a7, FieldMemOperand(a7, JSFunction::kSharedFunctionInfoOffset));
__ Lw(a0, __ Lw(a7,
FieldMemOperand(a0, SharedFunctionInfo::kFormalParameterCountOffset)); FieldMemOperand(a7, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(a3, fp); __ mov(a6, fp);
} }
__ Branch(&arguments_done); __ Branch(&arguments_done);
__ bind(&arguments_adaptor); __ bind(&arguments_adaptor);
{ {
// Just get the length from the ArgumentsAdaptorFrame. // Just get the length from the ArgumentsAdaptorFrame.
__ Lw(a0, UntagSmiMemOperand( __ Lw(a7, UntagSmiMemOperand(
a3, ArgumentsAdaptorFrameConstants::kLengthOffset)); a6, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ bind(&arguments_done); __ bind(&arguments_done);
Label stack_empty, stack_done, stack_overflow; Label stack_done, stack_overflow;
__ Subu(a0, a0, a2); __ Subu(a7, a7, a2);
__ Branch(&stack_empty, le, a0, Operand(zero_reg)); __ Branch(&stack_done, le, a7, Operand(zero_reg));
{ {
// Check for stack overflow. // 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. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ mov(a2, a0); __ Daddu(a0, a0, a7);
__ bind(&loop); __ bind(&loop);
{ {
__ Dlsa(at, a3, a2, kPointerSizeLog2); __ Dlsa(at, a6, a7, kPointerSizeLog2);
__ Ld(at, MemOperand(at, 1 * kPointerSize)); __ Ld(at, MemOperand(at, 1 * kPointerSize));
__ push(at); __ push(at);
__ Subu(a2, a2, Operand(1)); __ Subu(a7, a7, Operand(1));
__ Branch(&loop, ne, a2, Operand(zero_reg)); __ Branch(&loop, ne, a7, Operand(zero_reg));
} }
} }
} }
__ Branch(&stack_done); __ Branch(&stack_done);
__ bind(&stack_overflow); __ bind(&stack_overflow);
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ bind(&stack_empty);
{
// We just pass the receiver, which is already on the stack.
__ mov(a0, zero_reg);
}
__ bind(&stack_done); __ bind(&stack_done);
// Tail-call to the {code} handler.
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
} }

View File

@ -2416,13 +2416,13 @@ void Builtins::Generate_Apply(MacroAssembler* masm) {
} }
// static // static
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, void Builtins::Generate_ForwardVarargs(MacroAssembler* masm,
Handle<Code> code) { Handle<Code> code) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
// -- rdi : the target to call (can be any Object) // -- rax : the number of arguments (not including the receiver)
// -- rcx : start index (to support rest parameters) // -- rdx : the new target (for [[Construct]] calls)
// -- rsp[0] : return address. // -- rdi : the target to call (can be any Object)
// -- rsp[8] : thisArgument // -- rcx : start index (to support rest parameters)
// ----------------------------------- // -----------------------------------
// Check if we have an arguments adaptor frame below the function frame. // 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))); Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)));
__ j(equal, &arguments_adaptor, Label::kNear); __ j(equal, &arguments_adaptor, Label::kNear);
{ {
__ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ movp(r8, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
__ movp(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset)); __ movp(r8, FieldOperand(r8, JSFunction::kSharedFunctionInfoOffset));
__ LoadSharedFunctionInfoSpecialField( __ LoadSharedFunctionInfoSpecialField(
rax, rax, SharedFunctionInfo::kFormalParameterCountOffset); r8, r8, SharedFunctionInfo::kFormalParameterCountOffset);
__ movp(rbx, rbp); __ movp(rbx, rbp);
} }
__ jmp(&arguments_done, Label::kNear); __ jmp(&arguments_done, Label::kNear);
__ bind(&arguments_adaptor); __ bind(&arguments_adaptor);
{ {
__ SmiToInteger32( __ SmiToInteger32(
rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); r8, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
} }
__ bind(&arguments_done); __ bind(&arguments_done);
Label stack_empty, stack_done, stack_overflow; Label stack_done, stack_overflow;
__ subl(rax, rcx); __ subl(r8, rcx);
__ j(less_equal, &stack_empty); __ j(less_equal, &stack_done);
{ {
// Check for stack overflow. // 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. // Forward the arguments from the caller frame.
{ {
Label loop; Label loop;
__ movl(rcx, rax); __ addl(rax, r8);
__ Pop(r8); __ PopReturnAddressTo(rcx);
__ bind(&loop); __ bind(&loop);
{ {
StackArgumentsAccessor args(rbx, rcx, ARGUMENTS_DONT_CONTAIN_RECEIVER); StackArgumentsAccessor args(rbx, r8, ARGUMENTS_DONT_CONTAIN_RECEIVER);
__ Push(args.GetArgumentOperand(0)); __ Push(args.GetArgumentOperand(0));
__ decl(rcx); __ decl(r8);
__ j(not_zero, &loop); __ j(not_zero, &loop);
} }
__ Push(r8); __ PushReturnAddressFrom(rcx);
} }
} }
__ jmp(&stack_done, Label::kNear); __ jmp(&stack_done, Label::kNear);
__ bind(&stack_overflow); __ bind(&stack_overflow);
__ TailCallRuntime(Runtime::kThrowStackOverflow); __ TailCallRuntime(Runtime::kThrowStackOverflow);
__ bind(&stack_empty);
{
// We just pass the receiver, which is already on the stack.
__ Set(rax, 0);
}
__ bind(&stack_done); __ bind(&stack_done);
// Tail-call to the {code} handler.
__ Jump(code, RelocInfo::CODE_TARGET); __ Jump(code, RelocInfo::CODE_TARGET);
} }

View File

@ -457,6 +457,18 @@ Callable CodeFactory::ConstructFunction(Isolate* isolate) {
ConstructTrampolineDescriptor(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 // static
Callable CodeFactory::InterpreterPushArgsThenCall( Callable CodeFactory::InterpreterPushArgsThenCall(
Isolate* isolate, ConvertReceiverMode receiver_mode, Isolate* isolate, ConvertReceiverMode receiver_mode,

View File

@ -167,6 +167,8 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable Construct(Isolate* isolate); static Callable Construct(Isolate* isolate);
static Callable ConstructWithSpread(Isolate* isolate); static Callable ConstructWithSpread(Isolate* isolate);
static Callable ConstructFunction(Isolate* isolate); static Callable ConstructFunction(Isolate* isolate);
static Callable ConstructForwardVarargs(Isolate* isolate);
static Callable ConstructFunctionForwardVarargs(Isolate* isolate);
static Callable CreateIterResultObject(Isolate* isolate); static Callable CreateIterResultObject(Isolate* isolate);
static Callable HasProperty(Isolate* isolate); static Callable HasProperty(Isolate* isolate);
static Callable ForInFilter(Isolate* isolate); static Callable ForInFilter(Isolate* isolate);

View File

@ -124,7 +124,8 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
Node* arg_array = NodeProperties::GetValueInput(node, 3); Node* arg_array = NodeProperties::GetValueInput(node, 3);
if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : arg_array->use_edges()) { 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. // Ignore uses as frame state's locals or parameters.
if (user->opcode() == IrOpcode::kStateValues) continue; if (user->opcode() == IrOpcode::kStateValues) continue;
// Ignore uses as frame state's accumulator. // Ignore uses as frame state's accumulator.
@ -133,7 +134,6 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
continue; continue;
} }
if (!NodeProperties::IsValueEdge(edge)) continue; if (!NodeProperties::IsValueEdge(edge)) continue;
if (edge.from() == node) continue;
return NoChange(); return NoChange();
} }
// Check if the arguments can be handled in the fast case (i.e. we don't // 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(0); // Function.prototype.apply
node->RemoveInput(2); // arguments node->RemoveInput(2); // arguments
NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs( NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
start_index, p.tail_call_mode())); 2, start_index, p.tail_call_mode()));
return Changed(node); return Changed(node);
} }
// Get to the actual frame state from which to extract the arguments; // 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). // of spread (except for value uses in frame states).
if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : spread->use_edges()) { 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 (!NodeProperties::IsValueEdge(edge)) continue;
if (edge.from() == node) continue;
return NoChange(); return NoChange();
} }
// Get to the actual frame state from which to extract the arguments; // 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 // we can only optimize this in case the {node} was already inlined into
// some other function (and same for the {spread}). // 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); 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); 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); FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
// Need to take the parameters from the arguments adaptor. // Need to take the parameters from the arguments adaptor.
frame_state = outer_state; 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. // Add the actual parameters to the {node}, skipping the receiver.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); 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), node->InsertInput(graph()->zone(), static_cast<int>(++arity),
parameters->InputAt(i)); parameters->InputAt(i));
} }

View File

@ -523,6 +523,28 @@ void JSGenericLowering::LowerJSCreateScriptContext(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kNewScriptContext); 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) { void JSGenericLowering::LowerJSConstruct(Node* node) {
ConstructParameters const& p = ConstructParametersOf(node->op()); ConstructParameters const& p = ConstructParametersOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2); int const arg_count = static_cast<int>(p.arity() - 2);
@ -563,17 +585,20 @@ void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) { void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op()); 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); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
Callable callable = CodeFactory::CallForwardVarargs(isolate());
if (p.tail_call_mode() == TailCallMode::kAllow) { if (p.tail_call_mode() == TailCallMode::kAllow) {
flags |= CallDescriptor::kSupportsTailCalls; flags |= CallDescriptor::kSupportsTailCalls;
} }
CallDescriptor* desc = Linkage::GetStubCallDescriptor( 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_code = jsgraph()->HeapConstant(callable.code());
Node* stub_arity = jsgraph()->Int32Constant(arg_count);
Node* start_index = jsgraph()->Uint32Constant(p.start_index()); Node* start_index = jsgraph()->Uint32Constant(p.start_index());
node->InsertInput(zone(), 0, stub_code); 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)); NodeProperties::ChangeOp(node, common()->Call(desc));
} }

View File

@ -57,6 +57,17 @@ ToBooleanHints ToBooleanHintsOf(Operator const* op) {
return OpParameter<ToBooleanHints>(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, bool operator==(ConstructParameters const& lhs,
ConstructParameters const& rhs) { ConstructParameters const& rhs) {
return lhs.arity() == rhs.arity() && lhs.frequency() == rhs.frequency() && 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, std::ostream& operator<<(std::ostream& os,
CallForwardVarargsParameters const& p) { 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( CallForwardVarargsParameters const& CallForwardVarargsParametersOf(
@ -743,12 +755,12 @@ const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
} }
const Operator* JSOperatorBuilder::CallForwardVarargs( const Operator* JSOperatorBuilder::CallForwardVarargs(
uint32_t start_index, TailCallMode tail_call_mode) { size_t arity, uint32_t start_index, TailCallMode tail_call_mode) {
CallForwardVarargsParameters parameters(start_index, tail_call_mode); CallForwardVarargsParameters parameters(arity, start_index, tail_call_mode);
return new (zone()) Operator1<CallForwardVarargsParameters>( // -- return new (zone()) Operator1<CallForwardVarargsParameters>( // --
IrOpcode::kJSCallForwardVarargs, Operator::kNoProperties, // opcode IrOpcode::kJSCallForwardVarargs, Operator::kNoProperties, // opcode
"JSCallForwardVarargs", // name "JSCallForwardVarargs", // name
2, 1, 1, 1, 1, 2, // counts parameters.arity(), 1, 1, 1, 1, 2, // counts
parameters); // parameter parameters); // parameter
} }
@ -798,6 +810,16 @@ const Operator* JSOperatorBuilder::CallRuntime(const Runtime::Function* f,
parameters); // parameter 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, const Operator* JSOperatorBuilder::Construct(uint32_t arity,
CallFrequency frequency, CallFrequency frequency,
VectorSlotPair const& feedback) { VectorSlotPair const& feedback) {

View File

@ -90,6 +90,40 @@ ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
// The ToBooleanHints are used as parameter by JSToBoolean operators. // The ToBooleanHints are used as parameter by JSToBoolean operators.
ToBooleanHints ToBooleanHintsOf(Operator const* op); 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 // Defines the arity and the feedback for a JavaScript constructor call. This is
// used as a parameter by JSConstruct operators. // used as a parameter by JSConstruct operators.
@ -146,11 +180,13 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const*);
// is used as parameter by JSCallForwardVarargs operators. // is used as parameter by JSCallForwardVarargs operators.
class CallForwardVarargsParameters final { class CallForwardVarargsParameters final {
public: public:
CallForwardVarargsParameters(uint32_t start_index, CallForwardVarargsParameters(size_t arity, uint32_t start_index,
TailCallMode tail_call_mode) TailCallMode tail_call_mode)
: bit_field_(StartIndexField::encode(start_index) | : bit_field_(ArityField::encode(arity) |
StartIndexField::encode(start_index) |
TailCallModeField::encode(tail_call_mode)) {} TailCallModeField::encode(tail_call_mode)) {}
size_t arity() const { return ArityField::decode(bit_field_); }
uint32_t start_index() const { return StartIndexField::decode(bit_field_); } uint32_t start_index() const { return StartIndexField::decode(bit_field_); }
TailCallMode tail_call_mode() const { TailCallMode tail_call_mode() const {
return TailCallModeField::decode(bit_field_); return TailCallModeField::decode(bit_field_);
@ -168,8 +204,9 @@ class CallForwardVarargsParameters final {
return p.bit_field_; return p.bit_field_;
} }
typedef BitField<uint32_t, 0, 30> StartIndexField; typedef BitField<size_t, 0, 15> ArityField;
typedef BitField<TailCallMode, 31, 1> TailCallModeField; typedef BitField<uint32_t, 15, 15> StartIndexField;
typedef BitField<TailCallMode, 30, 1> TailCallModeField;
uint32_t const bit_field_; uint32_t const bit_field_;
}; };
@ -665,7 +702,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CreateLiteralRegExp(Handle<String> constant_pattern, const Operator* CreateLiteralRegExp(Handle<String> constant_pattern,
int literal_flags, int literal_index); 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); TailCallMode tail_call_mode);
const Operator* Call( const Operator* Call(
size_t arity, CallFrequency frequency = CallFrequency(), 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);
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity); const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallRuntime(const Runtime::Function* function, 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, const Operator* Construct(uint32_t arity,
CallFrequency frequency = CallFrequency(), CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair()); VectorSlotPair const& feedback = VectorSlotPair());

View File

@ -1703,6 +1703,38 @@ bool NeedsArgumentAdaptorFrame(Handle<SharedFunctionInfo> shared, int arity) {
} // namespace } // 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) { Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
ConstructParameters const& p = ConstructParametersOf(node->op()); ConstructParameters const& p = ConstructParametersOf(node->op());
@ -1781,6 +1813,9 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) { Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode()); DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode());
CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op()); 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); Node* target = NodeProperties::GetValueInput(node, 0);
Type* target_type = NodeProperties::GetType(target); Type* target_type = NodeProperties::GetType(target);
@ -1796,11 +1831,12 @@ Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate()); Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
node->InsertInput(graph()->zone(), 0, node->InsertInput(graph()->zone(), 0,
jsgraph()->HeapConstant(callable.code())); 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( NodeProperties::ChangeOp(
node, node, common()->Call(Linkage::GetStubCallDescriptor(
common()->Call(Linkage::GetStubCallDescriptor( isolate(), graph()->zone(), callable.descriptor(), arity + 1,
isolate(), graph()->zone(), callable.descriptor(), 1, flags))); flags)));
return Changed(node); return Changed(node);
} }
@ -2176,6 +2212,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSStoreModule(node); return ReduceJSStoreModule(node);
case IrOpcode::kJSConvertReceiver: case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node); return ReduceJSConvertReceiver(node);
case IrOpcode::kJSConstructForwardVarargs:
return ReduceJSConstructForwardVarargs(node);
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
return ReduceJSConstruct(node); return ReduceJSConstruct(node);
case IrOpcode::kJSCallForwardVarargs: case IrOpcode::kJSCallForwardVarargs:

View File

@ -69,6 +69,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSToString(Node* node); Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToObject(Node* node); Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node); Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSConstructForwardVarargs(Node* node);
Reduction ReduceJSConstruct(Node* node); Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSCallForwardVarargs(Node* node); Reduction ReduceJSCallForwardVarargs(Node* node);
Reduction ReduceJSCall(Node* node); Reduction ReduceJSCall(Node* node);

View File

@ -159,6 +159,7 @@
V(JSCreateScriptContext) V(JSCreateScriptContext)
#define JS_OTHER_OP_LIST(V) \ #define JS_OTHER_OP_LIST(V) \
V(JSConstructForwardVarargs) \
V(JSConstruct) \ V(JSConstruct) \
V(JSConstructWithSpread) \ V(JSConstructWithSpread) \
V(JSCallForwardVarargs) \ V(JSCallForwardVarargs) \

View File

@ -96,6 +96,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSToString: case IrOpcode::kJSToString:
// Call operations // Call operations
case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithSpread: case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSCallForwardVarargs: case IrOpcode::kJSCallForwardVarargs:

View File

@ -1335,6 +1335,10 @@ Type* Typer::Visitor::TypeJSCreateScriptContext(Node* node) {
// JS other operators. // JS other operators.
Type* Typer::Visitor::TypeJSConstructForwardVarargs(Node* node) {
return Type::Receiver();
}
Type* Typer::Visitor::TypeJSConstruct(Node* node) { return Type::Receiver(); } Type* Typer::Visitor::TypeJSConstruct(Node* node) { return Type::Receiver(); }
Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) { Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) {

View File

@ -700,6 +700,7 @@ void Verifier::Visitor::Check(Node* node) {
break; break;
} }
case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithSpread: case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver: case IrOpcode::kJSConvertReceiver:

View File

@ -156,9 +156,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// eax : number of arguments
// ecx : start index (to support rest parameters) // ecx : start index (to support rest parameters)
// edi : the target to call // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }

View File

@ -447,8 +447,18 @@ void CallTrampolineDescriptor::InitializePlatformIndependent(
void CallForwardVarargsDescriptor::InitializePlatformIndependent( void CallForwardVarargsDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) { 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 machine_types[] = {MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::Int32(),
MachineType::Int32()}; MachineType::Int32()};
data->InitializePlatformIndependent(arraysize(machine_types), 0, data->InitializePlatformIndependent(arraysize(machine_types), 0,
machine_types); machine_types);

View File

@ -48,6 +48,7 @@ class PlatformInterfaceDescriptor;
V(CallConstruct) \ V(CallConstruct) \
V(CallTrampoline) \ V(CallTrampoline) \
V(ConstructStub) \ V(ConstructStub) \
V(ConstructForwardVarargs) \
V(ConstructTrampoline) \ V(ConstructTrampoline) \
V(TransitionElementsKind) \ V(TransitionElementsKind) \
V(AllocateHeapNumber) \ V(AllocateHeapNumber) \
@ -575,11 +576,18 @@ class CallTrampolineDescriptor : public CallInterfaceDescriptor {
class CallForwardVarargsDescriptor : public CallInterfaceDescriptor { class CallForwardVarargsDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kTarget, kStartIndex) DEFINE_PARAMETERS(kTarget, kActualArgumentsCount, kStartIndex)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(CallForwardVarargsDescriptor, DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(CallForwardVarargsDescriptor,
CallInterfaceDescriptor) CallInterfaceDescriptor)
}; };
class ConstructForwardVarargsDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kTarget, kNewTarget, kActualArgumentsCount, kStartIndex)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(
ConstructForwardVarargsDescriptor, CallInterfaceDescriptor)
};
class ConstructStubDescriptor : public CallInterfaceDescriptor { class ConstructStubDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kFunction, kNewTarget, kActualArgumentsCount, DEFINE_PARAMETERS(kFunction, kNewTarget, kActualArgumentsCount,

View File

@ -156,8 +156,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// a1: the target to call // a1: the target to call
// a0: number of arguments
// a2: start index (to support rest parameters) // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }

View File

@ -156,8 +156,19 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// a1: the target to call // a1: the target to call
// a0: number of arguments
// a2: start index (to support rest parameters) // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }

View File

@ -155,9 +155,20 @@ void CallTrampolineDescriptor::InitializePlatformSpecific(
void CallForwardVarargsDescriptor::InitializePlatformSpecific( void CallForwardVarargsDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
// rax : number of arguments
// rcx : start index (to support rest parameters) // rcx : start index (to support rest parameters)
// rdi : the target to call // 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); data->InitializePlatformSpecific(arraysize(registers), registers);
} }