A few "extract method" refactorings, trying to get individual method definitions

onto a sinlge 30" screen. A lot of times, the AST visitor stops a bit too early,
so we have to do the rest of the dispatch by hand. This is caused by the fact
that the kind of the AST nodes are a bit too coarse for some traversals (e.g. a
single node type for all binary ops), perhaps one could try to refine this a
little bit more.
Review URL: http://codereview.chromium.org/6963008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7839 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
svenpanne@chromium.org 2011-05-10 13:29:57 +00:00
parent 00e8236096
commit 4912c0371b
2 changed files with 283 additions and 239 deletions

View File

@ -4568,108 +4568,140 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
Token::Value op = expr->op();
if (op == Token::VOID) {
CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantUndefined());
} else if (op == Token::DELETE) {
Property* prop = expr->expression()->AsProperty();
Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
if (prop == NULL && var == NULL) {
// Result of deleting non-property, non-variable reference is true.
// Evaluate the subexpression for side effects.
CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantTrue());
} else if (var != NULL &&
!var->is_global() &&
var->AsSlot() != NULL &&
var->AsSlot()->type() != Slot::LOOKUP) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else if (prop != NULL) {
if (prop->is_synthetic()) {
// Result of deleting parameters is false, even when they rewrite
// to accesses on the arguments object.
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else {
CHECK_ALIVE(VisitForValue(prop->obj()));
CHECK_ALIVE(VisitForValue(prop->key()));
HValue* key = Pop();
HValue* obj = Pop();
HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
ast_context()->ReturnInstruction(instr, expr->id());
}
} else if (var->is_global()) {
return Bailout("delete with global variable");
} else {
return Bailout("delete with non-global variable");
}
} else if (op == Token::NOT) {
if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
VisitForControl(expr->expression(),
context->if_false(),
context->if_true());
} else if (ast_context()->IsValue()) {
HBasicBlock* materialize_false = graph()->CreateBasicBlock();
HBasicBlock* materialize_true = graph()->CreateBasicBlock();
CHECK_BAILOUT(VisitForControl(expr->expression(),
materialize_false,
materialize_true));
if (materialize_false->HasPredecessor()) {
materialize_false->SetJoinId(expr->expression()->id());
set_current_block(materialize_false);
Push(graph()->GetConstantFalse());
} else {
materialize_false = NULL;
}
if (materialize_true->HasPredecessor()) {
materialize_true->SetJoinId(expr->expression()->id());
set_current_block(materialize_true);
Push(graph()->GetConstantTrue());
} else {
materialize_true = NULL;
}
HBasicBlock* join =
CreateJoin(materialize_false, materialize_true, expr->id());
set_current_block(join);
if (join != NULL) ast_context()->ReturnValue(Pop());
} else {
ASSERT(ast_context()->IsEffect());
VisitForEffect(expr->expression());
}
} else if (op == Token::TYPEOF) {
CHECK_ALIVE(VisitForTypeOf(expr->expression()));
HValue* value = Pop();
ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
} else {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HInstruction* instr = NULL;
switch (op) {
case Token::BIT_NOT:
instr = new(zone()) HBitNot(value);
break;
case Token::SUB:
instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
break;
case Token::ADD:
instr = new(zone()) HMul(value, graph_->GetConstant1());
break;
default:
return Bailout("Value: unsupported unary operation");
break;
}
ast_context()->ReturnInstruction(instr, expr->id());
switch (expr->op()) {
case Token::DELETE: return VisitDelete(expr);
case Token::VOID: return VisitVoid(expr);
case Token::TYPEOF: return VisitTypeof(expr);
case Token::ADD: return VisitAdd(expr);
case Token::SUB: return VisitSub(expr);
case Token::BIT_NOT: return VisitBitNot(expr);
case Token::NOT: return VisitNot(expr);
default: UNREACHABLE();
}
}
void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
Property* prop = expr->expression()->AsProperty();
Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
if (prop == NULL && var == NULL) {
// Result of deleting non-property, non-variable reference is true.
// Evaluate the subexpression for side effects.
CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantTrue());
} else if (var != NULL &&
!var->is_global() &&
var->AsSlot() != NULL &&
var->AsSlot()->type() != Slot::LOOKUP) {
// Result of deleting non-global, non-dynamic variables is false.
// The subexpression does not have side effects.
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else if (prop != NULL) {
if (prop->is_synthetic()) {
// Result of deleting parameters is false, even when they rewrite
// to accesses on the arguments object.
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else {
CHECK_ALIVE(VisitForValue(prop->obj()));
CHECK_ALIVE(VisitForValue(prop->key()));
HValue* key = Pop();
HValue* obj = Pop();
HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
ast_context()->ReturnInstruction(instr, expr->id());
}
} else if (var->is_global()) {
Bailout("delete with global variable");
} else {
Bailout("delete with non-global variable");
}
}
void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantUndefined());
}
void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
CHECK_ALIVE(VisitForTypeOf(expr->expression()));
HValue* value = Pop();
ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
}
void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HInstruction* instr = new(zone()) HMul(value, graph_->GetConstant1());
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitSub(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
Representation rep = ToRepresentation(oracle()->UnaryType(expr));
PrintF("********************** Old: %s, New: %s\n", instr->representation().Mnemonic(), rep.Mnemonic());
AssumeRepresentation(instr, rep);
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HInstruction* instr = new(zone()) HBitNot(value);
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitNot(UnaryOperation* expr) {
// TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
VisitForControl(expr->expression(),
context->if_false(),
context->if_true());
return;
}
if (ast_context()->IsEffect()) {
VisitForEffect(expr->expression());
return;
}
ASSERT(ast_context()->IsValue());
HBasicBlock* materialize_false = graph()->CreateBasicBlock();
HBasicBlock* materialize_true = graph()->CreateBasicBlock();
CHECK_BAILOUT(VisitForControl(expr->expression(),
materialize_false,
materialize_true));
if (materialize_false->HasPredecessor()) {
materialize_false->SetJoinId(expr->expression()->id());
set_current_block(materialize_false);
Push(graph()->GetConstantFalse());
} else {
materialize_false = NULL;
}
if (materialize_true->HasPredecessor()) {
materialize_true->SetJoinId(expr->expression()->id());
set_current_block(materialize_true);
Push(graph()->GetConstantTrue());
} else {
materialize_true = NULL;
}
HBasicBlock* join =
CreateJoin(materialize_false, materialize_true, expr->id());
set_current_block(join);
if (join != NULL) ast_context()->ReturnValue(Pop());
}
HInstruction* HGraphBuilder::BuildIncrement(HValue* value,
bool increment,
@ -4834,54 +4866,7 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right) {
TypeInfo info = oracle()->BinaryType(expr);
HInstruction* instr = NULL;
switch (expr->op()) {
case Token::ADD:
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(new(zone()) HCheckInstanceType(
left, FIRST_STRING_TYPE, LAST_STRING_TYPE));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(new(zone()) HCheckInstanceType(
right, FIRST_STRING_TYPE, LAST_STRING_TYPE));
instr = new(zone()) HStringAdd(left, right);
} else {
instr = new(zone()) HAdd(left, right);
}
break;
case Token::SUB:
instr = new(zone()) HSub(left, right);
break;
case Token::MUL:
instr = new(zone()) HMul(left, right);
break;
case Token::MOD:
instr = new(zone()) HMod(left, right);
break;
case Token::DIV:
instr = new(zone()) HDiv(left, right);
break;
case Token::BIT_XOR:
instr = new(zone()) HBitXor(left, right);
break;
case Token::BIT_AND:
instr = new(zone()) HBitAnd(left, right);
break;
case Token::BIT_OR:
instr = new(zone()) HBitOr(left, right);
break;
case Token::SAR:
instr = new(zone()) HSar(left, right);
break;
case Token::SHR:
instr = new(zone()) HShr(left, right);
break;
case Token::SHL:
instr = new(zone()) HShl(left, right);
break;
default:
UNREACHABLE();
}
HInstruction* instr = BuildBinaryOperation(expr->op(), left, right, info);
// If we hit an uninitialized binary op stub we will get type info
// for a smi operation. If one of the operands is a constant string
// do not generate code assuming it is a smi operation.
@ -4903,6 +4888,38 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
}
HInstruction* HGraphBuilder::BuildBinaryOperation(
Token::Value op, HValue* left, HValue* right, TypeInfo info) {
switch (op) {
case Token::ADD:
if (info.IsString()) {
AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(new(zone()) HCheckInstanceType(left, FIRST_STRING_TYPE,
LAST_STRING_TYPE));
AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(new(zone()) HCheckInstanceType(right, FIRST_STRING_TYPE,
LAST_STRING_TYPE));
return new(zone()) HStringAdd(left, right);
} else {
return new(zone()) HAdd(left, right);
}
case Token::SUB: return new(zone()) HSub(left, right);
case Token::MUL: return new(zone()) HMul(left, right);
case Token::MOD: return new(zone()) HMod(left, right);
case Token::DIV: return new(zone()) HDiv(left, right);
case Token::BIT_XOR: return new(zone()) HBitXor(left, right);
case Token::BIT_AND: return new(zone()) HBitAnd(left, right);
case Token::BIT_OR: return new(zone()) HBitOr(left, right);
case Token::SAR: return new(zone()) HSar(left, right);
case Token::SHR: return new(zone()) HShr(left, right);
case Token::SHL: return new(zone()) HShl(left, right);
default:
UNREACHABLE();
return NULL;
}
}
// Check for the form (%_ClassOf(foo) === 'BarClass').
static bool IsClassOfTest(CompareOperation* expr) {
if (expr->op() != Token::EQ_STRICT) return false;
@ -4921,110 +4938,121 @@ void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
if (expr->op() == Token::COMMA) {
CHECK_ALIVE(VisitForEffect(expr->left()));
// Visit the right subexpression in the same AST context as the entire
// expression.
Visit(expr->right());
switch (expr->op()) {
case Token::COMMA: return VisitComma(expr);
case Token::OR: return VisitAndOr(expr, false);
case Token::AND: return VisitAndOr(expr, true);
default: return VisitCommon(expr);
}
}
} else if (expr->op() == Token::AND || expr->op() == Token::OR) {
bool is_logical_and = (expr->op() == Token::AND);
if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
// Translate left subexpression.
HBasicBlock* eval_right = graph()->CreateBasicBlock();
if (is_logical_and) {
CHECK_BAILOUT(VisitForControl(expr->left(),
eval_right,
context->if_false()));
} else {
CHECK_BAILOUT(VisitForControl(expr->left(),
context->if_true(),
eval_right));
}
// Translate right subexpression by visiting it in the same AST
// context as the entire expression.
if (eval_right->HasPredecessor()) {
eval_right->SetJoinId(expr->RightId());
set_current_block(eval_right);
Visit(expr->right());
}
void HGraphBuilder::VisitComma(BinaryOperation* expr) {
CHECK_ALIVE(VisitForEffect(expr->left()));
// Visit the right subexpression in the same AST context as the entire
// expression.
Visit(expr->right());
}
} else if (ast_context()->IsValue()) {
CHECK_ALIVE(VisitForValue(expr->left()));
ASSERT(current_block() != NULL);
// We need an extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* eval_right = graph()->CreateBasicBlock();
HTest* test = is_logical_and
? new(zone()) HTest(Top(), eval_right, empty_block)
: new(zone()) HTest(Top(), empty_block, eval_right);
current_block()->Finish(test);
set_current_block(eval_right);
Drop(1); // Value of the left subexpression.
CHECK_BAILOUT(VisitForValue(expr->right()));
HBasicBlock* join_block =
CreateJoin(empty_block, current_block(), expr->id());
set_current_block(join_block);
ast_context()->ReturnValue(Pop());
void HGraphBuilder::VisitAndOr(BinaryOperation* expr, bool is_logical_and) {
if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
// Translate left subexpression.
HBasicBlock* eval_right = graph()->CreateBasicBlock();
if (is_logical_and) {
CHECK_BAILOUT(VisitForControl(expr->left(),
eval_right,
context->if_false()));
} else {
ASSERT(ast_context()->IsEffect());
// In an effect context, we don't need the value of the left
// subexpression, only its control flow and side effects. We need an
// extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* right_block = graph()->CreateBasicBlock();
if (is_logical_and) {
CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
} else {
CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
}
// TODO(kmillikin): Find a way to fix this. It's ugly that there are
// actually two empty blocks (one here and one inserted by
// TestContext::BuildBranch, and that they both have an HSimulate
// though the second one is not a merge node, and that we really have
// no good AST ID to put on that first HSimulate.
if (empty_block->HasPredecessor()) {
empty_block->SetJoinId(expr->id());
} else {
empty_block = NULL;
}
if (right_block->HasPredecessor()) {
right_block->SetJoinId(expr->RightId());
set_current_block(right_block);
CHECK_BAILOUT(VisitForEffect(expr->right()));
right_block = current_block();
} else {
right_block = NULL;
}
HBasicBlock* join_block =
CreateJoin(empty_block, right_block, expr->id());
set_current_block(join_block);
// We did not materialize any value in the predecessor environments,
// so there is no need to handle it here.
CHECK_BAILOUT(VisitForControl(expr->left(),
context->if_true(),
eval_right));
}
} else {
CHECK_ALIVE(VisitForValue(expr->left()));
CHECK_ALIVE(VisitForValue(expr->right()));
// Translate right subexpression by visiting it in the same AST
// context as the entire expression.
if (eval_right->HasPredecessor()) {
eval_right->SetJoinId(expr->RightId());
set_current_block(eval_right);
Visit(expr->right());
}
HValue* right = Pop();
HValue* left = Pop();
HInstruction* instr = BuildBinaryOperation(expr, left, right);
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
} else if (ast_context()->IsValue()) {
CHECK_ALIVE(VisitForValue(expr->left()));
ASSERT(current_block() != NULL);
// We need an extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* eval_right = graph()->CreateBasicBlock();
HTest* test = is_logical_and
? new(zone()) HTest(Top(), eval_right, empty_block)
: new(zone()) HTest(Top(), empty_block, eval_right);
current_block()->Finish(test);
set_current_block(eval_right);
Drop(1); // Value of the left subexpression.
CHECK_BAILOUT(VisitForValue(expr->right()));
HBasicBlock* join_block =
CreateJoin(empty_block, current_block(), expr->id());
set_current_block(join_block);
ast_context()->ReturnValue(Pop());
} else {
ASSERT(ast_context()->IsEffect());
// In an effect context, we don't need the value of the left subexpression,
// only its control flow and side effects. We need an extra block to
// maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* right_block = graph()->CreateBasicBlock();
if (is_logical_and) {
CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
} else {
CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
}
// TODO(kmillikin): Find a way to fix this. It's ugly that there are
// actually two empty blocks (one here and one inserted by
// TestContext::BuildBranch, and that they both have an HSimulate though the
// second one is not a merge node, and that we really have no good AST ID to
// put on that first HSimulate.
if (empty_block->HasPredecessor()) {
empty_block->SetJoinId(expr->id());
} else {
empty_block = NULL;
}
if (right_block->HasPredecessor()) {
right_block->SetJoinId(expr->RightId());
set_current_block(right_block);
CHECK_BAILOUT(VisitForEffect(expr->right()));
right_block = current_block();
} else {
right_block = NULL;
}
HBasicBlock* join_block =
CreateJoin(empty_block, right_block, expr->id());
set_current_block(join_block);
// We did not materialize any value in the predecessor environments,
// so there is no need to handle it here.
}
}
void HGraphBuilder::VisitCommon(BinaryOperation* expr) {
CHECK_ALIVE(VisitForValue(expr->left()));
CHECK_ALIVE(VisitForValue(expr->right()));
HValue* right = Pop();
HValue* left = Pop();
HInstruction* instr = BuildBinaryOperation(expr, left, right);
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::AssumeRepresentation(HValue* value, Representation r) {
if (value->CheckFlag(HValue::kFlexibleRepresentation)) {
if (FLAG_trace_representation) {

View File

@ -735,6 +735,18 @@ class HGraphBuilder: public AstVisitor {
INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
#undef INLINE_FUNCTION_GENERATOR_DECLARATION
void VisitDelete(UnaryOperation* expr);
void VisitVoid(UnaryOperation* expr);
void VisitTypeof(UnaryOperation* expr);
void VisitAdd(UnaryOperation* expr);
void VisitSub(UnaryOperation* expr);
void VisitBitNot(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
void VisitComma(BinaryOperation* expr);
void VisitAndOr(BinaryOperation* expr, bool is_logical_and);
void VisitCommon(BinaryOperation* expr);
void PreProcessOsrEntry(IterationStatement* statement);
// True iff. we are compiling for OSR and the statement is the entry.
bool HasOsrEntryAt(IterationStatement* statement);
@ -848,6 +860,10 @@ class HGraphBuilder: public AstVisitor {
HInstruction* BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right);
HInstruction* BuildBinaryOperation(Token::Value op,
HValue* left,
HValue* right,
TypeInfo info);
HInstruction* BuildIncrement(HValue* value,
bool increment,
CountOperation* expr);