Chromium Code Reviews| Index: src/hydrogen.cc |
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
| index fdd9dfb5d27140767812d556006ca0509f46c31d..bbe58c52a12091b8677cd206d5f3d056683ad18a 100644 |
| --- a/src/hydrogen.cc |
| +++ b/src/hydrogen.cc |
| @@ -734,6 +734,7 @@ void HGraph::Postorder(HBasicBlock* block, |
| Postorder(it.Current(), visited, order, block); |
| } |
| } else { |
| + ASSERT(block->IsFinished()); |
| for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) { |
| Postorder(it.Current(), visited, order, loop_header); |
| } |
| @@ -2708,43 +2709,116 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
| return Bailout("SwitchStatement: too many clauses"); |
| } |
| + HValue* context = environment()->LookupContext(); |
| + |
| CHECK_ALIVE(VisitForValue(stmt->tag())); |
| AddSimulate(stmt->EntryId()); |
| HValue* tag_value = Pop(); |
| HBasicBlock* first_test_block = current_block(); |
| - // 1. Build all the tests, with dangling true branches. Unconditionally |
| - // deoptimize if we encounter a non-smi comparison. |
| + SwitchType switch_type = UNKNOWN_SWITCH; |
| + |
| + // 1. Extract clause type |
| for (int i = 0; i < clause_count; ++i) { |
| CaseClause* clause = clauses->at(i); |
| if (clause->is_default()) continue; |
| - if (!clause->label()->IsSmiLiteral()) { |
| - return Bailout("SwitchStatement: non-literal switch label"); |
| + |
| + if (switch_type == UNKNOWN_SWITCH) { |
| + if (clause->label()->IsSmiLiteral()) { |
| + switch_type = SMI_SWITCH; |
| + } else if (clause->label()->IsStringLiteral()) { |
| + switch_type = STRING_SWITCH; |
| + } else { |
| + return Bailout("SwitchStatement: non-literal switch label"); |
| + } |
| + } else if ((switch_type == STRING_SWITCH && |
| + !clause->label()->IsStringLiteral()) || |
| + (switch_type == SMI_SWITCH && |
| + !clause->label()->IsSmiLiteral())) { |
| + return Bailout("SwitchStatemnt: mixed label types are not supported"); |
| } |
| + } |
| - // Unconditionally deoptimize on the first non-smi compare. |
| - clause->RecordTypeFeedback(oracle()); |
| - if (!clause->IsSmiCompare()) { |
| - // Finish with deoptimize and add uses of enviroment values to |
| - // account for invisible uses. |
| - current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); |
| - set_current_block(NULL); |
| - break; |
| + HUnaryControlInstruction* string_check = NULL; |
| + HBasicBlock* not_string_block = NULL; |
| + |
| + int iterations = clause_count; |
| + |
| + // Test switch's tag value if all clauses are string literals |
| + if (switch_type == STRING_SWITCH) { |
| + // Bailout if we seen string(non-symbol) comparisons here |
| + for (int i = 0; i < clause_count; ++i) { |
| + CaseClause* clause = clauses->at(i); |
| + if (clause->is_default()) continue; |
| + |
| + clause->RecordTypeFeedback(oracle()); |
| + |
| + if (clause->IsStringCompare()) { |
| + return Bailout("SwitchStatement: no symbol comparisons expected"); |
|
fschneider
2011/11/07 15:34:00
I don't understand the reason for this bailout. I
indutny
2011/11/07 17:06:08
In many cases switch is used for parsing (like lex
|
| + } |
| } |
| - // Otherwise generate a compare and branch. |
| + // We'll generate two passes of checks |
| + iterations = iterations * 2; |
| + |
| + string_check = new(zone()) HIsStringAndBranch(tag_value); |
| + first_test_block = graph()->CreateBasicBlock(); |
| + not_string_block = graph()->CreateBasicBlock(); |
| + |
| + string_check->SetSuccessorAt(0, first_test_block); |
| + string_check->SetSuccessorAt(1, not_string_block); |
| + current_block()->Finish(string_check); |
| + |
| + set_current_block(first_test_block); |
| + } |
| + |
| + // 2. Build all the tests, with dangling true branches |
| + for (int i = 0; i < iterations; ++i) { |
| + CaseClause* clause = clauses->at(i % clause_count); |
| + if (clause->is_default()) continue; |
| + |
| + if (switch_type == SMI_SWITCH) { |
| + clause->RecordTypeFeedback(oracle()); |
| + } |
| + |
| + // Generate a compare and branch. |
| CHECK_ALIVE(VisitForValue(clause->label())); |
| HValue* label_value = Pop(); |
| - HCompareIDAndBranch* compare = |
| - new(zone()) HCompareIDAndBranch(tag_value, |
| - label_value, |
| - Token::EQ_STRICT); |
| - compare->SetInputRepresentation(Representation::Integer32()); |
| - HBasicBlock* body_block = graph()->CreateBasicBlock(); |
| + |
| HBasicBlock* next_test_block = graph()->CreateBasicBlock(); |
| + HBasicBlock* body_block = graph()->CreateBasicBlock(); |
| + |
| + HControlInstruction* compare; |
| + |
| + if (switch_type == SMI_SWITCH) { |
| + if (!clause->IsSmiCompare()) { |
| + // Finish with deoptimize and add uses of enviroment values to |
| + // account for invisible uses. |
| + current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); |
| + set_current_block(NULL); |
| + break; |
| + } |
| + |
| + HCompareIDAndBranch* compare_ = |
| + new(zone()) HCompareIDAndBranch(tag_value, |
| + label_value, |
| + Token::EQ_STRICT); |
| + compare_->SetInputRepresentation(Representation::Integer32()); |
| + compare = compare_; |
| + } else { |
| + if (i < clause_count) { |
| + compare = new(zone()) HCompareObjectEqAndBranch(tag_value, label_value); |
| + } else { |
| + compare = new(zone()) HStringCompareAndBranch(context, tag_value, |
| + label_value, |
| + Token::EQ_STRICT); |
| + } |
| + } |
| + |
| compare->SetSuccessorAt(0, body_block); |
| compare->SetSuccessorAt(1, next_test_block); |
| current_block()->Finish(compare); |
| + |
| set_current_block(next_test_block); |
| } |
| @@ -2752,14 +2826,26 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
| // exit. This block is NULL if we deoptimized. |
| HBasicBlock* last_block = current_block(); |
| - // 2. Loop over the clauses and the linked list of tests in lockstep, |
| + if (not_string_block != NULL) { |
| + last_block = CreateJoin(last_block, not_string_block, stmt->ExitId()); |
| + } |
| + |
| + // 3. Loop over the clauses and the linked list of tests in lockstep, |
| // translating the clause bodies. |
| HBasicBlock* curr_test_block = first_test_block; |
| HBasicBlock* fall_through_block = NULL; |
| + HBasicBlock* fall_through_block_left = NULL; |
| + |
| BreakAndContinueInfo break_info(stmt); |
| { BreakAndContinueScope push(&break_info, this); |
| - for (int i = 0; i < clause_count; ++i) { |
| - CaseClause* clause = clauses->at(i); |
| + for (int i = 0; i < iterations; ++i) { |
| + // Create shadow last block for second pass |
| + if (i == clause_count) { |
| + fall_through_block_left = fall_through_block; |
| + fall_through_block = NULL; |
| + } |
| + |
| + CaseClause* clause = clauses->at(i % clause_count); |
| // Identify the block where normal (non-fall-through) control flow |
| // goes to. |
| @@ -2803,6 +2889,10 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
| } |
| } |
| + // Join tails of parallel branches |
| + fall_through_block = CreateJoin(fall_through_block, fall_through_block_left, |
| + stmt->ExitId()); |
| + |
| // Create an up-to-3-way join. Use the break block if it exists since |
| // it's already a join block. |
| HBasicBlock* break_block = break_info.break_block(); |