// r1: called JS function
// cp: callee's context
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
}
#endif
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// tos: code slot
frame_->Adjust(4);
allocator_->Unuse(r1);
allocator_->Unuse(lr);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
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(CompilationInfo* info);
inline void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// The following are used by class Reference.
void LoadReference(Reference* ref);
if (!destination().is(no_reg)) {
__ orr(destination(), accumulator1(), Operand(accumulator0()));
}
- } else if (destination().is(no_reg)) {
- // Result is not needed but do not clobber the operands in case of
- // bailout.
- __ orr(scratch0(), accumulator1(), Operand(accumulator0()));
- __ BranchOnNotSmi(scratch0(), bailout());
} else {
- // Preserve the destination operand in a scratch register in case of
- // bailout.
- __ mov(scratch0(), destination());
- __ orr(destination(), accumulator1(), Operand(accumulator0()));
- __ BranchOnNotSmi(destination(), bailout());
+ // Left is in accumulator1, right in accumulator0.
+ if (destination().is(accumulator0())) {
+ __ mov(scratch0(), accumulator0());
+ __ orr(destination(), accumulator1(), Operand(accumulator1()));
+ Label* bailout =
+ info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ __ BranchOnNotSmi(destination(), bailout);
+ } else if (destination().is(accumulator1())) {
+ __ mov(scratch0(), accumulator1());
+ __ orr(destination(), accumulator1(), Operand(accumulator0()));
+ Label* bailout = info()->AddBailout(scratch0(), accumulator0());
+ __ BranchOnNotSmi(destination(), bailout);
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ orr(scratch0(), accumulator1(), Operand(accumulator0()));
+ Label* bailout = info()->AddBailout(accumulator1(), accumulator0());
+ __ BranchOnNotSmi(scratch0(), bailout);
+ }
}
// If we didn't bailout, the result (in fact, both inputs too) is known to
// Note that we keep a live register reference to cp (context) at
// this point.
+ Label* bailout_to_beginning = info()->AddBailout();
// Receiver (this) is allocated to a fixed register.
if (info()->has_this_properties()) {
Comment cmnt(masm(), ";; MapCheck(this)");
Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
Handle<Map> map(object->map());
EmitLoadReceiver();
- __ CheckMap(receiver_reg(), scratch0(), map, bailout(), false);
+ __ CheckMap(receiver_reg(), scratch0(), map, bailout_to_beginning, false);
}
// If there is a global variable access check if the global object is the
ASSERT(info()->has_global_object());
Handle<Map> map(info()->global_object()->map());
__ ldr(scratch0(), CodeGenerator::GlobalObject());
- __ CheckMap(scratch0(), scratch1(), map, bailout(), true);
+ __ CheckMap(scratch0(), scratch1(), map, bailout_to_beginning, true);
}
VisitStatements(function()->body());
int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
__ add(sp, sp, Operand(sp_delta));
__ Jump(lr);
-
- __ bind(&bailout_);
}
CodeGenerator cgen(&masm);
CodeGeneratorScope scope(&cgen);
live_edit_tracker.RecordFunctionScope(info->function()->scope());
- cgen.Generate(info, PRIMARY);
+ cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
// is constructed based on the resources available at compile-time.
class CompilationInfo BASE_EMBEDDED {
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
+ };
+
+ // A description of the compilation state at a bailout to the secondary
+ // code generator.
+ //
+ // The state is currently simple: there are no parameters or local
+ // variables to worry about ('this' can be found in the stack frame).
+ // There are at most two live values.
+ //
+ // There is a label that should be bound to the beginning of the bailout
+ // stub code.
+ class Bailout : public ZoneObject {
+ public:
+ Bailout(Register left, Register right) : left_(left), right_(right) {}
+
+ Label* label() { return &label_; }
+
+ private:
+ Register left_;
+ Register right_;
+ Label label_;
+ };
+
+
// Lazy compilation of a JSFunction.
CompilationInfo(Handle<JSFunction> closure,
int loop_nesting,
int loop_nesting() { return loop_nesting_; }
bool has_receiver() { return !receiver_.is_null(); }
Handle<Object> receiver() { return receiver_; }
+ List<Bailout*>* bailouts() { return &bailouts_; }
- // Accessors for mutable fields, possibly set by analysis passes with
+ // Accessors for mutable fields (possibly set by analysis passes) with
// default values given by Initialize.
+ Mode mode() { return mode_; }
+ void set_mode(Mode mode) { mode_ = mode; }
+
bool has_this_properties() { return has_this_properties_; }
void set_has_this_properties(bool flag) { has_this_properties_ = flag; }
// Derived accessors.
Scope* scope() { return function()->scope(); }
+ // Add a bailout with two live values.
+ Label* AddBailout(Register left, Register right) {
+ Bailout* bailout = new Bailout(left, right);
+ bailouts_.Add(bailout);
+ return bailout->label();
+ }
+
+ // Add a bailout with no live values.
+ Label* AddBailout() { return AddBailout(no_reg, no_reg); }
+
private:
void Initialize() {
+ mode_ = PRIMARY;
has_this_properties_ = false;
has_globals_ = false;
}
Handle<Script> script_;
FunctionLiteral* function_;
+ Mode mode_;
bool is_eval_;
int loop_nesting_;
bool has_this_properties_;
bool has_globals_;
+ // An ordered list of bailout points encountered during fast-path
+ // compilation.
+ List<Bailout*> bailouts_;
+
DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
};
// macro assembler.
CodeGenerator cgen(&masm);
CodeGeneratorScope scope(&cgen);
- cgen.Generate(info, CodeGenerator::SECONDARY);
+ info->set_mode(CompilationInfo::SECONDARY);
+ cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
#include "ast.h"
#include "compiler.h"
+#include "list.h"
namespace v8 {
namespace internal {
private:
MacroAssembler* masm() { return masm_; }
CompilationInfo* info() { return info_; }
- Label* bailout() { return &bailout_; }
Register destination() { return destination_; }
void set_destination(Register reg) { destination_ = reg; }
MacroAssembler* masm_;
CompilationInfo* info_;
- Label bailout_;
Register destination_;
uint32_t smi_bits_;
// edi: called JS function
// esi: callee's context
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
// esi: callee's context
allocator_->Initialize();
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// Allocate space for locals and initialize them.
// frame to match this state.
frame_->Adjust(3);
allocator_->Unuse(edi);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
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(CompilationInfo* info);
void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
// macro assembler.
CodeGenerator cgen(&masm);
CodeGeneratorScope scope(&cgen);
- cgen.Generate(info, CodeGenerator::SECONDARY);
+ info->set_mode(CompilationInfo::SECONDARY);
+ cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
// commutative.
__ or_(destination(), Operand(other_accumulator(destination())));
}
- } else if (destination().is(no_reg)) {
- // Result is not needed but do not clobber the operands in case of
- // bailout.
- __ mov(scratch0(), accumulator1());
- __ or_(scratch0(), Operand(accumulator0()));
- __ test(scratch0(), Immediate(kSmiTagMask));
- __ j(not_zero, bailout(), not_taken);
} else {
- // Preserve the destination operand in a scratch register in case of
- // bailout.
- __ mov(scratch0(), destination());
- __ or_(destination(), Operand(other_accumulator(destination())));
- __ test(destination(), Immediate(kSmiTagMask));
- __ j(not_zero, bailout(), not_taken);
+ // Left is in accumulator1, right in accumulator0.
+ Label* bailout = NULL;
+ if (destination().is(accumulator0())) {
+ __ mov(scratch0(), accumulator0());
+ __ or_(destination(), Operand(accumulator1())); // Or is commutative.
+ __ test(destination(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ } else if (destination().is(accumulator1())) {
+ __ mov(scratch0(), accumulator1());
+ __ or_(destination(), Operand(accumulator0()));
+ __ test(destination(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(scratch0(), accumulator0());
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ mov(scratch0(), accumulator1());
+ __ or_(scratch0(), Operand(accumulator0()));
+ __ test(scratch0(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(accumulator1(), accumulator0());
+ }
+ __ j(not_zero, bailout, not_taken);
}
// If we didn't bailout, the result (in fact, both inputs too) is known to
// Note that we keep a live register reference to esi (context) at this
// point.
+ Label* bailout_to_beginning = info()->AddBailout();
// Receiver (this) is allocated to a fixed register.
if (info()->has_this_properties()) {
Comment cmnt(masm(), ";; MapCheck(this)");
Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
Handle<Map> map(object->map());
EmitLoadReceiver();
- __ CheckMap(receiver_reg(), map, bailout(), false);
+ __ CheckMap(receiver_reg(), map, bailout_to_beginning, false);
}
// If there is a global variable access check if the global object is the
ASSERT(info()->has_global_object());
Handle<Map> map(info()->global_object()->map());
__ mov(scratch0(), CodeGenerator::GlobalObject());
- __ CheckMap(scratch0(), map, bailout(), true);
+ __ CheckMap(scratch0(), map, bailout_to_beginning, true);
}
VisitStatements(function()->body());
__ mov(esp, ebp);
__ pop(ebp);
__ ret((scope()->num_parameters() + 1) * kPointerSize);
-
- __ bind(&bailout_);
}
#include "ast.h"
#include "compiler.h"
+#include "list.h"
namespace v8 {
namespace internal {
private:
MacroAssembler* masm() { return masm_; }
CompilationInfo* info() { return info_; }
- Label* bailout() { return &bailout_; }
Register destination() { return destination_; }
void set_destination(Register reg) { destination_ = reg; }
MacroAssembler* masm_;
CompilationInfo* info_;
- Label bailout_;
+
Register destination_;
uint32_t smi_bits_;
}
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
// rsi: callee's context
allocator_->Initialize();
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// Allocate space for locals and initialize them.
// frame to match this state.
frame_->Adjust(3);
allocator_->Unuse(rdi);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
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(CompilationInfo* info);
void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
// commutative.
__ or_(destination(), other_accumulator(destination()));
}
- } else if (destination().is(no_reg)) {
- // Result is not needed but do not clobber the operands in case of
- // bailout.
- __ movq(scratch0(), accumulator1());
- __ or_(scratch0(), accumulator0());
- __ JumpIfNotSmi(scratch0(), bailout());
} else {
- // Preserve the destination operand in a scratch register in case of
- // bailout.
- __ movq(scratch0(), destination());
- __ or_(destination(), other_accumulator(destination()));
- __ JumpIfNotSmi(destination(), bailout());
+ // Left is in accumulator1, right in accumulator0.
+ if (destination().is(accumulator0())) {
+ __ movq(scratch0(), accumulator0());
+ __ or_(destination(), accumulator1()); // Or is commutative.
+ Label* bailout =
+ info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ __ JumpIfNotSmi(destination(), bailout);
+ } else if (destination().is(accumulator1())) {
+ __ movq(scratch0(), accumulator1());
+ __ or_(destination(), accumulator0());
+ Label* bailout = info()->AddBailout(scratch0(), accumulator0());
+ __ JumpIfNotSmi(destination(), bailout);
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ movq(scratch0(), accumulator1());
+ __ or_(scratch0(), accumulator0());
+ Label* bailout = info()->AddBailout(accumulator1(), accumulator0());
+ __ JumpIfNotSmi(scratch0(), bailout);
+ }
}
-
// If we didn't bailout, the result (in fact, both inputs too) is known to
// be a smi.
set_as_smi(accumulator0());
// Note that we keep a live register reference to esi (context) at this
// point.
+ Label* bailout_to_beginning = info()->AddBailout();
// Receiver (this) is allocated to a fixed register.
if (info()->has_this_properties()) {
Comment cmnt(masm(), ";; MapCheck(this)");
Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
Handle<Map> map(object->map());
EmitLoadReceiver();
- __ CheckMap(receiver_reg(), map, bailout(), false);
+ __ CheckMap(receiver_reg(), map, bailout_to_beginning, false);
}
// If there is a global variable access check if the global object is the
ASSERT(info()->has_global_object());
Handle<Map> map(info()->global_object()->map());
__ movq(scratch0(), CodeGenerator::GlobalObject());
- __ CheckMap(scratch0(), map, bailout(), true);
+ __ CheckMap(scratch0(), map, bailout_to_beginning, true);
}
VisitStatements(info()->function()->body());
__ movq(rsp, rbp);
__ pop(rbp);
__ ret((scope()->num_parameters() + 1) * kPointerSize);
-
- __ bind(&bailout_);
}