Chromium Code Reviews| Index: src/hydrogen.cc |
| =================================================================== |
| --- src/hydrogen.cc (revision 7814) |
| +++ src/hydrogen.cc (working copy) |
| @@ -4568,106 +4568,133 @@ |
| ASSERT(!HasStackOverflow()); |
| ASSERT(current_block() != NULL); |
| ASSERT(current_block()->HasPredecessor()); |
| - Token::Value op = expr->op(); |
| - if (op == Token::VOID) { |
| + switch (expr->op()) { |
| + case Token::DELETE: return VisitUnaryOperationDelete(expr); |
|
fschneider
2011/05/09 15:05:35
Indent all case-clauses by 2.
Sven Panne
2011/05/10 07:48:47
Ooops, google.el was accidentally commented out in
|
| + case Token::VOID: return VisitUnaryOperationVoid(expr); |
| + case Token::TYPEOF: return VisitUnaryOperationTypeof(expr); |
| + case Token::ADD: return VisitUnaryOperationAdd(expr); |
| + case Token::SUB: return VisitUnaryOperationSub(expr); |
| + case Token::BIT_NOT: return VisitUnaryOperationBitNot(expr); |
| + case Token::NOT: return VisitUnaryOperationNot(expr); |
| + default: UNREACHABLE(); |
| + } |
| +} |
| + |
| +void HGraphBuilder::VisitUnaryOperationDelete(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()->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()->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 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"); |
| + 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 (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)); |
| + } else if (var->is_global()) { |
| + Bailout("delete with global variable"); |
| + } else { |
| + Bailout("delete with non-global variable"); |
| + } |
| +} |
| - 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; |
| - } |
| +void HGraphBuilder::VisitUnaryOperationVoid(UnaryOperation* expr) { |
| + CHECK_ALIVE(VisitForEffect(expr->expression())); |
| + ast_context()->ReturnValue(graph()->GetConstantUndefined()); |
| +} |
| - 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()); |
| +void HGraphBuilder::VisitUnaryOperationTypeof(UnaryOperation* expr) { |
| + CHECK_ALIVE(VisitForTypeOf(expr->expression())); |
| + HValue* value = Pop(); |
| + ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id()); |
| +} |
| + |
| +void HGraphBuilder::VisitUnaryOperationAdd(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::VisitUnaryOperationSub(UnaryOperation* expr) { |
| + CHECK_ALIVE(VisitForValue(expr->expression())); |
| + HValue* value = Pop(); |
| + HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1()); |
| + ast_context()->ReturnInstruction(instr, expr->id()); |
| +} |
| + |
| + |
| +void HGraphBuilder::VisitUnaryOperationBitNot(UnaryOperation* expr) { |
| + CHECK_ALIVE(VisitForValue(expr->expression())); |
| + HValue* value = Pop(); |
| + HInstruction* instr = new(zone()) HBitNot(value); |
| + ast_context()->ReturnInstruction(instr, expr->id()); |
| +} |
| + |
| + |
| +void HGraphBuilder::VisitUnaryOperationNot(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 { |
| - 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()); |
| + 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()); |
| } |
| @@ -4834,54 +4861,7 @@ |
| 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 +4883,38 @@ |
| } |
| +HInstruction* HGraphBuilder::BuildBinaryOperation( |
| + Token::Value op, HValue* left, HValue* right, TypeInfo info) { |
| + switch (op) { |
| + case Token::ADD: |
|
fschneider
2011/05/09 15:05:35
Indent all case-clauses by 2.
Sven Panne
2011/05/10 07:48:47
Done.
|
| + 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 0; |
|
fschneider
2011/05/09 15:05:35
I'd return NULL instead of 0.
Sven Panne
2011/05/10 07:48:47
I think that each project has its own ideas about
|
| + } |
| +} |
| + |
| + |
| // Check for the form (%_ClassOf(foo) === 'BarClass'). |
| static bool IsClassOfTest(CompareOperation* expr) { |
| if (expr->op() != Token::EQ_STRICT) return false; |
| @@ -4921,110 +4933,122 @@ |
| 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 VisitBinaryOperationComma(expr); |
|
fschneider
2011/05/09 15:05:35
Indentation.
Sven Panne
2011/05/10 07:48:47
Done.
|
| + case Token::OR: return VisitBinaryOperationAndOr(expr, false); |
| + case Token::AND: return VisitBinaryOperationAndOr(expr, true); |
| + default: return VisitBinaryOperationCommon(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::VisitBinaryOperationComma(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); |
| +void HGraphBuilder::VisitBinaryOperationAndOr(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 { |
| + 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); |
| - Drop(1); // Value of the left subexpression. |
| - CHECK_BAILOUT(VisitForValue(expr->right())); |
| + Visit(expr->right()); |
| + } |
| - HBasicBlock* join_block = |
| - CreateJoin(empty_block, current_block(), expr->id()); |
| - set_current_block(join_block); |
| - ast_context()->ReturnValue(Pop()); |
| + } 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 { |
| - 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)); |
| - } |
| + 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; |
| - } |
| + // 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 (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; |
| - } |
| + if (empty_block->HasPredecessor()) { |
| + empty_block->SetJoinId(expr->id()); |
| + } else { |
| + empty_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. |
| + 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; |
| } |
| - } else { |
| - 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()); |
| + 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::VisitBinaryOperationCommon(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) { |