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
This commit is contained in:
parent
0045327b7d
commit
dc49c566a4
@ -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();
|
||||
if (mode == PRIMARY) {
|
||||
frame_->Enter();
|
||||
// tos: code slot
|
||||
|
||||
// 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();
|
||||
#endif
|
||||
// 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) {
|
||||
// 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.
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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();
|
||||
#endif
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -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<Code> MakeCode(FunctionLiteral* fun,
|
||||
@ -240,7 +249,7 @@ class CodeGenerator: public AstVisitor {
|
||||
inline void VisitStatementsAndSpill(ZoneList<Statement*>* 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);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -228,7 +228,7 @@ Handle<Code> 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<Code>::null();
|
||||
|
@ -55,7 +55,7 @@
|
||||
// CodeGenerator
|
||||
// ~CodeGenerator
|
||||
// ProcessDeferred
|
||||
// GenCode
|
||||
// Generate
|
||||
// ComputeLazyCompile
|
||||
// BuildBoilerplate
|
||||
// ComputeCallInitialize
|
||||
|
@ -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<Code> 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<Code>::null();
|
||||
}
|
||||
|
@ -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();
|
||||
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();
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
} 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;
|
||||
|
||||
// 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();
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -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<Code> MakeCode(FunctionLiteral* fun,
|
||||
@ -379,7 +388,7 @@ class CodeGenerator: public AstVisitor {
|
||||
void VisitStatementsAndSpill(ZoneList<Statement*>* 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
|
||||
|
@ -278,7 +278,9 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> 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<Statement*>* 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();
|
||||
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();
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
} 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;
|
||||
|
||||
// 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();
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -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<Code> MakeCode(FunctionLiteral* fun,
|
||||
@ -381,7 +390,7 @@ class CodeGenerator: public AstVisitor {
|
||||
void VisitStatementsAndSpill(ZoneList<Statement*>* 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
|
||||
|
Loading…
Reference in New Issue
Block a user