| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 99fe33e92dec810595d1bec22182cee8e69b60b8..0cf8746cf8cc752bd760d7cbba55d44fcf0c1cf1 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -2360,13 +2360,6 @@ HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
|
| }
|
|
|
|
|
| -HSubgraph* HGraphBuilder::CreateEmptySubgraph() {
|
| - HSubgraph* subgraph = new HSubgraph(graph());
|
| - subgraph->Initialize(graph()->CreateBasicBlock());
|
| - return subgraph;
|
| -}
|
| -
|
| -
|
| HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
|
| HBasicBlock* header = graph()->CreateBasicBlock();
|
| HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
|
| @@ -2514,173 +2507,125 @@ void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
|
| }
|
|
|
|
|
| -HCompare* HGraphBuilder::BuildSwitchCompare(HSubgraph* subgraph,
|
| - HValue* switch_value,
|
| - CaseClause* clause) {
|
| - AddToSubgraph(subgraph, clause->label());
|
| - if (HasStackOverflow()) return NULL;
|
| - HValue* clause_value = subgraph->exit_block()->last_environment()->Pop();
|
| - HCompare* compare = new HCompare(switch_value,
|
| - clause_value,
|
| - Token::EQ_STRICT);
|
| - compare->SetInputRepresentation(Representation::Integer32());
|
| - subgraph->exit_block()->AddInstruction(compare);
|
| - return compare;
|
| -}
|
| -
|
| -
|
| void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| - VISIT_FOR_VALUE(stmt->tag());
|
| - // TODO(3168478): simulate added for tag should be enough.
|
| - AddSimulate(stmt->EntryId());
|
| - HValue* switch_value = Pop();
|
| -
|
| + // We only optimize switch statements with smi-literal smi comparisons,
|
| + // with a bounded number of clauses.
|
| + const int kCaseClauseLimit = 128;
|
| ZoneList<CaseClause*>* clauses = stmt->cases();
|
| - int num_clauses = clauses->length();
|
| - if (num_clauses == 0) return;
|
| - if (num_clauses > 128) BAILOUT("SwitchStatement: too many clauses");
|
| + int clause_count = clauses->length();
|
| + if (clause_count > kCaseClauseLimit) {
|
| + BAILOUT("SwitchStatement: too many clauses");
|
| + }
|
| +
|
| + VISIT_FOR_VALUE(stmt->tag());
|
| + HValue* tag_value = Pop();
|
| + HBasicBlock* first_test_block = current_block();
|
|
|
| - int num_smi_clauses = num_clauses;
|
| - for (int i = 0; i < num_clauses; i++) {
|
| + // 1. Build all the tests, with dangling true branches. Unconditionally
|
| + // deoptimize if we encounter a non-smi comparison.
|
| + for (int i = 0; i < clause_count; ++i) {
|
| CaseClause* clause = clauses->at(i);
|
| if (clause->is_default()) continue;
|
| - clause->RecordTypeFeedback(oracle());
|
| - if (!clause->IsSmiCompare()) {
|
| - if (i == 0) BAILOUT("SwitchStatement: no smi compares");
|
| - // We will deoptimize if the first non-smi compare is reached.
|
| - num_smi_clauses = i;
|
| - break;
|
| - }
|
| if (!clause->label()->IsSmiLiteral()) {
|
| BAILOUT("SwitchStatement: non-literal switch label");
|
| }
|
| - }
|
| -
|
| - // The single exit block of the whole switch statement.
|
| - HBasicBlock* single_exit_block = graph_->CreateBasicBlock();
|
| -
|
| - // Build a series of empty subgraphs for the comparisons.
|
| - // The default clause does not have a comparison subgraph.
|
| - ZoneList<HSubgraph*> compare_graphs(num_smi_clauses);
|
| - for (int i = 0; i < num_smi_clauses; i++) {
|
| - if (clauses->at(i)->is_default()) {
|
| - compare_graphs.Add(NULL);
|
| - } else {
|
| - compare_graphs.Add(CreateEmptySubgraph());
|
| - }
|
| - }
|
|
|
| - HSubgraph* prev_graph = current_subgraph_;
|
| - HCompare* prev_compare_inst = NULL;
|
| - for (int i = 0; i < num_smi_clauses; i++) {
|
| - CaseClause* clause = clauses->at(i);
|
| - if (clause->is_default()) continue;
|
| -
|
| - // Finish the previous graph by connecting it to the current.
|
| - HSubgraph* subgraph = compare_graphs.at(i);
|
| - if (prev_compare_inst == NULL) {
|
| - ASSERT(prev_graph == current_subgraph_);
|
| - prev_graph->exit_block()->Finish(new HGoto(subgraph->entry_block()));
|
| - } else {
|
| - HBasicBlock* empty = graph()->CreateBasicBlock();
|
| - prev_graph->exit_block()->Finish(new HTest(prev_compare_inst,
|
| - empty,
|
| - subgraph->entry_block()));
|
| + // Unconditionally deoptimize on the first non-smi compare.
|
| + clause->RecordTypeFeedback(oracle());
|
| + if (!clause->IsSmiCompare()) {
|
| + if (current_block() == first_test_block) {
|
| + // If the first test is the one that deopts and if the tag value is
|
| + // a phi, we need to have some use of that phi to prevent phi
|
| + // elimination from removing it. This HSimulate is such a use.
|
| + Push(tag_value);
|
| + AddSimulate(stmt->EntryId());
|
| + Drop(1);
|
| + }
|
| + current_block()->Finish(new HDeoptimize());
|
| + set_current_block(NULL);
|
| + break;
|
| }
|
|
|
| - // Build instructions for current subgraph.
|
| - ASSERT(clause->IsSmiCompare());
|
| - prev_compare_inst = BuildSwitchCompare(subgraph, switch_value, clause);
|
| - if (HasStackOverflow()) return;
|
| -
|
| - prev_graph = subgraph;
|
| - }
|
| -
|
| - // Finish last comparison if there was at least one comparison.
|
| - // last_false_block is the (empty) false-block of the last comparison. If
|
| - // there are no comparisons at all (a single default clause), it is just
|
| - // the last block of the current subgraph.
|
| - HBasicBlock* last_false_block = current_block();
|
| - if (prev_graph != current_subgraph_) {
|
| - last_false_block = graph()->CreateBasicBlock();
|
| - HBasicBlock* empty = graph()->CreateBasicBlock();
|
| - prev_graph->exit_block()->Finish(new HTest(prev_compare_inst,
|
| - empty,
|
| - last_false_block));
|
| - }
|
| -
|
| - // If we have a non-smi compare clause, we deoptimize after trying
|
| - // all the previous compares.
|
| - if (num_smi_clauses < num_clauses) {
|
| - last_false_block->Finish(new HDeoptimize);
|
| - }
|
| -
|
| - // Build statement blocks, connect them to their comparison block and
|
| - // to the previous statement block, if there is a fall-through.
|
| - HSubgraph* previous_subgraph = NULL;
|
| - for (int i = 0; i < num_clauses; i++) {
|
| - CaseClause* clause = clauses->at(i);
|
| - // Subgraph for the statements of the clause is only created when
|
| - // it's reachable either from the corresponding compare or as a
|
| - // fall-through from previous statements.
|
| - HSubgraph* subgraph = NULL;
|
| + // Otherwise generate a compare and branch.
|
| + VISIT_FOR_VALUE(clause->label());
|
| + HValue* label_value = Pop();
|
| + HCompare* compare = new HCompare(tag_value, label_value, Token::EQ_STRICT);
|
| + compare->SetInputRepresentation(Representation::Integer32());
|
| + ASSERT(!compare->HasSideEffects());
|
| + AddInstruction(compare);
|
| + HBasicBlock* body_block = graph()->CreateBasicBlock();
|
| + HBasicBlock* next_test_block = graph()->CreateBasicBlock();
|
| + HTest* branch = new HTest(compare, body_block, next_test_block);
|
| + current_block()->Finish(branch);
|
| + set_current_block(next_test_block);
|
| + }
|
| +
|
| + // Save the current block for to use for the default or to join with the
|
| + // exit. This block can be NULL if we deoptimized.
|
| + HBasicBlock* last_block = current_block();
|
| +
|
| + // 2. 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;
|
| + BreakAndContinueInfo break_info(stmt);
|
| + { BreakAndContinueScope push(&break_info, this);
|
| + for (int i = 0; i < clause_count; ++i) {
|
| + CaseClause* clause = clauses->at(i);
|
|
|
| - if (i < num_smi_clauses) {
|
| + // Identify the block where normal (non-fall-through) control flow
|
| + // comes from.
|
| + HBasicBlock* normal_block = NULL;
|
| if (clause->is_default()) {
|
| - if (!last_false_block->IsFinished()) {
|
| - // Default clause: Connect it to the last false block.
|
| - subgraph = CreateEmptySubgraph();
|
| - last_false_block->Finish(new HGoto(subgraph->entry_block()));
|
| + normal_block = last_block;
|
| + last_block = NULL; // Cleared to indicate we've handled it.
|
| + } else if (!curr_test_block->end()->IsDeoptimize()) {
|
| + normal_block = curr_test_block->end()->FirstSuccessor();
|
| + curr_test_block = curr_test_block->end()->SecondSuccessor();
|
| + }
|
| +
|
| + // Identify a block to emit the body into.
|
| + if (normal_block == NULL) {
|
| + if (fall_through_block == NULL) {
|
| + // (a) Unreachable.
|
| + continue;
|
| + } else {
|
| + // (b) Reachable only as fall through.
|
| + set_current_block(fall_through_block);
|
| }
|
| + } else if (fall_through_block == NULL) {
|
| + // (c) Reachable only normally.
|
| + set_current_block(normal_block);
|
| } else {
|
| - ASSERT(clause->IsSmiCompare());
|
| - // Connect with the corresponding comparison.
|
| - subgraph = CreateEmptySubgraph();
|
| - HBasicBlock* empty =
|
| - compare_graphs.at(i)->exit_block()->end()->FirstSuccessor();
|
| - empty->Finish(new HGoto(subgraph->entry_block()));
|
| + // (d) Reachable both ways.
|
| + HBasicBlock* join = CreateJoin(fall_through_block,
|
| + normal_block,
|
| + clause->EntryId());
|
| + set_current_block(join);
|
| }
|
| - }
|
|
|
| - // Check for fall-through from previous statement block.
|
| - if (previous_subgraph != NULL && previous_subgraph->exit_block() != NULL) {
|
| - if (subgraph == NULL) subgraph = CreateEmptySubgraph();
|
| - previous_subgraph->exit_block()->
|
| - Finish(new HGoto(subgraph->entry_block()));
|
| - }
|
| -
|
| - if (subgraph != NULL) {
|
| - BreakAndContinueInfo break_info(stmt);
|
| - { BreakAndContinueScope push(&break_info, this);
|
| - ADD_TO_SUBGRAPH(subgraph, clause->statements());
|
| - }
|
| - if (break_info.break_block() != NULL) {
|
| - break_info.break_block()->SetJoinId(stmt->ExitId());
|
| - break_info.break_block()->Finish(new HGoto(single_exit_block));
|
| - }
|
| + VisitStatements(clause->statements());
|
| + CHECK_BAILOUT;
|
| + fall_through_block = current_block();
|
| }
|
| -
|
| - previous_subgraph = subgraph;
|
| - }
|
| -
|
| - // If the last statement block has a fall-through, connect it to the
|
| - // single exit block.
|
| - if (previous_subgraph != NULL && previous_subgraph->exit_block() != NULL) {
|
| - previous_subgraph->exit_block()->Finish(new HGoto(single_exit_block));
|
| }
|
|
|
| - // If there is no default clause finish the last comparison's false target.
|
| - if (!last_false_block->IsFinished()) {
|
| - last_false_block->Finish(new HGoto(single_exit_block));
|
| - }
|
| -
|
| - if (single_exit_block->HasPredecessor()) {
|
| - set_current_block(single_exit_block);
|
| + // 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();
|
| + if (break_block == NULL) {
|
| + set_current_block(CreateJoin(fall_through_block,
|
| + last_block,
|
| + stmt->ExitId()));
|
| } else {
|
| - set_current_block(NULL);
|
| + if (fall_through_block != NULL) fall_through_block->Goto(break_block);
|
| + if (last_block != NULL) last_block->Goto(break_block);
|
| + break_block->SetJoinId(stmt->ExitId());
|
| + set_current_block(break_block);
|
| }
|
| }
|
|
|
| +
|
| bool HGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
|
| return statement->OsrEntryId() == info()->osr_ast_id();
|
| }
|
|
|