From: ager@chromium.org Date: Wed, 3 Feb 2010 07:58:47 +0000 (+0000) Subject: Use the virtual-frame based optimizing compiler for split-compilation X-Git-Tag: upstream/4.7.83~22553 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dc49c566a411b70cc3f9792fcc411859affa0eae;p=platform%2Fupstream%2Fv8.git Use the virtual-frame based optimizing compiler for split-compilation bailouts. For now the virtual-frame state at entry of a function is hard-coded when using the virtual-frame based compiler as the secondary compiler. Setup frame pointer correctly on function entry on ARM in fast-codegen-arm. Review URL: http://codereview.chromium.org/569010 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3776 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 2ab96328e..8854140d0 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -143,7 +143,9 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm, // r1: called JS function // cp: callee's context -void CodeGenerator::GenCode(FunctionLiteral* fun, CompilationInfo* info) { +void CodeGenerator::Generate(FunctionLiteral* fun, + Mode mode, + CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(fun); @@ -169,8 +171,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun, CompilationInfo* info) { // r1: called JS function // cp: callee's context allocator_->Initialize(); - frame_->Enter(); - // tos: code slot + #ifdef DEBUG if (strlen(FLAG_stop_at) > 0 && fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { @@ -179,104 +180,118 @@ void CodeGenerator::GenCode(FunctionLiteral* fun, CompilationInfo* info) { } #endif - // Allocate space for locals and initialize them. This also checks - // for stack overflow. - frame_->AllocateStackSlots(); - // Initialize the function return target after the locals are set - // up, because it needs the expected frame height from the frame. - function_return_.set_direction(JumpTarget::BIDIRECTIONAL); - function_return_is_shadowed_ = false; + if (mode == PRIMARY) { + frame_->Enter(); + // tos: code slot - VirtualFrame::SpilledScope spilled_scope; - int heap_slots = scope_->num_heap_slots(); - if (heap_slots > 0) { - // Allocate local context. - // Get outer context and create a new context based on it. - __ ldr(r0, frame_->Function()); - frame_->EmitPush(r0); - if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub(heap_slots); - frame_->CallStub(&stub, 1); - } else { - frame_->CallRuntime(Runtime::kNewContext, 1); - } + // Allocate space for locals and initialize them. This also checks + // for stack overflow. + frame_->AllocateStackSlots(); + + VirtualFrame::SpilledScope spilled_scope; + int heap_slots = scope_->num_heap_slots(); + if (heap_slots > 0) { + // Allocate local context. + // Get outer context and create a new context based on it. + __ ldr(r0, frame_->Function()); + frame_->EmitPush(r0); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + frame_->CallStub(&stub, 1); + } else { + frame_->CallRuntime(Runtime::kNewContext, 1); + } #ifdef DEBUG - JumpTarget verified_true; - __ cmp(r0, Operand(cp)); - verified_true.Branch(eq); - __ stop("NewContext: r0 is expected to be the same as cp"); - verified_true.Bind(); + JumpTarget verified_true; + __ cmp(r0, Operand(cp)); + verified_true.Branch(eq); + __ stop("NewContext: r0 is expected to be the same as cp"); + verified_true.Bind(); #endif - // Update context local. - __ str(cp, frame_->Context()); - } + // Update context local. + __ str(cp, frame_->Context()); + } - // TODO(1241774): Improve this code: - // 1) only needed if we have a context - // 2) no need to recompute context ptr every single time - // 3) don't copy parameter operand code from SlotOperand! - { - Comment cmnt2(masm_, "[ copy context parameters into .context"); - - // Note that iteration order is relevant here! If we have the same - // parameter twice (e.g., function (x, y, x)), and that parameter - // needs to be copied into the context, it must be the last argument - // passed to the parameter that needs to be copied. This is a rare - // case so we don't check for it, instead we rely on the copying - // order: such a parameter is copied repeatedly into the same - // context location and thus the last value is what is seen inside - // the function. - for (int i = 0; i < scope_->num_parameters(); i++) { - Variable* par = scope_->parameter(i); - Slot* slot = par->slot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { - ASSERT(!scope_->is_global_scope()); // no parameters in global scope - __ ldr(r1, frame_->ParameterAt(i)); - // Loads r2 with context; used below in RecordWrite. - __ str(r1, SlotOperand(slot, r2)); - // Load the offset into r3. - int slot_offset = - FixedArray::kHeaderSize + slot->index() * kPointerSize; - __ mov(r3, Operand(slot_offset)); - __ RecordWrite(r2, r3, r1); + // TODO(1241774): Improve this code: + // 1) only needed if we have a context + // 2) no need to recompute context ptr every single time + // 3) don't copy parameter operand code from SlotOperand! + { + Comment cmnt2(masm_, "[ copy context parameters into .context"); + + // Note that iteration order is relevant here! If we have the same + // parameter twice (e.g., function (x, y, x)), and that parameter + // needs to be copied into the context, it must be the last argument + // passed to the parameter that needs to be copied. This is a rare + // case so we don't check for it, instead we rely on the copying + // order: such a parameter is copied repeatedly into the same + // context location and thus the last value is what is seen inside + // the function. + for (int i = 0; i < scope_->num_parameters(); i++) { + Variable* par = scope_->parameter(i); + Slot* slot = par->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + // No parameters in global scope. + ASSERT(!scope_->is_global_scope()); + __ ldr(r1, frame_->ParameterAt(i)); + // Loads r2 with context; used below in RecordWrite. + __ str(r1, SlotOperand(slot, r2)); + // Load the offset into r3. + int slot_offset = + FixedArray::kHeaderSize + slot->index() * kPointerSize; + __ mov(r3, Operand(slot_offset)); + __ RecordWrite(r2, r3, r1); + } } } - } - // Store the arguments object. This must happen after context - // initialization because the arguments object may be stored in the - // context. - if (scope_->arguments() != NULL) { - Comment cmnt(masm_, "[ allocate arguments object"); - ASSERT(scope_->arguments_shadow() != NULL); - Variable* arguments = scope_->arguments()->var(); - Variable* shadow = scope_->arguments_shadow()->var(); - ASSERT(arguments != NULL && arguments->slot() != NULL); - ASSERT(shadow != NULL && shadow->slot() != NULL); - ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); - __ ldr(r2, frame_->Function()); - // The receiver is below the arguments, the return address, and the - // frame pointer on the stack. - const int kReceiverDisplacement = 2 + scope_->num_parameters(); - __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize)); - __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters()))); - frame_->Adjust(3); - __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit()); - frame_->CallStub(&stub, 3); - frame_->EmitPush(r0); - StoreToSlot(arguments->slot(), NOT_CONST_INIT); - StoreToSlot(shadow->slot(), NOT_CONST_INIT); - frame_->Drop(); // Value is no longer needed. - } + // Store the arguments object. This must happen after context + // initialization because the arguments object may be stored in the + // context. + if (scope_->arguments() != NULL) { + Comment cmnt(masm_, "[ allocate arguments object"); + ASSERT(scope_->arguments_shadow() != NULL); + Variable* arguments = scope_->arguments()->var(); + Variable* shadow = scope_->arguments_shadow()->var(); + ASSERT(arguments != NULL && arguments->slot() != NULL); + ASSERT(shadow != NULL && shadow->slot() != NULL); + ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); + __ ldr(r2, frame_->Function()); + // The receiver is below the arguments, the return address, and the + // frame pointer on the stack. + const int kReceiverDisplacement = 2 + scope_->num_parameters(); + __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize)); + __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters()))); + frame_->Adjust(3); + __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit()); + frame_->CallStub(&stub, 3); + frame_->EmitPush(r0); + StoreToSlot(arguments->slot(), NOT_CONST_INIT); + StoreToSlot(shadow->slot(), NOT_CONST_INIT); + frame_->Drop(); // Value is no longer needed. + } - // Initialize ThisFunction reference if present. - if (scope_->is_function_scope() && scope_->function() != NULL) { - __ mov(ip, Operand(Factory::the_hole_value())); - frame_->EmitPush(ip); - StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + // Initialize ThisFunction reference if present. + if (scope_->is_function_scope() && scope_->function() != NULL) { + __ mov(ip, Operand(Factory::the_hole_value())); + frame_->EmitPush(ip); + StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + } + } else { + // When used as the secondary compiler for splitting, r1, cp, + // fp, and lr have been pushed on the stack. Adjust the virtual + // frame to match this state. + frame_->Adjust(4); + allocator_->Unuse(r1); + allocator_->Unuse(lr); } + // Initialize the function return target after the locals are set + // up, because it needs the expected frame height from the frame. + function_return_.set_direction(JumpTarget::BIDIRECTIONAL); + function_return_is_shadowed_ = false; + // Generate code to 'execute' declarations and initialize functions // (source elements). In case of an illegal redeclaration we need to // handle that instead of processing the declarations. diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 4f56c84fc..0384485f1 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -150,6 +150,15 @@ class CodeGenState BASE_EMBEDDED { class CodeGenerator: public AstVisitor { public: + // Compilation mode. Either the compiler is used as the primary + // compiler and needs to setup everything or the compiler is used as + // the secondary compiler for split compilation and has to handle + // bailouts. + enum Mode { + PRIMARY, + SECONDARY + }; + // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle MakeCode(FunctionLiteral* fun, @@ -240,7 +249,7 @@ class CodeGenerator: public AstVisitor { inline void VisitStatementsAndSpill(ZoneList* statements); // Main code generation function - void GenCode(FunctionLiteral* fun, CompilationInfo* info); + void Generate(FunctionLiteral* fun, Mode mode, CompilationInfo* info); // The following are used by class Reference. void LoadReference(Reference* ref); diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 62b5f6ffc..1aeea7ab6 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -111,6 +111,7 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun, CompilationInfo* info) { // Save the caller's frame pointer and set up our own. Comment prologue_cmnt(masm(), ";; Prologue"); __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); + __ add(fp, sp, Operand(2 * kPointerSize)); // Note that we keep a live register reference to cp (context) at // this point. diff --git a/src/codegen.cc b/src/codegen.cc index 979b0aa65..1db07e48a 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -228,7 +228,7 @@ Handle CodeGenerator::MakeCode(FunctionLiteral* fun, MacroAssembler masm(NULL, kInitialBufferSize); CodeGenerator cgen(&masm, script, is_eval); CodeGeneratorScope scope(&cgen); - cgen.GenCode(fun, info); + cgen.Generate(fun, PRIMARY, info); if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle::null(); diff --git a/src/codegen.h b/src/codegen.h index 48566b202..3f4256a07 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -55,7 +55,7 @@ // CodeGenerator // ~CodeGenerator // ProcessDeferred -// GenCode +// Generate // ComputeLazyCompile // BuildBoilerplate // ComputeCallInitialize diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc index 1ead93c3f..4e6f259c6 100644 --- a/src/fast-codegen.cc +++ b/src/fast-codegen.cc @@ -30,7 +30,6 @@ #include "codegen-inl.h" #include "data-flow.h" #include "fast-codegen.h" -#include "full-codegen.h" #include "scopes.h" namespace v8 { @@ -358,9 +357,10 @@ Handle FastCodeGenerator::MakeCode(FunctionLiteral* fun, // Generate the full code for the function in bailout mode, using the same // macro assembler. - FullCodeGenerator full_cgen(&masm, script, is_eval); - full_cgen.Generate(fun, FullCodeGenerator::SECONDARY); - if (full_cgen.HasStackOverflow()) { + CodeGenerator cgen(&masm, script, is_eval); + CodeGeneratorScope scope(&cgen); + cgen.Generate(fun, CodeGenerator::SECONDARY, info); + if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle::null(); } diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 1b4f098a1..815427ad4 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -126,7 +126,9 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm, // edi: called JS function // esi: callee's context -void CodeGenerator::GenCode(FunctionLiteral* fun, CompilationInfo* info) { +void CodeGenerator::Generate(FunctionLiteral* fun, + Mode mode, + CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(fun); @@ -167,96 +169,106 @@ void CodeGenerator::GenCode(FunctionLiteral* fun, CompilationInfo* info) { // edi: called JS function // esi: callee's context allocator_->Initialize(); - frame_->Enter(); - // Allocate space for locals and initialize them. - frame_->AllocateStackSlots(); - // Initialize the function return target after the locals are set - // up, because it needs the expected frame height from the frame. - function_return_.set_direction(JumpTarget::BIDIRECTIONAL); - function_return_is_shadowed_ = false; - - // Allocate the local context if needed. - int heap_slots = scope_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; - if (heap_slots > 0) { - Comment cmnt(masm_, "[ allocate local context"); - // Allocate local context. - // Get outer context and create a new context based on it. - frame_->PushFunction(); - Result context; - if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub(heap_slots); - context = frame_->CallStub(&stub, 1); - } else { - context = frame_->CallRuntime(Runtime::kNewContext, 1); - } + if (mode == PRIMARY) { + frame_->Enter(); + + // Allocate space for locals and initialize them. + frame_->AllocateStackSlots(); + + // Allocate the local context if needed. + int heap_slots = scope_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; + if (heap_slots > 0) { + Comment cmnt(masm_, "[ allocate local context"); + // Allocate local context. + // Get outer context and create a new context based on it. + frame_->PushFunction(); + Result context; + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + context = frame_->CallStub(&stub, 1); + } else { + context = frame_->CallRuntime(Runtime::kNewContext, 1); + } - // Update context local. - frame_->SaveContextRegister(); + // Update context local. + frame_->SaveContextRegister(); - // Verify that the runtime call result and esi agree. - if (FLAG_debug_code) { - __ cmp(context.reg(), Operand(esi)); - __ Assert(equal, "Runtime::NewContext should end up in esi"); + // Verify that the runtime call result and esi agree. + if (FLAG_debug_code) { + __ cmp(context.reg(), Operand(esi)); + __ Assert(equal, "Runtime::NewContext should end up in esi"); + } } - } - // TODO(1241774): Improve this code: - // 1) only needed if we have a context - // 2) no need to recompute context ptr every single time - // 3) don't copy parameter operand code from SlotOperand! - { - Comment cmnt2(masm_, "[ copy context parameters into .context"); - - // Note that iteration order is relevant here! If we have the same - // parameter twice (e.g., function (x, y, x)), and that parameter - // needs to be copied into the context, it must be the last argument - // passed to the parameter that needs to be copied. This is a rare - // case so we don't check for it, instead we rely on the copying - // order: such a parameter is copied repeatedly into the same - // context location and thus the last value is what is seen inside - // the function. - for (int i = 0; i < scope_->num_parameters(); i++) { - Variable* par = scope_->parameter(i); - Slot* slot = par->slot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { - // The use of SlotOperand below is safe in unspilled code - // because the slot is guaranteed to be a context slot. - // - // There are no parameters in the global scope. - ASSERT(!scope_->is_global_scope()); - frame_->PushParameterAt(i); - Result value = frame_->Pop(); - value.ToRegister(); - - // SlotOperand loads context.reg() with the context object - // stored to, used below in RecordWrite. - Result context = allocator_->Allocate(); - ASSERT(context.is_valid()); - __ mov(SlotOperand(slot, context.reg()), value.reg()); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - Result scratch = allocator_->Allocate(); - ASSERT(scratch.is_valid()); - frame_->Spill(context.reg()); - frame_->Spill(value.reg()); - __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg()); + // TODO(1241774): Improve this code: + // 1) only needed if we have a context + // 2) no need to recompute context ptr every single time + // 3) don't copy parameter operand code from SlotOperand! + { + Comment cmnt2(masm_, "[ copy context parameters into .context"); + + // Note that iteration order is relevant here! If we have the same + // parameter twice (e.g., function (x, y, x)), and that parameter + // needs to be copied into the context, it must be the last argument + // passed to the parameter that needs to be copied. This is a rare + // case so we don't check for it, instead we rely on the copying + // order: such a parameter is copied repeatedly into the same + // context location and thus the last value is what is seen inside + // the function. + for (int i = 0; i < scope_->num_parameters(); i++) { + Variable* par = scope_->parameter(i); + Slot* slot = par->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + // The use of SlotOperand below is safe in unspilled code + // because the slot is guaranteed to be a context slot. + // + // There are no parameters in the global scope. + ASSERT(!scope_->is_global_scope()); + frame_->PushParameterAt(i); + Result value = frame_->Pop(); + value.ToRegister(); + + // SlotOperand loads context.reg() with the context object + // stored to, used below in RecordWrite. + Result context = allocator_->Allocate(); + ASSERT(context.is_valid()); + __ mov(SlotOperand(slot, context.reg()), value.reg()); + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + Result scratch = allocator_->Allocate(); + ASSERT(scratch.is_valid()); + frame_->Spill(context.reg()); + frame_->Spill(value.reg()); + __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg()); + } } } - } - // Store the arguments object. This must happen after context - // initialization because the arguments object may be stored in - // the context. - if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) { - StoreArgumentsObject(true); - } + // Store the arguments object. This must happen after context + // initialization because the arguments object may be stored in + // the context. + if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) { + StoreArgumentsObject(true); + } - // Initialize ThisFunction reference if present. - if (scope_->is_function_scope() && scope_->function() != NULL) { - frame_->Push(Factory::the_hole_value()); - StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + // Initialize ThisFunction reference if present. + if (scope_->is_function_scope() && scope_->function() != NULL) { + frame_->Push(Factory::the_hole_value()); + StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + } + } else { + // When used as the secondary compiler for splitting, ebp, esi, + // and edi have been pushed on the stack. Adjust the virtual + // frame to match this state. + frame_->Adjust(3); + allocator_->Unuse(edi); } + // Initialize the function return target after the locals are set + // up, because it needs the expected frame height from the frame. + function_return_.set_direction(JumpTarget::BIDIRECTIONAL); + function_return_is_shadowed_ = false; + // Generate code to 'execute' declarations and initialize functions // (source elements). In case of an illegal redeclaration we need to // handle that instead of processing the declarations. diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index e036a1d64..956f42433 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -294,6 +294,15 @@ enum ArgumentsAllocationMode { class CodeGenerator: public AstVisitor { public: + // Compilation mode. Either the compiler is used as the primary + // compiler and needs to setup everything or the compiler is used as + // the secondary compiler for split compilation and has to handle + // bailouts. + enum Mode { + PRIMARY, + SECONDARY + }; + // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle MakeCode(FunctionLiteral* fun, @@ -379,7 +388,7 @@ class CodeGenerator: public AstVisitor { void VisitStatementsAndSpill(ZoneList* statements); // Main code generation function - void GenCode(FunctionLiteral* fun, CompilationInfo* info); + void Generate(FunctionLiteral* fun, Mode mode, CompilationInfo* info); // Generate the return sequence code. Should be called no more than // once per compiled function, immediately after binding the return diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 0fb0ce2da..6094f22e4 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -278,7 +278,9 @@ void CodeGenerator::DeclareGlobals(Handle pairs) { } -void CodeGenerator::GenCode(FunctionLiteral* function, CompilationInfo* info) { +void CodeGenerator::Generate(FunctionLiteral* function, + Mode mode, + CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(function); ZoneList* body = function->body(); @@ -318,96 +320,106 @@ void CodeGenerator::GenCode(FunctionLiteral* function, CompilationInfo* info) { // rdi: called JS function // rsi: callee's context allocator_->Initialize(); - frame_->Enter(); - // Allocate space for locals and initialize them. - frame_->AllocateStackSlots(); - // Initialize the function return target after the locals are set - // up, because it needs the expected frame height from the frame. - function_return_.set_direction(JumpTarget::BIDIRECTIONAL); - function_return_is_shadowed_ = false; - - // Allocate the local context if needed. - int heap_slots = scope_->num_heap_slots(); - if (heap_slots > 0) { - Comment cmnt(masm_, "[ allocate local context"); - // Allocate local context. - // Get outer context and create a new context based on it. - frame_->PushFunction(); - Result context; - if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub(heap_slots); - context = frame_->CallStub(&stub, 1); - } else { - context = frame_->CallRuntime(Runtime::kNewContext, 1); - } + if (mode == PRIMARY) { + frame_->Enter(); + + // Allocate space for locals and initialize them. + frame_->AllocateStackSlots(); + + // Allocate the local context if needed. + int heap_slots = scope_->num_heap_slots(); + if (heap_slots > 0) { + Comment cmnt(masm_, "[ allocate local context"); + // Allocate local context. + // Get outer context and create a new context based on it. + frame_->PushFunction(); + Result context; + if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub(heap_slots); + context = frame_->CallStub(&stub, 1); + } else { + context = frame_->CallRuntime(Runtime::kNewContext, 1); + } - // Update context local. - frame_->SaveContextRegister(); + // Update context local. + frame_->SaveContextRegister(); - // Verify that the runtime call result and rsi agree. - if (FLAG_debug_code) { - __ cmpq(context.reg(), rsi); - __ Assert(equal, "Runtime::NewContext should end up in rsi"); + // Verify that the runtime call result and rsi agree. + if (FLAG_debug_code) { + __ cmpq(context.reg(), rsi); + __ Assert(equal, "Runtime::NewContext should end up in rsi"); + } } - } - // TODO(1241774): Improve this code: - // 1) only needed if we have a context - // 2) no need to recompute context ptr every single time - // 3) don't copy parameter operand code from SlotOperand! - { - Comment cmnt2(masm_, "[ copy context parameters into .context"); - - // Note that iteration order is relevant here! If we have the same - // parameter twice (e.g., function (x, y, x)), and that parameter - // needs to be copied into the context, it must be the last argument - // passed to the parameter that needs to be copied. This is a rare - // case so we don't check for it, instead we rely on the copying - // order: such a parameter is copied repeatedly into the same - // context location and thus the last value is what is seen inside - // the function. - for (int i = 0; i < scope_->num_parameters(); i++) { - Variable* par = scope_->parameter(i); - Slot* slot = par->slot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { - // The use of SlotOperand below is safe in unspilled code - // because the slot is guaranteed to be a context slot. - // - // There are no parameters in the global scope. - ASSERT(!scope_->is_global_scope()); - frame_->PushParameterAt(i); - Result value = frame_->Pop(); - value.ToRegister(); - - // SlotOperand loads context.reg() with the context object - // stored to, used below in RecordWrite. - Result context = allocator_->Allocate(); - ASSERT(context.is_valid()); - __ movq(SlotOperand(slot, context.reg()), value.reg()); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - Result scratch = allocator_->Allocate(); - ASSERT(scratch.is_valid()); - frame_->Spill(context.reg()); - frame_->Spill(value.reg()); - __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg()); + // TODO(1241774): Improve this code: + // 1) only needed if we have a context + // 2) no need to recompute context ptr every single time + // 3) don't copy parameter operand code from SlotOperand! + { + Comment cmnt2(masm_, "[ copy context parameters into .context"); + + // Note that iteration order is relevant here! If we have the same + // parameter twice (e.g., function (x, y, x)), and that parameter + // needs to be copied into the context, it must be the last argument + // passed to the parameter that needs to be copied. This is a rare + // case so we don't check for it, instead we rely on the copying + // order: such a parameter is copied repeatedly into the same + // context location and thus the last value is what is seen inside + // the function. + for (int i = 0; i < scope_->num_parameters(); i++) { + Variable* par = scope_->parameter(i); + Slot* slot = par->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + // The use of SlotOperand below is safe in unspilled code + // because the slot is guaranteed to be a context slot. + // + // There are no parameters in the global scope. + ASSERT(!scope_->is_global_scope()); + frame_->PushParameterAt(i); + Result value = frame_->Pop(); + value.ToRegister(); + + // SlotOperand loads context.reg() with the context object + // stored to, used below in RecordWrite. + Result context = allocator_->Allocate(); + ASSERT(context.is_valid()); + __ movq(SlotOperand(slot, context.reg()), value.reg()); + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + Result scratch = allocator_->Allocate(); + ASSERT(scratch.is_valid()); + frame_->Spill(context.reg()); + frame_->Spill(value.reg()); + __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg()); + } } } - } - // Store the arguments object. This must happen after context - // initialization because the arguments object may be stored in - // the context. - if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) { - StoreArgumentsObject(true); - } + // Store the arguments object. This must happen after context + // initialization because the arguments object may be stored in + // the context. + if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) { + StoreArgumentsObject(true); + } - // Initialize ThisFunction reference if present. - if (scope_->is_function_scope() && scope_->function() != NULL) { - frame_->Push(Factory::the_hole_value()); - StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + // Initialize ThisFunction reference if present. + if (scope_->is_function_scope() && scope_->function() != NULL) { + frame_->Push(Factory::the_hole_value()); + StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT); + } + } else { + // When used as the secondary compiler for splitting, rbp, rsi, + // and rdi have been pushed on the stack. Adjust the virtual + // frame to match this state. + frame_->Adjust(3); + allocator_->Unuse(rdi); } + // Initialize the function return target after the locals are set + // up, because it needs the expected frame height from the frame. + function_return_.set_direction(JumpTarget::BIDIRECTIONAL); + function_return_is_shadowed_ = false; + // Generate code to 'execute' declarations and initialize functions // (source elements). In case of an illegal redeclaration we need to // handle that instead of processing the declarations. diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index c3c0fb53e..a758e739b 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -294,6 +294,15 @@ enum ArgumentsAllocationMode { class CodeGenerator: public AstVisitor { public: + // Compilation mode. Either the compiler is used as the primary + // compiler and needs to setup everything or the compiler is used as + // the secondary compiler for split compilation and has to handle + // bailouts. + enum Mode { + PRIMARY, + SECONDARY + }; + // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle MakeCode(FunctionLiteral* fun, @@ -381,7 +390,7 @@ class CodeGenerator: public AstVisitor { void VisitStatementsAndSpill(ZoneList* statements); // Main code generation function - void GenCode(FunctionLiteral* fun, CompilationInfo* info); + void Generate(FunctionLiteral* fun, Mode mode, CompilationInfo* info); // Generate the return sequence code. Should be called no more than // once per compiled function, immediately after binding the return