Commit 5ced12c1 authored by chunyang.dai's avatar chunyang.dai Committed by Commit bot

X87: [builtins] Add support for NewTarget to Execution::New.

port 1dfac69f (r30857).

original commit message:

    Introduce new builtins Construct and ConstructFunction (in line
    with the Call and CallFunction builtins that we already have) as
    proper bottleneck for Construct and [[Construct]] on JSFunctions.
    Use these builtins to support passing NewTarget from C++ to
    JavaScript land.

    Long-term we want the CallConstructStub to be used for
    gathering feedback on entry to construction chain (i.e. the
    initial new Foo), and use the Construct builtins to do the
    actual work inside the construction chain (i.e. calling into
    super and stuff).

BUG=

Review URL: https://codereview.chromium.org/1362573002

Cr-Commit-Position: refs/heads/master@{#30899}
parent b785daa7
...@@ -3907,9 +3907,6 @@ void FullCodeGenerator::EmitDefaultConstructorCallSuper(CallRuntime* expr) { ...@@ -3907,9 +3907,6 @@ void FullCodeGenerator::EmitDefaultConstructorCallSuper(CallRuntime* expr) {
VisitForStackValue(args->at(0)); VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1)); VisitForStackValue(args->at(1));
// Load original constructor into ecx.
__ mov(ecx, Operand(esp, 1 * kPointerSize));
// Check if the calling frame is an arguments adaptor frame. // Check if the calling frame is an arguments adaptor frame.
Label adaptor_frame, args_set_up, runtime; Label adaptor_frame, args_set_up, runtime;
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
...@@ -3939,10 +3936,9 @@ void FullCodeGenerator::EmitDefaultConstructorCallSuper(CallRuntime* expr) { ...@@ -3939,10 +3936,9 @@ void FullCodeGenerator::EmitDefaultConstructorCallSuper(CallRuntime* expr) {
__ bind(&args_set_up); __ bind(&args_set_up);
__ mov(edi, Operand(esp, eax, times_pointer_size, 0)); __ mov(edx, Operand(esp, eax, times_pointer_size, 1 * kPointerSize));
__ mov(ebx, Immediate(isolate()->factory()->undefined_value())); __ mov(edi, Operand(esp, eax, times_pointer_size, 0 * kPointerSize));
CallConstructStub stub(isolate(), SUPER_CONSTRUCTOR_CALL); __ Call(isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL);
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
// Restore context register. // Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
......
...@@ -24,13 +24,20 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, ...@@ -24,13 +24,20 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm,
// -- eax : number of arguments excluding receiver // -- eax : number of arguments excluding receiver
// -- edi : called function (only guaranteed when // -- edi : called function (only guaranteed when
// extra_args requires it) // extra_args requires it)
// -- esi : context
// -- esp[0] : return address // -- esp[0] : return address
// -- esp[4] : last argument // -- esp[4] : last argument
// -- ... // -- ...
// -- esp[4 * argc] : first argument (argc == eax) // -- esp[4 * argc] : first argument (argc == eax)
// -- esp[4 * (argc +1)] : receiver // -- esp[4 * (argc +1)] : receiver
// ----------------------------------- // -----------------------------------
__ AssertFunction(edi);
// Make sure we operate in the context of the called function (for example
// ConstructStubs implemented in C++ will be run in the context of the caller
// instead of the callee, due to the way that [[Construct]] is defined for
// ordinary functions).
// TODO(bmeurer): Can we make this more robust?
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
// Insert extra arguments. // Insert extra arguments.
int num_extra_args = 0; int num_extra_args = 0;
...@@ -488,15 +495,16 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, ...@@ -488,15 +495,16 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
{ {
FrameScope scope(masm, StackFrame::INTERNAL); FrameScope scope(masm, StackFrame::INTERNAL);
// Setup the context (we need to use the caller context from the isolate).
ExternalReference context_address(Isolate::kContextAddress,
masm->isolate());
__ mov(esi, Operand::StaticVariable(context_address));
// Load the previous frame pointer (ebx) to access C arguments // Load the previous frame pointer (ebx) to access C arguments
__ mov(ebx, Operand(ebp, 0)); __ mov(ebx, Operand(ebp, 0));
// Get the function from the frame and setup the context.
__ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
__ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
// Push the function and the receiver onto the stack. // Push the function and the receiver onto the stack.
__ push(ecx); __ push(Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
__ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
// Load the number of arguments and setup pointer to the arguments. // Load the number of arguments and setup pointer to the arguments.
...@@ -514,7 +522,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, ...@@ -514,7 +522,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
// Copy arguments to the stack in a loop. // Copy arguments to the stack in a loop.
Label loop, entry; Label loop, entry;
__ Move(ecx, Immediate(0)); __ Move(ecx, Immediate(0));
__ jmp(&entry); __ jmp(&entry, Label::kNear);
__ bind(&loop); __ bind(&loop);
__ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
__ push(Operand(edx, 0)); // dereference handle __ push(Operand(edx, 0)); // dereference handle
...@@ -523,19 +531,18 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, ...@@ -523,19 +531,18 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
__ cmp(ecx, eax); __ cmp(ecx, eax);
__ j(not_equal, &loop); __ j(not_equal, &loop);
// Get the function from the stack and call it. // Load the previous frame pointer (ebx) to access C arguments
// kPointerSize for the receiver. __ mov(ebx, Operand(ebp, 0));
__ mov(edi, Operand(esp, eax, times_4, kPointerSize));
// Get the new.target and function from the frame.
__ mov(edx, Operand(ebx, EntryFrameConstants::kNewTargetArgOffset));
__ mov(edi, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
// Invoke the code. // Invoke the code.
if (is_construct) { Handle<Code> builtin = is_construct
// No type feedback cell is available ? masm->isolate()->builtins()->Construct()
__ mov(ebx, masm->isolate()->factory()->undefined_value()); : masm->isolate()->builtins()->Call();
CallConstructStub stub(masm->isolate(), NO_CALL_CONSTRUCTOR_FLAGS); __ Call(builtin, RelocInfo::CODE_TARGET);
__ CallStub(&stub);
} else {
__ Call(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
// Exit the internal frame. Notice that this also removes the empty. // Exit the internal frame. Notice that this also removes the empty.
// context and the function left on the stack by the code // context and the function left on the stack by the code
...@@ -1552,6 +1559,68 @@ void Builtins::Generate_Call(MacroAssembler* masm) { ...@@ -1552,6 +1559,68 @@ void Builtins::Generate_Call(MacroAssembler* masm) {
} }
// static
void Builtins::Generate_ConstructFunction(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : the original constructor (checked to be a JSFunction)
// -- edi : the constructor to call (checked to be a JSFunction)
// -----------------------------------
__ AssertFunction(edx);
__ AssertFunction(edi);
// Calling convention for function specific ConstructStubs require
// ebx to contain either an AllocationSite or undefined.
__ LoadRoot(ebx, Heap::kUndefinedValueRootIndex);
// Tail call to the function-specific construct stub (still in the caller
// context at this point).
__ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset));
__ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
__ jmp(ecx);
}
// static
void Builtins::Generate_Construct(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : the number of arguments (not including the receiver)
// -- edx : the original constructor (either the same as the constructor or
// the JSFunction on which new was invoked initially)
// -- edi : the constructor to call (can be any Object)
// -----------------------------------
Label slow;
__ JumpIfSmi(edi, &slow, Label::kNear);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(equal, masm->isolate()->builtins()->ConstructFunction(),
RelocInfo::CODE_TARGET);
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
__ j(not_equal, &slow, Label::kNear);
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset));
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ bind(&slow);
{
// Determine the delegate for the target (if any).
FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(eax);
__ Push(eax);
__ Push(edi);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ mov(edi, eax);
__ Pop(eax);
__ SmiUntag(eax);
}
// The delegate is always a regular function.
__ AssertFunction(edi);
__ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
}
// static // static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
// ----------- S t a t e ------------- // ----------- S t a t e -------------
......
...@@ -1819,17 +1819,17 @@ void CallConstructStub::Generate(MacroAssembler* masm) { ...@@ -1819,17 +1819,17 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
// ecx : original constructor (for IsSuperConstructorCall) // ecx : original constructor (for IsSuperConstructorCall)
// edx : slot in feedback vector (Smi, for RecordCallTarget) // edx : slot in feedback vector (Smi, for RecordCallTarget)
// edi : constructor function // edi : constructor function
Label slow, non_function_call;
if (IsSuperConstructorCall()) { if (IsSuperConstructorCall()) {
__ push(ecx); __ push(ecx);
} }
Label non_function;
// Check that function is not a smi. // Check that function is not a smi.
__ JumpIfSmi(edi, &non_function_call); __ JumpIfSmi(edi, &non_function);
// Check that function is a JSFunction. // Check that function is a JSFunction.
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(not_equal, &slow); __ j(not_equal, &non_function);
if (RecordCallTarget()) { if (RecordCallTarget()) {
GenerateRecordCallTarget(masm, IsSuperConstructorCall()); GenerateRecordCallTarget(masm, IsSuperConstructorCall());
...@@ -1855,44 +1855,17 @@ void CallConstructStub::Generate(MacroAssembler* masm) { ...@@ -1855,44 +1855,17 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
__ mov(edx, edi); __ mov(edx, edi);
} }
// Jump to the function-specific construct stub. // Tail call to the function-specific construct stub (still in the caller
Register jmp_reg = ecx; // context at this point).
__ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(jmp_reg, FieldOperand(jmp_reg, __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset));
SharedFunctionInfo::kConstructStubOffset)); __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
__ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize)); __ jmp(ecx);
__ jmp(jmp_reg);
__ bind(&non_function);
// edi: called object if (IsSuperConstructorCall()) __ Drop(1);
// eax: number of arguments __ mov(edx, edi);
// ecx: object map __ Jump(isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET);
// esp[0]: original receiver (for IsSuperConstructorCall)
__ bind(&slow);
{
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
__ j(not_equal, &non_function_call, Label::kNear);
if (IsSuperConstructorCall()) __ Drop(1);
// TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies.
__ mov(edi, FieldOperand(edi, JSFunctionProxy::kConstructTrapOffset));
__ Jump(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
__ bind(&non_function_call);
if (IsSuperConstructorCall()) __ Drop(1);
{
// Determine the delegate for the target (if any).
FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(eax);
__ Push(eax);
__ Push(edi);
__ CallRuntime(Runtime::kGetConstructorDelegate, 1);
__ mov(edi, eax);
__ Pop(eax);
__ SmiUntag(eax);
}
// The delegate is always a regular function.
__ AssertFunction(edi);
__ Jump(isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET);
}
} }
......
...@@ -39,6 +39,7 @@ class EntryFrameConstants : public AllStatic { ...@@ -39,6 +39,7 @@ class EntryFrameConstants : public AllStatic {
public: public:
static const int kCallerFPOffset = -6 * kPointerSize; static const int kCallerFPOffset = -6 * kPointerSize;
static const int kNewTargetArgOffset = +2 * kPointerSize;
static const int kFunctionArgOffset = +3 * kPointerSize; static const int kFunctionArgOffset = +3 * kPointerSize;
static const int kReceiverArgOffset = +4 * kPointerSize; static const int kReceiverArgOffset = +4 * kPointerSize;
static const int kArgcOffset = +5 * kPointerSize; static const int kArgcOffset = +5 * kPointerSize;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment