Add support to the CFG builder for non-short-circuited binary

expressions.  Add compilation support in fast mode (optimized for
compilation time and code size).

This breaks one debugger test each on ARM and IA32 (expectedly).
Review URL: http://codereview.chromium.org/160584

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2628 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent eb5c9458
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cfg.h" #include "cfg.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "codegen-arm.h" // Include after codegen-inl.h.
#include "macro-assembler-arm.h" #include "macro-assembler-arm.h"
namespace v8 { namespace v8 {
...@@ -42,6 +43,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) { ...@@ -42,6 +43,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) {
{ {
Comment cmt(masm, "[ InstructionBlock"); Comment cmt(masm, "[ InstructionBlock");
for (int i = 0, len = instructions_.length(); i < len; i++) { for (int i = 0, len = instructions_.length(); i < len; i++) {
// If the location of the current instruction is a temp, then the
// instruction cannot be in tail position in the block. Allocate the
// temp based on peeking ahead to the next instruction.
Instruction* instr = instructions_[i];
Location* loc = instr->location();
if (loc->is_temporary()) {
instructions_[i+1]->FastAllocate(TempLocation::cast(loc));
}
instructions_[i]->Compile(masm); instructions_[i]->Compile(masm);
} }
} }
...@@ -91,34 +100,124 @@ void ExitNode::Compile(MacroAssembler* masm) { ...@@ -91,34 +100,124 @@ void ExitNode::Compile(MacroAssembler* masm) {
} }
void BinaryOpInstr::Compile(MacroAssembler* masm) {
// The right-hand value should not be on the stack---if it is a
// compiler-generated temporary it is in the accumulator.
ASSERT(!val1_->is_on_stack());
Comment cmnt(masm, "[ BinaryOpInstr");
// We can overwrite one of the operands if it is a temporary.
OverwriteMode mode = NO_OVERWRITE;
if (val0_->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
mode = OVERWRITE_RIGHT;
}
// Move left to r1 and right to r0.
val0_->Get(masm, r1);
val1_->Get(masm, r0);
GenericBinaryOpStub stub(op_, mode);
__ CallStub(&stub);
loc_->Set(masm, r0);
}
void ReturnInstr::Compile(MacroAssembler* masm) { void ReturnInstr::Compile(MacroAssembler* masm) {
// The location should be 'Effect'. As a side effect, move the value to
// the accumulator.
Comment cmnt(masm, "[ ReturnInstr"); Comment cmnt(masm, "[ ReturnInstr");
value_->ToRegister(masm, r0); value_->Get(masm, r0);
} }
void Constant::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Get(MacroAssembler* masm, Register reg) {
__ mov(reg, Operand(handle_)); __ mov(reg, Operand(handle_));
} }
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Push(MacroAssembler* masm) {
switch (type_) { __ mov(ip, Operand(handle_));
__ push(ip);
}
static MemOperand ToMemOperand(SlotLocation* loc) {
switch (loc->type()) {
case Slot::PARAMETER: { case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ ldr(reg, MemOperand(fp, (1 + count - index_) * kPointerSize)); return MemOperand(fp, (1 + count - loc->index()) * kPointerSize);
break;
} }
case Slot::LOCAL: { case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ ldr(reg, MemOperand(fp, kOffset - index_ * kPointerSize)); return MemOperand(fp, kOffset - loc->index() * kPointerSize);
break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
return MemOperand(r0);
} }
} }
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ ldr(reg, ToMemOperand(this));
}
void SlotLocation::Set(MacroAssembler* masm, Register reg) {
__ str(reg, ToMemOperand(this));
}
void SlotLocation::Push(MacroAssembler* masm) {
__ ldr(ip, ToMemOperand(this));
__ push(ip); // Push will not destroy ip.
}
void TempLocation::Get(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(r0)) __ mov(reg, r0);
break;
case STACK:
__ pop(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
}
}
void TempLocation::Set(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(r0)) __ mov(r0, reg);
break;
case STACK:
__ push(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
}
}
void TempLocation::Push(MacroAssembler* masm) {
switch (where_) {
case ACCUMULATOR:
__ push(r0);
break;
case STACK:
case NOWHERE:
UNREACHABLE();
break;
}
}
#undef __ #undef __
} } // namespace v8::internal } } // namespace v8::internal
...@@ -697,96 +697,6 @@ void CodeGenerator::ToBoolean(JumpTarget* true_target, ...@@ -697,96 +697,6 @@ void CodeGenerator::ToBoolean(JumpTarget* true_target,
} }
class GenericBinaryOpStub : public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
int constant_rhs = CodeGenerator::kUnknownIntValue)
: op_(op),
mode_(mode),
constant_rhs_(constant_rhs),
specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)) { }
private:
Token::Value op_;
OverwriteMode mode_;
int constant_rhs_;
bool specialized_on_rhs_;
static const int kMaxKnownRhs = 0x40000000;
// Minor key encoding in 16 bits.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 6> {};
class KnownIntBits: public BitField<int, 8, 8> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| KnownIntBits::encode(MinorKeyForKnownInt());
}
void Generate(MacroAssembler* masm);
void HandleNonSmiBitwiseOp(MacroAssembler* masm);
static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) {
if (constant_rhs == CodeGenerator::kUnknownIntValue) return false;
if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3;
if (op == Token::MOD) {
if (constant_rhs <= 1) return false;
if (constant_rhs <= 10) return true;
if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true;
return false;
}
return false;
}
int MinorKeyForKnownInt() {
if (!specialized_on_rhs_) return 0;
if (constant_rhs_ <= 10) return constant_rhs_ + 1;
ASSERT(IsPowerOf2(constant_rhs_));
int key = 12;
int d = constant_rhs_;
while ((d & 1) == 0) {
key++;
d >>= 1;
}
return key;
}
const char* GetName() {
switch (op_) {
case Token::ADD: return "GenericBinaryOpStub_ADD";
case Token::SUB: return "GenericBinaryOpStub_SUB";
case Token::MUL: return "GenericBinaryOpStub_MUL";
case Token::DIV: return "GenericBinaryOpStub_DIV";
case Token::MOD: return "GenericBinaryOpStub_MOD";
case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
case Token::SAR: return "GenericBinaryOpStub_SAR";
case Token::SHL: return "GenericBinaryOpStub_SHL";
case Token::SHR: return "GenericBinaryOpStub_SHR";
default: return "GenericBinaryOpStub";
}
}
#ifdef DEBUG
void Print() {
if (!specialized_on_rhs_) {
PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_));
} else {
PrintF("GenericBinaryOpStub (%s by %d)\n",
Token::String(op_),
constant_rhs_);
}
}
#endif
};
void CodeGenerator::GenericBinaryOperation(Token::Value op, void CodeGenerator::GenericBinaryOperation(Token::Value op,
OverwriteMode overwrite_mode, OverwriteMode overwrite_mode,
int constant_rhs) { int constant_rhs) {
......
...@@ -421,6 +421,96 @@ class CodeGenerator: public AstVisitor { ...@@ -421,6 +421,96 @@ class CodeGenerator: public AstVisitor {
}; };
class GenericBinaryOpStub : public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
int constant_rhs = CodeGenerator::kUnknownIntValue)
: op_(op),
mode_(mode),
constant_rhs_(constant_rhs),
specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)) { }
private:
Token::Value op_;
OverwriteMode mode_;
int constant_rhs_;
bool specialized_on_rhs_;
static const int kMaxKnownRhs = 0x40000000;
// Minor key encoding in 16 bits.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 6> {};
class KnownIntBits: public BitField<int, 8, 8> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| KnownIntBits::encode(MinorKeyForKnownInt());
}
void Generate(MacroAssembler* masm);
void HandleNonSmiBitwiseOp(MacroAssembler* masm);
static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) {
if (constant_rhs == CodeGenerator::kUnknownIntValue) return false;
if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3;
if (op == Token::MOD) {
if (constant_rhs <= 1) return false;
if (constant_rhs <= 10) return true;
if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true;
return false;
}
return false;
}
int MinorKeyForKnownInt() {
if (!specialized_on_rhs_) return 0;
if (constant_rhs_ <= 10) return constant_rhs_ + 1;
ASSERT(IsPowerOf2(constant_rhs_));
int key = 12;
int d = constant_rhs_;
while ((d & 1) == 0) {
key++;
d >>= 1;
}
return key;
}
const char* GetName() {
switch (op_) {
case Token::ADD: return "GenericBinaryOpStub_ADD";
case Token::SUB: return "GenericBinaryOpStub_SUB";
case Token::MUL: return "GenericBinaryOpStub_MUL";
case Token::DIV: return "GenericBinaryOpStub_DIV";
case Token::MOD: return "GenericBinaryOpStub_MOD";
case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
case Token::SAR: return "GenericBinaryOpStub_SAR";
case Token::SHL: return "GenericBinaryOpStub_SHL";
case Token::SHR: return "GenericBinaryOpStub_SHR";
default: return "GenericBinaryOpStub";
}
}
#ifdef DEBUG
void Print() {
if (!specialized_on_rhs_) {
PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_));
} else {
PrintF("GenericBinaryOpStub (%s by %d)\n",
Token::String(op_),
constant_rhs_);
}
}
#endif
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_ARM_CODEGEN_ARM_H_ #endif // V8_ARM_CODEGEN_ARM_H_
...@@ -42,8 +42,10 @@ CfgGlobals* CfgGlobals::top_ = NULL; ...@@ -42,8 +42,10 @@ CfgGlobals* CfgGlobals::top_ = NULL;
CfgGlobals::CfgGlobals(FunctionLiteral* fun) CfgGlobals::CfgGlobals(FunctionLiteral* fun)
: global_fun_(fun), : global_fun_(fun),
global_exit_(new ExitNode()), global_exit_(new ExitNode()),
effect_(new Effect()),
#ifdef DEBUG #ifdef DEBUG
node_counter_(0), node_counter_(0),
temp_counter_(0),
#endif #endif
previous_(top_) { previous_(top_) {
top_ = this; top_ = this;
...@@ -73,6 +75,9 @@ Cfg* Cfg::Build() { ...@@ -73,6 +75,9 @@ Cfg* Cfg::Build() {
if (cfg == NULL) { if (cfg == NULL) {
BAILOUT("unsupported statement type"); BAILOUT("unsupported statement type");
} }
if (cfg->is_empty()) {
BAILOUT("function body produces empty cfg");
}
if (cfg->has_exit()) { if (cfg->has_exit()) {
BAILOUT("control path without explicit return"); BAILOUT("control path without explicit return");
} }
...@@ -90,8 +95,10 @@ void Cfg::PrependEntryNode() { ...@@ -90,8 +95,10 @@ void Cfg::PrependEntryNode() {
void Cfg::Append(Instruction* instr) { void Cfg::Append(Instruction* instr) {
ASSERT(has_exit()); ASSERT(is_empty() || has_exit());
ASSERT(!is_empty()); if (is_empty()) {
entry_ = exit_ = new InstructionBlock();
}
InstructionBlock::cast(exit_)->Append(instr); InstructionBlock::cast(exit_)->Append(instr);
} }
...@@ -104,6 +111,27 @@ void Cfg::AppendReturnInstruction(Value* value) { ...@@ -104,6 +111,27 @@ void Cfg::AppendReturnInstruction(Value* value) {
} }
void Cfg::Concatenate(Cfg* other) {
ASSERT(is_empty() || has_exit());
if (other->is_empty()) return;
if (is_empty()) {
entry_ = other->entry();
exit_ = other->exit();
} else {
// We have a pair of nonempty fragments and this has an available exit.
// Destructively glue the fragments together.
InstructionBlock* first = InstructionBlock::cast(exit_);
InstructionBlock* second = InstructionBlock::cast(other->entry());
first->instructions()->AddAll(*second->instructions());
if (second->successor() != NULL) {
first->set_successor(second->successor());
exit_ = other->exit();
}
}
}
void InstructionBlock::Unmark() { void InstructionBlock::Unmark() {
if (is_marked_) { if (is_marked_) {
is_marked_ = false; is_marked_ = false;
...@@ -166,6 +194,26 @@ Handle<Code> Cfg::Compile(Handle<Script> script) { ...@@ -166,6 +194,26 @@ Handle<Code> Cfg::Compile(Handle<Script> script) {
} }
void BinaryOpInstr::FastAllocate(TempLocation* temp) {
ASSERT(temp->where() == TempLocation::NOWHERE);
if (temp == val0_ || temp == val1_) {
temp->set_where(TempLocation::ACCUMULATOR);
} else {
temp->set_where(TempLocation::STACK);
}
}
void ReturnInstr::FastAllocate(TempLocation* temp) {
ASSERT(temp->where() == TempLocation::NOWHERE);
if (temp == value_) {
temp->set_where(TempLocation::ACCUMULATOR);
} else {
temp->set_where(TempLocation::STACK);
}
}
// The expression builder should not be used for declarations or statements. // The expression builder should not be used for declarations or statements.
void ExpressionBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } void ExpressionBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); }
...@@ -178,13 +226,10 @@ STATEMENT_NODE_LIST(DEFINE_VISIT) ...@@ -178,13 +226,10 @@ STATEMENT_NODE_LIST(DEFINE_VISIT)
// Macros (temporarily) handling unsupported expression types. // Macros (temporarily) handling unsupported expression types.
#define BAILOUT(reason) \ #define BAILOUT(reason) \
do { \ do { \
value_ = NULL; \ cfg_ = NULL; \
return; \ return; \
} while (false) } while (false)
#define CHECK_BAILOUT() \
if (value_ == NULL) { return; } else {}
void ExpressionBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { void ExpressionBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
BAILOUT("FunctionLiteral"); BAILOUT("FunctionLiteral");
} }
...@@ -290,7 +335,46 @@ void ExpressionBuilder::VisitCountOperation(CountOperation* expr) { ...@@ -290,7 +335,46 @@ void ExpressionBuilder::VisitCountOperation(CountOperation* expr) {
void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) { void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) {
BAILOUT("BinaryOperation"); Token::Value op = expr->op();
switch (op) {
case Token::COMMA:
case Token::OR:
case Token::AND:
BAILOUT("unsupported binary operation");
case Token::BIT_OR:
case Token::BIT_XOR:
case Token::BIT_AND:
case Token::SHL:
case Token::SAR:
case Token::SHR:
case Token::ADD:
case Token::SUB:
case Token::MUL:
case Token::DIV:
case Token::MOD: {
ExpressionBuilder left, right;
left.Build(expr->left());
if (left.cfg() == NULL) {
BAILOUT("unsupported left subexpression in binop");
}
right.Build(expr->right());
if (right.cfg() == NULL) {
BAILOUT("unsupported right subexpression in binop");
}
Location* temp = new TempLocation();
cfg_ = left.cfg();
cfg_->Concatenate(right.cfg());
cfg_->Append(new BinaryOpInstr(temp, op, left.value(), right.value()));
value_ = temp;
return;
}
default:
UNREACHABLE();
}
} }
...@@ -304,7 +388,6 @@ void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) { ...@@ -304,7 +388,6 @@ void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) {
} }
#undef BAILOUT #undef BAILOUT
#undef CHECK_BAILOUT
// Macros (temporarily) handling unsupported statement types. // Macros (temporarily) handling unsupported statement types.
...@@ -367,10 +450,13 @@ void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) { ...@@ -367,10 +450,13 @@ void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) {
void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) { void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) {
ExpressionBuilder builder; ExpressionBuilder builder;
builder.Visit(stmt->expression()); builder.Build(stmt->expression());
Value* value = builder.value(); if (builder.cfg() == NULL) {
if (value == NULL) BAILOUT("unsupported expression type"); BAILOUT("unsupported expression in return statement");
cfg_->AppendReturnInstruction(value); }
cfg_->Concatenate(builder.cfg());
cfg_->AppendReturnInstruction(builder.value());
} }
...@@ -430,6 +516,11 @@ void Constant::Print() { ...@@ -430,6 +516,11 @@ void Constant::Print() {
} }
void Effect::Print() {
PrintF("Effect");
}
void SlotLocation::Print() { void SlotLocation::Print() {
PrintF("Slot("); PrintF("Slot(");
switch (type_) { switch (type_) {
...@@ -445,10 +536,26 @@ void SlotLocation::Print() { ...@@ -445,10 +536,26 @@ void SlotLocation::Print() {
} }
void TempLocation::Print() {
PrintF("Temp(%d)", number());
}
void BinaryOpInstr::Print() {
PrintF("BinaryOp(");
loc_->Print();
PrintF(", %s, ", Token::Name(op_));
val0_->Print();
PrintF(", ");
val1_->Print();
PrintF(")\n");
}
void ReturnInstr::Print() { void ReturnInstr::Print() {
PrintF("Return "); PrintF("Return(");
value_->Print(); value_->Print();
PrintF("\n"); PrintF(")\n");
} }
......
...@@ -34,6 +34,47 @@ namespace v8 { ...@@ -34,6 +34,47 @@ namespace v8 {
namespace internal { namespace internal {
class ExitNode; class ExitNode;
class Location;
// Translate a source AST into a control-flow graph (CFG). The CFG contains
// single-entry, single-exit blocks of straight-line instructions and
// administrative nodes.
//
// Instructions are described by the following grammar.
//
// <Instruction> ::=
// BinaryOpInstr <Location> Token::Value <Value> <Value>
// | ReturnInstr Effect <Value>
//
// Values are trivial expressions:
//
// <Value> ::= Constant | <Location>
//
// Locations are storable values ('lvalues'). They can be slots,
// compiler-generated temporaries, or the special location 'Effect'
// indicating that no value is needed.
//
// <Location> ::=
// SlotLocation Slot::Type <Index>
// | TempLocation
// | Effect
// Administrative nodes: There are several types of 'administrative' nodes
// that do not contain instructions and do not necessarily have a single
// predecessor and a single successor.
//
// EntryNode: there is a distinguished entry node that has no predecessors
// and a single successor.
//
// ExitNode: there is a distinguished exit node that has arbitrarily many
// predecessors and no successor.
//
// JoinNode: join nodes have multiple predecessors and a single successor.
//
// BranchNode: branch nodes have a single predecessor and multiple
// successors.
// A convenient class to keep 'global' values when building a CFG. Since // A convenient class to keep 'global' values when building a CFG. Since
// CFG construction can be invoked recursively, CFG globals are stacked. // CFG construction can be invoked recursively, CFG globals are stacked.
...@@ -48,40 +89,58 @@ class CfgGlobals BASE_EMBEDDED { ...@@ -48,40 +89,58 @@ class CfgGlobals BASE_EMBEDDED {
return top_; return top_;
} }
// The function currently being compiled.
FunctionLiteral* fun() { return global_fun_; } FunctionLiteral* fun() { return global_fun_; }
// The shared global exit node for all exits from the function.
ExitNode* exit() { return global_exit_; } ExitNode* exit() { return global_exit_; }
// A singleton effect location.
Location* effect_location() { return effect_; }
#ifdef DEBUG #ifdef DEBUG
int next_number() { return node_counter_++; } int next_node_number() { return node_counter_++; }
int next_temp_number() { return temp_counter_++; }
#endif #endif
private: private:
static CfgGlobals* top_; static CfgGlobals* top_;
// Function literal currently compiling.
FunctionLiteral* global_fun_; FunctionLiteral* global_fun_;
// Shared global exit node for all returns from the same function.
ExitNode* global_exit_; ExitNode* global_exit_;
Location* effect_;
#ifdef DEBUG #ifdef DEBUG
// Used to number nodes when printing. // Used to number nodes and temporaries when printing.
int node_counter_; int node_counter_;
int temp_counter_;
#endif #endif
CfgGlobals* previous_; CfgGlobals* previous_;
}; };
// Values appear in instructions. They represent trivial source // Values represent trivial source expressions: ones with no side effects
// expressions: ones with no side effects and that do not require code to be // and that do not require code to be generated.
// generated.
class Value : public ZoneObject { class Value : public ZoneObject {
public: public:
virtual ~Value() {} virtual ~Value() {}
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0; // Predicates:
// True if the value is a temporary allocated to the stack in
// fast-compilation mode.
virtual bool is_on_stack() { return false; }
// True if the value is a compiler-generated temporary location.
virtual bool is_temporary() { return false; }
// Support for fast-compilation mode:
// Move the value into a register.
virtual void Get(MacroAssembler* masm, Register reg) = 0;
// Push the value on the stack.
virtual void Push(MacroAssembler* masm) = 0;
#ifdef DEBUG #ifdef DEBUG
virtual void Print() = 0; virtual void Print() = 0;
...@@ -96,7 +155,9 @@ class Constant : public Value { ...@@ -96,7 +155,9 @@ class Constant : public Value {
virtual ~Constant() {} virtual ~Constant() {}
void ToRegister(MacroAssembler* masm, Register reg); // Support for fast-compilation mode.
void Get(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
#ifdef DEBUG #ifdef DEBUG
void Print(); void Print();
...@@ -112,7 +173,23 @@ class Location : public Value { ...@@ -112,7 +173,23 @@ class Location : public Value {
public: public:
virtual ~Location() {} virtual ~Location() {}
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0; // Static factory function returning the singleton effect location.
static Location* Effect() {
return CfgGlobals::current()->effect_location();
}
// Support for fast-compilation mode:
// Assumes temporaries have been allocated.
virtual void Get(MacroAssembler* masm, Register reg) = 0;
// Store the value in a register to the location. Assumes temporaries
// have been allocated.
virtual void Set(MacroAssembler* masm, Register reg) = 0;
// Assumes temporaries have been allocated, and if the value is a
// temporary it was not allocated to the stack.
virtual void Push(MacroAssembler* masm) = 0;
#ifdef DEBUG #ifdef DEBUG
virtual void Print() = 0; virtual void Print() = 0;
...@@ -120,13 +197,40 @@ class Location : public Value { ...@@ -120,13 +197,40 @@ class Location : public Value {
}; };
// Effect is a special (singleton) location that indicates the value of a
// computation is not needed (though its side effects are).
class Effect : public Location {
public:
// We should not try to emit code to read or write to Effect.
void Get(MacroAssembler* masm, Register reg) { UNREACHABLE(); }
void Set(MacroAssembler* masm, Register reg) { UNREACHABLE(); }
void Push(MacroAssembler* masm) { UNREACHABLE(); }
#ifdef DEBUG
void Print();
#endif
private:
Effect() {}
friend class CfgGlobals;
};
// SlotLocations represent parameters and stack-allocated (i.e., // SlotLocations represent parameters and stack-allocated (i.e.,
// non-context) local variables. // non-context) local variables.
class SlotLocation : public Location { class SlotLocation : public Location {
public: public:
SlotLocation(Slot::Type type, int index) : type_(type), index_(index) {} SlotLocation(Slot::Type type, int index) : type_(type), index_(index) {}
void ToRegister(MacroAssembler* masm, Register reg); // Accessors.
Slot::Type type() { return type_; }
int index() { return index_; }
// Support for fast-compilation mode.
void Get(MacroAssembler* masm, Register reg);
void Set(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
#ifdef DEBUG #ifdef DEBUG
void Print(); void Print();
...@@ -138,29 +242,136 @@ class SlotLocation : public Location { ...@@ -138,29 +242,136 @@ class SlotLocation : public Location {
}; };
// TempLocations represent compiler generated temporaries. They are
// allocated to registers or memory either before code generation (in the
// optimized-for-speed compiler) or on the fly during code generation (in
// the optimized-for-space compiler).
class TempLocation : public Location {
public:
// Fast-compilation mode allocation decisions.
enum Where {
NOWHERE, // Not yet allocated.
ACCUMULATOR, // Allocated to the dedicated accumulator register.
STACK // " " " " stack.
};
TempLocation() : where_(NOWHERE) {
#ifdef DEBUG
number_ = -1;
#endif
}
// Cast accessor.
static TempLocation* cast(Location* loc) {
ASSERT(loc->is_temporary());
return reinterpret_cast<TempLocation*>(loc);
}
// Accessors.
Where where() { return where_; }
void set_where(Where where) { where_ = where; }
// Predicates.
bool is_on_stack() { return where_ == STACK; }
bool is_temporary() { return true; }
// Support for fast-compilation mode. Assume the temp has been allocated.
void Get(MacroAssembler* masm, Register reg);
void Set(MacroAssembler* masm, Register reg);
void Push(MacroAssembler* masm);
#ifdef DEBUG
int number() {
if (number_ == -1) number_ = CfgGlobals::current()->next_temp_number();
return number_;
}
void Print();
#endif
private:
Where where_;
#ifdef DEBUG
int number_;
#endif
};
// Instructions are computations. The represent non-trivial source // Instructions are computations. The represent non-trivial source
// expressions: typically ones that have side effects and require code to // expressions: typically ones that have side effects and require code to
// be generated. // be generated.
class Instruction : public ZoneObject { class Instruction : public ZoneObject {
public: public:
// Every instruction has a location where its result is stored (which may
// be Effect).
explicit Instruction(Location* loc) : loc_(loc) {}
virtual ~Instruction() {} virtual ~Instruction() {}
// Accessors.
Location* location() { return loc_; }
// Support for fast-compilation mode:
// Emit code to perform the instruction.
virtual void Compile(MacroAssembler* masm) = 0; virtual void Compile(MacroAssembler* masm) = 0;
// Allocate a temporary which is the result of the immediate predecessor
// instruction. It is allocated to the accumulator register if it is used
// as an operand to this instruction, otherwise to the stack.
virtual void FastAllocate(TempLocation* temp) = 0;
#ifdef DEBUG #ifdef DEBUG
virtual void Print() = 0; virtual void Print() = 0;
#endif #endif
protected:
Location* loc_;
}; };
// Return a value. // Perform a (non-short-circuited) binary operation on a pair of values,
// leaving the result in a location.
class BinaryOpInstr : public Instruction {
public:
BinaryOpInstr(Location* loc, Token::Value op, Value* val0, Value* val1)
: Instruction(loc), op_(op), val0_(val0), val1_(val1) {
}
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm);
void FastAllocate(TempLocation* temp);
#ifdef DEBUG
void Print();
#endif
private:
Token::Value op_;
Value* val0_;
Value* val1_;
};
// Return a value. Has the side effect of moving its value into the return
// value register. Can only occur as the last instruction in an instruction
// block, and implies that the block is closed (cannot have instructions
// appended or graph fragments concatenated to the end) and that the block's
// successor is the global exit node for the current function.
class ReturnInstr : public Instruction { class ReturnInstr : public Instruction {
public: public:
explicit ReturnInstr(Value* value) : value_(value) {} // Location is always Effect.
explicit ReturnInstr(Value* value)
: Instruction(CfgGlobals::current()->effect_location()),
value_(value) {
}
virtual ~ReturnInstr() {} virtual ~ReturnInstr() {}
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm); void Compile(MacroAssembler* masm);
void FastAllocate(TempLocation* temp);
#ifdef DEBUG #ifdef DEBUG
void Print(); void Print();
...@@ -171,9 +382,7 @@ class ReturnInstr : public Instruction { ...@@ -171,9 +382,7 @@ class ReturnInstr : public Instruction {
}; };
// Nodes make up control-flow graphs. They contain single-entry, // Nodes make up control-flow graphs.
// single-exit blocks of instructions and administrative nodes making up the
// graph structure.
class CfgNode : public ZoneObject { class CfgNode : public ZoneObject {
public: public:
CfgNode() : is_marked_(false) { CfgNode() : is_marked_(false) {
...@@ -184,17 +393,26 @@ class CfgNode : public ZoneObject { ...@@ -184,17 +393,26 @@ class CfgNode : public ZoneObject {
virtual ~CfgNode() {} virtual ~CfgNode() {}
// Because CFGs contain cycles, nodes support marking during traversal
// (e.g., for printing or compilation). The traversal functions will mark
// unmarked nodes and backtrack if they encounter a marked one. After a
// traversal, the graph should be explicitly unmarked by calling Unmark on
// the entry node.
bool is_marked() { return is_marked_; } bool is_marked() { return is_marked_; }
virtual void Unmark() = 0;
virtual bool is_block() { return false; } // Predicates:
virtual void Unmark() = 0; // True if the node is an instruction block.
virtual bool is_block() { return false; }
// Support for fast-compilation mode. Emit the instructions or control
// flow represented by the node.
virtual void Compile(MacroAssembler* masm) = 0; virtual void Compile(MacroAssembler* masm) = 0;
#ifdef DEBUG #ifdef DEBUG
int number() { int number() {
if (number_ == -1) number_ = CfgGlobals::current()->next_number(); if (number_ == -1) number_ = CfgGlobals::current()->next_node_number();
return number_; return number_;
} }
...@@ -217,22 +435,30 @@ class InstructionBlock : public CfgNode { ...@@ -217,22 +435,30 @@ class InstructionBlock : public CfgNode {
virtual ~InstructionBlock() {} virtual ~InstructionBlock() {}
void Unmark();
// Cast accessor.
static InstructionBlock* cast(CfgNode* node) { static InstructionBlock* cast(CfgNode* node) {
ASSERT(node->is_block()); ASSERT(node->is_block());
return reinterpret_cast<InstructionBlock*>(node); return reinterpret_cast<InstructionBlock*>(node);
} }
bool is_block() { return true; }
// Accessors.
CfgNode* successor() { return successor_; }
void set_successor(CfgNode* succ) { void set_successor(CfgNode* succ) {
ASSERT(successor_ == NULL); ASSERT(successor_ == NULL);
successor_ = succ; successor_ = succ;
} }
bool is_block() { return true; } ZoneList<Instruction*>* instructions() { return &instructions_; }
void Unmark();
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm); void Compile(MacroAssembler* masm);
// Add an instruction to the end of the block.
void Append(Instruction* instr) { instructions_.Add(instr); } void Append(Instruction* instr) { instructions_.Add(instr); }
#ifdef DEBUG #ifdef DEBUG
...@@ -245,9 +471,7 @@ class InstructionBlock : public CfgNode { ...@@ -245,9 +471,7 @@ class InstructionBlock : public CfgNode {
}; };
// The CFG for a function has a distinguished entry node. It has no // An entry node (one per function).
// predecessors and a single successor. The successor is the block
// containing the function's first instruction.
class EntryNode : public CfgNode { class EntryNode : public CfgNode {
public: public:
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {} explicit EntryNode(InstructionBlock* succ) : successor_(succ) {}
...@@ -256,6 +480,7 @@ class EntryNode : public CfgNode { ...@@ -256,6 +480,7 @@ class EntryNode : public CfgNode {
void Unmark(); void Unmark();
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm); void Compile(MacroAssembler* masm);
#ifdef DEBUG #ifdef DEBUG
...@@ -267,9 +492,7 @@ class EntryNode : public CfgNode { ...@@ -267,9 +492,7 @@ class EntryNode : public CfgNode {
}; };
// The CFG for a function has a distinguished exit node. It has no // An exit node (one per function).
// successor and arbitrarily many predecessors. The predecessors are all
// the blocks returning from the function.
class ExitNode : public CfgNode { class ExitNode : public CfgNode {
public: public:
ExitNode() {} ExitNode() {}
...@@ -278,6 +501,7 @@ class ExitNode : public CfgNode { ...@@ -278,6 +501,7 @@ class ExitNode : public CfgNode {
void Unmark(); void Unmark();
// Support for fast-compilation mode.
void Compile(MacroAssembler* masm); void Compile(MacroAssembler* masm);
#ifdef DEBUG #ifdef DEBUG
...@@ -286,28 +510,36 @@ class ExitNode : public CfgNode { ...@@ -286,28 +510,36 @@ class ExitNode : public CfgNode {
}; };
// A CFG consists of a linked structure of nodes. It has a single entry // A CFG consists of a linked structure of nodes. Nodes are linked by
// node and optionally an exit node. There is a distinguished global exit // pointing to their successors, always beginning with a (single) entry node
// node that is used as the successor of all blocks that return from the // (not necessarily of type EntryNode). If it is still possible to add
// function. // nodes to the end of the graph (i.e., there is a (single) path that does
// not end with the global exit node), then the CFG has an exit node as
// well.
//
// The empty CFG is represented by a NULL entry and a NULL exit.
//
// We use the term 'open fragment' to mean a CFG whose entry and exits are
// both instruction blocks. It is always possible to add instructions and
// nodes to the beginning or end of an open fragment.
// //
// Fragments of control-flow graphs, produced when traversing the statements // We use the term 'closed fragment' to mean a CFG whose entry is an
// and expressions in the source AST, are represented by the same class. // instruction block and whose exit is NULL (all paths go to the global
// They have instruction blocks as both their entry and exit (if there is // exit).
// one). Instructions can always be prepended or appended to fragments, and
// fragments can always be concatenated.
// //
// A singleton CFG fragment (i.e., with only one node) has the same node as // We use the term 'fragment' to refer to a CFG that is known to be an open
// both entry and exit (if the exit is available). // or closed fragment.
class Cfg : public ZoneObject { class Cfg : public ZoneObject {
public: public:
// Create a singleton CFG fragment. // Create an empty CFG fragment.
explicit Cfg(InstructionBlock* block) : entry_(block), exit_(block) {} Cfg() : entry_(NULL), exit_(NULL) {}
// Build the CFG for a function. // Build the CFG for a function. The returned CFG begins with an
// EntryNode and all paths end with the ExitNode.
static Cfg* Build(); static Cfg* Build();
// The entry and exit nodes. // The entry and exit nodes of the CFG (not necessarily EntryNode and
// ExitNode).
CfgNode* entry() { return entry_; } CfgNode* entry() { return entry_; }
CfgNode* exit() { return exit_; } CfgNode* exit() { return exit_; }
...@@ -318,18 +550,21 @@ class Cfg : public ZoneObject { ...@@ -318,18 +550,21 @@ class Cfg : public ZoneObject {
// concatenated to). // concatenated to).
bool has_exit() { return exit_ != NULL; } bool has_exit() { return exit_ != NULL; }
// Add an entry node to a CFG fragment. It is no longer a fragment // Add an EntryNode to a CFG fragment. It is no longer a fragment
// (instructions cannot be prepended). // (instructions can no longer be prepended).
void PrependEntryNode(); void PrependEntryNode();
// Append an instruction to the end of a CFG fragment. Assumes it has an // Append an instruction to the end of an open fragment.
// available exit.
void Append(Instruction* instr); void Append(Instruction* instr);
// Appends a return instruction to the end of a CFG fragment. It no // Appends a return instruction to the end of an open fragment and make
// longer has an available exit node. // it a closed fragment (the exit's successor becomes global exit node).
void AppendReturnInstruction(Value* value); void AppendReturnInstruction(Value* value);
// Glue an other CFG fragment to the end of this (open) fragment.
void Concatenate(Cfg* other);
// Support for compilation. Compile the entire CFG.
Handle<Code> Compile(Handle<Script> script); Handle<Code> Compile(Handle<Script> script);
#ifdef DEBUG #ifdef DEBUG
...@@ -344,12 +579,24 @@ class Cfg : public ZoneObject { ...@@ -344,12 +579,24 @@ class Cfg : public ZoneObject {
}; };
// An Expression Builder traverses a trivial expression and returns a value. // An ExpressionBuilder traverses an expression and returns an open CFG
// fragment (currently a possibly empty list of instructions represented by
// a singleton instruction block) and the expression's value.
//
// Failure is to build the CFG is indicated by a NULL CFG.
class ExpressionBuilder : public AstVisitor { class ExpressionBuilder : public AstVisitor {
public: public:
ExpressionBuilder() : value_(new Constant(Handle<Object>::null())) {} ExpressionBuilder() : value_(NULL), cfg_(NULL) {}
// Result accessors.
Value* value() { return value_; } Value* value() { return value_; }
Cfg* cfg() { return cfg_; }
void Build(Expression* expr) {
value_ = NULL;
cfg_ = new Cfg();
Visit(expr);
}
// AST node visitors. // AST node visitors.
#define DECLARE_VISIT(type) void Visit##type(type* node); #define DECLARE_VISIT(type) void Visit##type(type* node);
...@@ -358,13 +605,16 @@ class ExpressionBuilder : public AstVisitor { ...@@ -358,13 +605,16 @@ class ExpressionBuilder : public AstVisitor {
private: private:
Value* value_; Value* value_;
Cfg* cfg_;
}; };
// A StatementBuilder traverses a statement and returns a CFG. // A StatementBuilder maintains a CFG fragment accumulator. When it visits
// a statement, it concatenates the CFG for the statement to the end of the
// accumulator.
class StatementBuilder : public AstVisitor { class StatementBuilder : public AstVisitor {
public: public:
StatementBuilder() : cfg_(new Cfg(new InstructionBlock())) {} StatementBuilder() : cfg_(new Cfg()) {}
Cfg* cfg() { return cfg_; } Cfg* cfg() { return cfg_; }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cfg.h" #include "cfg.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "codegen-ia32.h"
#include "macro-assembler-ia32.h" #include "macro-assembler-ia32.h"
namespace v8 { namespace v8 {
...@@ -42,6 +43,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) { ...@@ -42,6 +43,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) {
{ {
Comment cmt(masm, "[ InstructionBlock"); Comment cmt(masm, "[ InstructionBlock");
for (int i = 0, len = instructions_.length(); i < len; i++) { for (int i = 0, len = instructions_.length(); i < len; i++) {
// If the location of the current instruction is a temp, then the
// instruction cannot be in tail position in the block. Allocate the
// temp based on peeking ahead to the next instruction.
Instruction* instr = instructions_[i];
Location* loc = instr->location();
if (loc->is_temporary()) {
instructions_[i+1]->FastAllocate(TempLocation::cast(loc));
}
instructions_[i]->Compile(masm); instructions_[i]->Compile(masm);
} }
} }
...@@ -79,6 +88,7 @@ void EntryNode::Compile(MacroAssembler* masm) { ...@@ -79,6 +88,7 @@ void EntryNode::Compile(MacroAssembler* masm) {
} }
successor_->Compile(masm); successor_->Compile(masm);
if (FLAG_check_stack) { if (FLAG_check_stack) {
Comment cmnt(masm, "[ Deferred Stack Check");
__ bind(&deferred_enter); __ bind(&deferred_enter);
StackCheckStub stub; StackCheckStub stub;
__ CallStub(&stub); __ CallStub(&stub);
...@@ -103,31 +113,120 @@ void ExitNode::Compile(MacroAssembler* masm) { ...@@ -103,31 +113,120 @@ void ExitNode::Compile(MacroAssembler* masm) {
} }
void BinaryOpInstr::Compile(MacroAssembler* masm) {
// The right-hand value should not be on the stack---if it is a
// compiler-generated temporary it is in the accumulator.
ASSERT(!val1_->is_on_stack());
Comment cmnt(masm, "[ BinaryOpInstr");
// We can overwrite one of the operands if it is a temporary.
OverwriteMode mode = NO_OVERWRITE;
if (val0_->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
mode = OVERWRITE_RIGHT;
}
// Push both operands and call the specialized stub.
if (!val0_->is_on_stack()) {
val0_->Push(masm);
}
val1_->Push(masm);
GenericBinaryOpStub stub(op_, mode, SMI_CODE_IN_STUB);
__ CallStub(&stub);
loc_->Set(masm, eax);
}
void ReturnInstr::Compile(MacroAssembler* masm) { void ReturnInstr::Compile(MacroAssembler* masm) {
// The location should be 'Effect'. As a side effect, move the value to
// the accumulator.
Comment cmnt(masm, "[ ReturnInstr"); Comment cmnt(masm, "[ ReturnInstr");
value_->ToRegister(masm, eax); value_->Get(masm, eax);
} }
void Constant::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Get(MacroAssembler* masm, Register reg) {
__ mov(reg, Immediate(handle_)); __ mov(reg, Immediate(handle_));
} }
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Push(MacroAssembler* masm) {
switch (type_) { __ push(Immediate(handle_));
}
static Operand ToOperand(SlotLocation* loc) {
switch (loc->type()) {
case Slot::PARAMETER: { case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ mov(reg, Operand(ebp, (1 + count - index_) * kPointerSize)); return Operand(ebp, (1 + count - loc->index()) * kPointerSize);
break;
} }
case Slot::LOCAL: { case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ mov(reg, Operand(ebp, kOffset - index_ * kPointerSize)); return Operand(ebp, kOffset - loc->index() * kPointerSize);
break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
return Operand(eax);
}
}
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ mov(reg, ToOperand(this));
}
void SlotLocation::Set(MacroAssembler* masm, Register reg) {
__ mov(ToOperand(this), reg);
}
void SlotLocation::Push(MacroAssembler* masm) {
__ push(ToOperand(this));
}
void TempLocation::Get(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(eax)) __ mov(reg, eax);
break;
case STACK:
__ pop(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
}
}
void TempLocation::Set(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(eax)) __ mov(eax, reg);
break;
case STACK:
__ push(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
}
}
void TempLocation::Push(MacroAssembler* masm) {
switch (where_) {
case ACCUMULATOR:
__ push(eax);
break;
case STACK:
case NOWHERE:
UNREACHABLE();
break;
} }
} }
......
...@@ -778,57 +778,6 @@ class FloatingPointHelper : public AllStatic { ...@@ -778,57 +778,6 @@ class FloatingPointHelper : public AllStatic {
}; };
// Flag that indicates whether or not the code that handles smi arguments
// should be placed in the stub, inlined, or omitted entirely.
enum GenericBinaryFlags {
SMI_CODE_IN_STUB,
SMI_CODE_INLINED
};
class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags)
: op_(op), mode_(mode), flags_(flags) {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
void GenerateSmiCode(MacroAssembler* masm, Label* slow);
private:
Token::Value op_;
OverwriteMode mode_;
GenericBinaryFlags flags_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_));
}
#endif
// Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 13> {};
class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
const char* GenericBinaryOpStub::GetName() { const char* GenericBinaryOpStub::GetName() {
switch (op_) { switch (op_) {
case Token::ADD: return "GenericBinaryOpStub_ADD"; case Token::ADD: return "GenericBinaryOpStub_ADD";
......
...@@ -609,6 +609,57 @@ class CodeGenerator: public AstVisitor { ...@@ -609,6 +609,57 @@ class CodeGenerator: public AstVisitor {
}; };
// Flag that indicates whether or not the code that handles smi arguments
// should be placed in the stub, inlined, or omitted entirely.
enum GenericBinaryFlags {
SMI_CODE_IN_STUB,
SMI_CODE_INLINED
};
class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags)
: op_(op), mode_(mode), flags_(flags) {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
void GenerateSmiCode(MacroAssembler* masm, Label* slow);
private:
Token::Value op_;
OverwriteMode mode_;
GenericBinaryFlags flags_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_));
}
#endif
// Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 13> {};
class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_ #endif // V8_IA32_CODEGEN_IA32_H_
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cfg.h" #include "cfg.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "codegen-x64.h"
#include "debug.h" #include "debug.h"
#include "macro-assembler-x64.h" #include "macro-assembler-x64.h"
...@@ -43,6 +44,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) { ...@@ -43,6 +44,14 @@ void InstructionBlock::Compile(MacroAssembler* masm) {
{ {
Comment cmt(masm, "[ InstructionBlock"); Comment cmt(masm, "[ InstructionBlock");
for (int i = 0, len = instructions_.length(); i < len; i++) { for (int i = 0, len = instructions_.length(); i < len; i++) {
// If the location of the current instruction is a temp, then the
// instruction cannot be in tail position in the block. Allocate the
// temp based on peeking ahead to the next instruction.
Instruction* instr = instructions_[i];
Location* loc = instr->location();
if (loc->is_temporary()) {
instructions_[i+1]->FastAllocate(TempLocation::cast(loc));
}
instructions_[i]->Compile(masm); instructions_[i]->Compile(masm);
} }
} }
...@@ -82,6 +91,7 @@ void EntryNode::Compile(MacroAssembler* masm) { ...@@ -82,6 +91,7 @@ void EntryNode::Compile(MacroAssembler* masm) {
} }
successor_->Compile(masm); successor_->Compile(masm);
if (FLAG_check_stack) { if (FLAG_check_stack) {
Comment cmnt(masm, "[ Deferred Stack Check");
__ bind(&deferred_enter); __ bind(&deferred_enter);
StackCheckStub stub; StackCheckStub stub;
__ CallStub(&stub); __ CallStub(&stub);
...@@ -113,34 +123,124 @@ void ExitNode::Compile(MacroAssembler* masm) { ...@@ -113,34 +123,124 @@ void ExitNode::Compile(MacroAssembler* masm) {
} }
void BinaryOpInstr::Compile(MacroAssembler* masm) {
// The right-hand value should not be on the stack---if it is a
// compiler-generated temporary it is in the accumulator.
ASSERT(!val1_->is_on_stack());
Comment cmnt(masm, "[ BinaryOpInstr");
// We can overwrite one of the operands if it is a temporary.
OverwriteMode mode = NO_OVERWRITE;
if (val0_->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
mode = OVERWRITE_RIGHT;
}
// Push both operands and call the specialized stub.
if (!val0_->is_on_stack()) {
val0_->Push(masm);
}
val1_->Push(masm);
GenericBinaryOpStub stub(op_, mode, SMI_CODE_IN_STUB);
__ CallStub(&stub);
loc_->Set(masm, rax);
}
void ReturnInstr::Compile(MacroAssembler* masm) { void ReturnInstr::Compile(MacroAssembler* masm) {
// The location should be 'Effect'. As a side effect, move the value to
// the accumulator.
Comment cmnt(masm, "[ ReturnInstr"); Comment cmnt(masm, "[ ReturnInstr");
value_->ToRegister(masm, rax); value_->Get(masm, rax);
} }
void Constant::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Get(MacroAssembler* masm, Register reg) {
__ Move(reg, handle_); __ Move(reg, handle_);
} }
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { void Constant::Push(MacroAssembler* masm) {
switch (type_) { __ Push(handle_);
}
static Operand ToOperand(SlotLocation* loc) {
switch (loc->type()) {
case Slot::PARAMETER: { case Slot::PARAMETER: {
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); int count = CfgGlobals::current()->fun()->scope()->num_parameters();
__ movq(reg, Operand(rbp, (1 + count - index_) * kPointerSize)); return Operand(rbp, (1 + count - loc->index()) * kPointerSize);
break;
} }
case Slot::LOCAL: { case Slot::LOCAL: {
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; const int kOffset = JavaScriptFrameConstants::kLocal0Offset;
__ movq(reg, Operand(rbp, kOffset - index_ * kPointerSize)); return Operand(rbp, kOffset - loc->index() * kPointerSize);
break;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
return Operand(rax, 0);
}
}
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ movq(reg, ToOperand(this));
}
void SlotLocation::Set(MacroAssembler* masm, Register reg) {
__ movq(ToOperand(this), reg);
}
void SlotLocation::Push(MacroAssembler* masm) {
__ push(ToOperand(this));
}
void TempLocation::Get(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(rax)) __ movq(reg, rax);
break;
case STACK:
__ pop(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
} }
} }
void TempLocation::Set(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
if (!reg.is(rax)) __ movq(rax, reg);
break;
case STACK:
__ push(reg);
break;
case NOWHERE:
UNREACHABLE();
break;
}
}
void TempLocation::Push(MacroAssembler* masm) {
switch (where_) {
case ACCUMULATOR:
__ push(rax);
break;
case STACK:
case NOWHERE:
UNREACHABLE();
break;
}
}
#undef __ #undef __
} } // namespace v8::internal } } // namespace v8::internal
...@@ -4534,14 +4534,6 @@ void CodeGenerator::Comparison(Condition cc, ...@@ -4534,14 +4534,6 @@ void CodeGenerator::Comparison(Condition cc,
} }
// Flag that indicates whether or not the code that handles smi arguments
// should be placed in the stub, inlined, or omitted entirely.
enum GenericBinaryFlags {
SMI_CODE_IN_STUB,
SMI_CODE_INLINED
};
class FloatingPointHelper : public AllStatic { class FloatingPointHelper : public AllStatic {
public: public:
// Code pattern for loading a floating point value. Input value must // Code pattern for loading a floating point value. Input value must
...@@ -4593,49 +4585,6 @@ class FloatingPointHelper : public AllStatic { ...@@ -4593,49 +4585,6 @@ class FloatingPointHelper : public AllStatic {
}; };
class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags)
: op_(op), mode_(mode), flags_(flags) {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
void GenerateSmiCode(MacroAssembler* masm, Label* slow);
private:
Token::Value op_;
OverwriteMode mode_;
GenericBinaryFlags flags_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_));
}
#endif
// Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 13> {};
class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
class DeferredInlineBinaryOperation: public DeferredCode { class DeferredInlineBinaryOperation: public DeferredCode {
public: public:
DeferredInlineBinaryOperation(Token::Value op, DeferredInlineBinaryOperation(Token::Value op,
......
...@@ -601,6 +601,57 @@ class CodeGenerator: public AstVisitor { ...@@ -601,6 +601,57 @@ class CodeGenerator: public AstVisitor {
}; };
// Flag that indicates whether or not the code that handles smi arguments
// should be placed in the stub, inlined, or omitted entirely.
enum GenericBinaryFlags {
SMI_CODE_IN_STUB,
SMI_CODE_INLINED
};
class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags)
: op_(op), mode_(mode), flags_(flags) {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
void GenerateSmiCode(MacroAssembler* masm, Label* slow);
private:
Token::Value op_;
OverwriteMode mode_;
GenericBinaryFlags flags_;
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_));
}
#endif
// Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 13> {};
class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_X64_CODEGEN_X64_H_ #endif // V8_X64_CODEGEN_X64_H_
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