Add support for (some) assignment expressions to the CFG builder and

fast-mode compiler.

1. We avoid generating a useless temporary for assignments with
nontrivial right-hand sides.  Instead of translating id = expr into:

...
tmp = <last expr instruction>
id = tmp

we generate directly

...
id = <last expr instruction>

by passing a data destination ('hint') down the AST.  The semantics is
to use the destination as a result location if a temp is needed.  It
may be ignored.  NULL indicates I don't care and you should generate a
temp.

2. We correctly handle assignments as subexpressions.  When building
the CFG for an expression we accumulate the assigned variables and we
emit a move to a fresh temporary if a value in a variable is in
jeopardy of being overwritten.

Review URL: http://codereview.chromium.org/165056

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2643 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bcbef79a
......@@ -108,26 +108,31 @@ void PositionInstr::Compile(MacroAssembler* masm) {
}
void MoveInstr::Compile(MacroAssembler* masm) {
location()->Move(masm, value());
}
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());
ASSERT(!value1()->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()) {
if (value0()->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
} else if (value1()->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);
value0()->Get(masm, r1);
value1()->Get(masm, r0);
GenericBinaryOpStub stub(op(), mode);
__ CallStub(&stub);
loc_->Set(masm, r0);
location()->Set(masm, r0);
}
......@@ -167,6 +172,12 @@ static MemOperand ToMemOperand(SlotLocation* loc) {
}
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
__ mov(ip, Operand(handle_));
__ str(ip, ToMemOperand(loc));
}
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ ldr(reg, ToMemOperand(this));
}
......@@ -183,6 +194,18 @@ void SlotLocation::Push(MacroAssembler* masm) {
}
void SlotLocation::Move(MacroAssembler* masm, Value* value) {
// Double dispatch.
value->MoveToSlot(masm, this);
}
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
__ ldr(ip, ToMemOperand(this));
__ str(ip, ToMemOperand(loc));
}
void TempLocation::Get(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
......@@ -191,9 +214,8 @@ void TempLocation::Get(MacroAssembler* masm, Register reg) {
case STACK:
__ pop(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -206,9 +228,8 @@ void TempLocation::Set(MacroAssembler* masm, Register reg) {
case STACK:
__ push(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -219,13 +240,38 @@ void TempLocation::Push(MacroAssembler* masm) {
__ push(r0);
break;
case STACK:
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::Move(MacroAssembler* masm, Value* value) {
switch (where_) {
case ACCUMULATOR:
value->Get(masm, r0);
case STACK:
value->Push(masm);
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
switch (where_) {
case ACCUMULATOR:
__ str(r0, ToMemOperand(loc));
case STACK:
__ pop(ip);
__ str(ip, ToMemOperand(loc));
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
#undef __
} } // namespace v8::internal
......@@ -42,7 +42,7 @@ CfgGlobals* CfgGlobals::top_ = NULL;
CfgGlobals::CfgGlobals(FunctionLiteral* fun)
: global_fun_(fun),
global_exit_(new ExitNode()),
effect_(new Effect()),
nowhere_(new Nowhere()),
#ifdef DEBUG
node_counter_(0),
temp_counter_(0),
......@@ -60,6 +60,12 @@ Cfg* Cfg::Build() {
if (fun->scope()->num_heap_slots() > 0) {
BAILOUT("function has context slots");
}
if (fun->scope()->num_stack_slots() > kPointerSize) {
BAILOUT("function has too many locals");
}
if (fun->scope()->num_parameters() > kPointerSize - 1) {
BAILOUT("function has too many parameters");
}
if (fun->scope()->arguments() != NULL) {
BAILOUT("function uses .arguments");
}
......@@ -71,18 +77,18 @@ Cfg* Cfg::Build() {
StatementBuilder builder;
builder.VisitStatements(body);
Cfg* cfg = builder.cfg();
if (cfg == NULL) {
Cfg* graph = builder.graph();
if (graph == NULL) {
BAILOUT("unsupported statement type");
}
if (cfg->is_empty()) {
if (graph->is_empty()) {
BAILOUT("function body produces empty cfg");
}
if (cfg->has_exit()) {
if (graph->has_exit()) {
BAILOUT("control path without explicit return");
}
cfg->PrependEntryNode();
return cfg;
graph->PrependEntryNode();
return graph;
}
#undef BAILOUT
......@@ -194,9 +200,19 @@ Handle<Code> Cfg::Compile(Handle<Script> script) {
}
void MoveInstr::FastAllocate(TempLocation* temp) {
ASSERT(temp->where() == TempLocation::NOT_ALLOCATED);
if (temp == value()) {
temp->set_where(TempLocation::ACCUMULATOR);
} else {
temp->set_where(TempLocation::STACK);
}
}
void BinaryOpInstr::FastAllocate(TempLocation* temp) {
ASSERT(temp->where() == TempLocation::NOWHERE);
if (temp == val0_ || temp == val1_) {
ASSERT(temp->where() == TempLocation::NOT_ALLOCATED);
if (temp == value0() || temp == value1()) {
temp->set_where(TempLocation::ACCUMULATOR);
} else {
temp->set_where(TempLocation::STACK);
......@@ -205,7 +221,7 @@ void BinaryOpInstr::FastAllocate(TempLocation* temp) {
void ReturnInstr::FastAllocate(TempLocation* temp) {
ASSERT(temp->where() == TempLocation::NOWHERE);
ASSERT(temp->where() == TempLocation::NOT_ALLOCATED);
if (temp == value_) {
temp->set_where(TempLocation::ACCUMULATOR);
} else {
......@@ -226,7 +242,7 @@ STATEMENT_NODE_LIST(DEFINE_VISIT)
// Macros (temporarily) handling unsupported expression types.
#define BAILOUT(reason) \
do { \
cfg_ = NULL; \
graph_ = NULL; \
return; \
} while (false)
......@@ -260,11 +276,13 @@ void ExpressionBuilder::VisitVariableProxy(VariableProxy* expr) {
if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) {
BAILOUT("unsupported slot type (not a parameter or local)");
}
// Ignore the passed destination.
value_ = new SlotLocation(slot->type(), slot->index());
}
void ExpressionBuilder::VisitLiteral(Literal* expr) {
// Ignore the passed destination.
value_ = new Constant(expr->handle());
}
......@@ -290,7 +308,42 @@ void ExpressionBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
void ExpressionBuilder::VisitAssignment(Assignment* expr) {
BAILOUT("Assignment");
if (expr->op() != Token::ASSIGN && expr->op() != Token::INIT_VAR) {
BAILOUT("unsupported compound assignment");
}
Expression* lhs = expr->target();
if (lhs->AsProperty() != NULL) {
BAILOUT("unsupported property assignment");
}
Variable* var = lhs->AsVariableProxy()->AsVariable();
if (var == NULL) {
BAILOUT("unsupported invalid left-hand side");
}
if (var->is_global()) {
BAILOUT("unsupported global variable");
}
Slot* slot = var->slot();
ASSERT(slot != NULL);
if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) {
BAILOUT("unsupported slot lhs (not a parameter or local)");
}
ExpressionBuilder builder;
SlotLocation* loc = new SlotLocation(slot->type(), slot->index());
builder.Build(expr->value(), loc);
if (builder.graph() == NULL) {
BAILOUT("unsupported expression in assignment");
}
// If the expression did not come back in the slot location, append
// a move to the CFG.
graph_ = builder.graph();
if (builder.value() != loc) {
graph()->Append(new MoveInstr(loc, builder.value()));
}
// Record the assignment.
assigned_vars_.AddElement(loc);
// Ignore the destination passed to us.
value_ = loc;
}
......@@ -354,21 +407,35 @@ void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) {
case Token::DIV:
case Token::MOD: {
ExpressionBuilder left, right;
left.Build(expr->left());
if (left.cfg() == NULL) {
left.Build(expr->left(), NULL);
if (left.graph() == NULL) {
BAILOUT("unsupported left subexpression in binop");
}
right.Build(expr->right());
if (right.cfg() == NULL) {
right.Build(expr->right(), NULL);
if (right.graph() == 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()));
if (destination_ == NULL) destination_ = new TempLocation();
graph_ = left.graph();
// Insert a move to a fresh temporary if the left value is in a
// slot that's assigned on the right.
Location* temp = NULL;
if (left.value()->is_slot() &&
right.assigned_vars()->Contains(SlotLocation::cast(left.value()))) {
temp = new TempLocation();
graph()->Append(new MoveInstr(temp, left.value()));
}
graph()->Concatenate(right.graph());
graph()->Append(new BinaryOpInstr(destination_, op,
temp == NULL ? left.value() : temp,
right.value()));
assigned_vars_ = *left.assigned_vars();
assigned_vars()->Union(right.assigned_vars());
value_ = temp;
value_ = destination_;
return;
}
......@@ -393,18 +460,18 @@ void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) {
// Macros (temporarily) handling unsupported statement types.
#define BAILOUT(reason) \
do { \
cfg_ = NULL; \
graph_ = NULL; \
return; \
} while (false)
#define CHECK_BAILOUT() \
if (cfg_ == NULL) { return; } else {}
if (graph() == NULL) { return; } else {}
void StatementBuilder::VisitStatements(ZoneList<Statement*>* stmts) {
for (int i = 0, len = stmts->length(); i < len; i++) {
Visit(stmts->at(i));
CHECK_BAILOUT();
if (!cfg_->has_exit()) return;
if (!graph()->has_exit()) return;
}
}
......@@ -425,19 +492,12 @@ void StatementBuilder::VisitBlock(Block* stmt) {
void StatementBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
ExpressionBuilder builder;
builder.Build(stmt->expression());
if (builder.cfg() == NULL) {
builder.Build(stmt->expression(), CfgGlobals::current()->nowhere());
if (builder.graph() == NULL) {
BAILOUT("unsupported expression in expression statement");
}
// Here's a temporary hack: we bang on the last instruction of the
// expression (if any) to set its location to Effect.
if (!builder.cfg()->is_empty()) {
InstructionBlock* block = InstructionBlock::cast(builder.cfg()->exit());
Instruction* instr = block->instructions()->last();
instr->set_location(CfgGlobals::current()->effect_location());
}
cfg_->Append(new PositionInstr(stmt->statement_pos()));
cfg_->Concatenate(builder.cfg());
graph()->Append(new PositionInstr(stmt->statement_pos()));
graph()->Concatenate(builder.graph());
}
......@@ -463,14 +523,14 @@ void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) {
void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) {
ExpressionBuilder builder;
builder.Build(stmt->expression());
if (builder.cfg() == NULL) {
builder.Build(stmt->expression(), NULL);
if (builder.graph() == NULL) {
BAILOUT("unsupported expression in return statement");
}
cfg_->Append(new PositionInstr(stmt->statement_pos()));
cfg_->Concatenate(builder.cfg());
cfg_->AppendReturnInstruction(builder.value());
graph()->Append(new PositionInstr(stmt->statement_pos()));
graph()->Concatenate(builder.graph());
graph()->AppendReturnInstruction(builder.value());
}
......@@ -530,8 +590,8 @@ void Constant::Print() {
}
void Effect::Print() {
PrintF("Effect");
void Nowhere::Print() {
PrintF("Nowhere");
}
......@@ -555,13 +615,22 @@ void TempLocation::Print() {
}
void MoveInstr::Print() {
PrintF("Move(");
location()->Print();
PrintF(", ");
value_->Print();
PrintF(")\n");
}
void BinaryOpInstr::Print() {
PrintF("BinaryOp(");
loc_->Print();
PrintF(", %s, ", Token::Name(op_));
val0_->Print();
location()->Print();
PrintF(", %s, ", Token::Name(op()));
value0()->Print();
PrintF(", ");
val1_->Print();
value1()->Print();
PrintF(")\n");
}
......
This diff is collapsed.
......@@ -121,28 +121,33 @@ void PositionInstr::Compile(MacroAssembler* masm) {
}
void MoveInstr::Compile(MacroAssembler* masm) {
location()->Move(masm, value());
}
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());
ASSERT(!value1()->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()) {
if (value0()->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
} else if (value1()->is_temporary()) {
mode = OVERWRITE_RIGHT;
}
// Push both operands and call the specialized stub.
if (!val0_->is_on_stack()) {
val0_->Push(masm);
if (!value0()->is_on_stack()) {
value0()->Push(masm);
}
val1_->Push(masm);
GenericBinaryOpStub stub(op_, mode, SMI_CODE_IN_STUB);
value1()->Push(masm);
GenericBinaryOpStub stub(op(), mode, SMI_CODE_IN_STUB);
__ CallStub(&stub);
loc_->Set(masm, eax);
location()->Set(masm, eax);
}
......@@ -181,6 +186,11 @@ static Operand ToOperand(SlotLocation* loc) {
}
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
__ mov(ToOperand(loc), Immediate(handle_));
}
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ mov(reg, ToOperand(this));
}
......@@ -196,6 +206,19 @@ void SlotLocation::Push(MacroAssembler* masm) {
}
void SlotLocation::Move(MacroAssembler* masm, Value* value) {
// We dispatch to the value because in some cases (temp or constant)
// we can use a single instruction.
value->MoveToSlot(masm, this);
}
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
// The accumulator is not live across a MoveInstr.
__ mov(eax, ToOperand(this));
__ mov(ToOperand(loc), eax);
}
void TempLocation::Get(MacroAssembler* masm, Register reg) {
switch (where_) {
case ACCUMULATOR:
......@@ -204,9 +227,8 @@ void TempLocation::Get(MacroAssembler* masm, Register reg) {
case STACK:
__ pop(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -219,9 +241,8 @@ void TempLocation::Set(MacroAssembler* masm, Register reg) {
case STACK:
__ push(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -232,9 +253,36 @@ void TempLocation::Push(MacroAssembler* masm) {
__ push(eax);
break;
case STACK:
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::Move(MacroAssembler* masm, Value* value) {
switch (where_) {
case ACCUMULATOR:
value->Get(masm, eax);
break;
case STACK:
value->Push(masm);
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
switch (where_) {
case ACCUMULATOR:
__ mov(ToOperand(loc), eax);
break;
case STACK:
__ pop(ToOperand(loc));
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
......
......@@ -131,28 +131,33 @@ void PositionInstr::Compile(MacroAssembler* masm) {
}
void MoveInstr::Compile(MacroAssembler* masm) {
location()->Move(masm, value());
}
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());
ASSERT(!value1()->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()) {
if (value0()->is_temporary()) {
mode = OVERWRITE_LEFT;
} else if (val1_->is_temporary()) {
} else if (value1()->is_temporary()) {
mode = OVERWRITE_RIGHT;
}
// Push both operands and call the specialized stub.
if (!val0_->is_on_stack()) {
val0_->Push(masm);
if (!value0()->is_on_stack()) {
value0()->Push(masm);
}
val1_->Push(masm);
GenericBinaryOpStub stub(op_, mode, SMI_CODE_IN_STUB);
value1()->Push(masm);
GenericBinaryOpStub stub(op(), mode, SMI_CODE_IN_STUB);
__ CallStub(&stub);
loc_->Set(masm, rax);
location()->Set(masm, rax);
}
......@@ -191,6 +196,11 @@ static Operand ToOperand(SlotLocation* loc) {
}
void Constant::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
__ Move(ToOperand(loc), handle_);
}
void SlotLocation::Get(MacroAssembler* masm, Register reg) {
__ movq(reg, ToOperand(this));
}
......@@ -201,6 +211,19 @@ void SlotLocation::Set(MacroAssembler* masm, Register reg) {
}
void SlotLocation::Move(MacroAssembler* masm, Value* value) {
// We dispatch to the value because in some cases (temp or constant) we
// can use special instruction sequences.
value->MoveToSlot(masm, this);
}
void SlotLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
__ movq(kScratchRegister, ToOperand(this));
__ movq(ToOperand(loc), kScratchRegister);
}
void SlotLocation::Push(MacroAssembler* masm) {
__ push(ToOperand(this));
}
......@@ -214,9 +237,8 @@ void TempLocation::Get(MacroAssembler* masm, Register reg) {
case STACK:
__ pop(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -229,9 +251,8 @@ void TempLocation::Set(MacroAssembler* masm, Register reg) {
case STACK:
__ push(reg);
break;
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
break;
}
}
......@@ -242,9 +263,36 @@ void TempLocation::Push(MacroAssembler* masm) {
__ push(rax);
break;
case STACK:
case NOWHERE:
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::Move(MacroAssembler* masm, Value* value) {
switch (where_) {
case ACCUMULATOR:
value->Get(masm, rax);
break;
case STACK:
value->Push(masm);
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
void TempLocation::MoveToSlot(MacroAssembler* masm, SlotLocation* loc) {
switch (where_) {
case ACCUMULATOR:
__ movq(ToOperand(loc), rax);
break;
case STACK:
__ pop(ToOperand(loc));
break;
case NOT_ALLOCATED:
UNREACHABLE();
}
}
......
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