Chromium Code Reviews| Index: runtime/vm/flow_graph_builder.cc |
| =================================================================== |
| --- runtime/vm/flow_graph_builder.cc (revision 6099) |
| +++ runtime/vm/flow_graph_builder.cc (working copy) |
| @@ -701,12 +701,144 @@ |
| void EffectGraphVisitor::VisitSwitchNode(SwitchNode* node) { |
| - Bailout("EffectGraphVisitor::VisitSwitchNode"); |
| + EffectGraphVisitor switch_body(owner(), temp_index()); |
| + node->body()->Visit(&switch_body); |
| + Append(switch_body); |
| + if ((node->label() != NULL) && (node->label()->join_for_break() != NULL)) { |
| + if (is_open()) { |
| + AddInstruction(node->label()->join_for_break()); |
| + } else { |
| + exit_ = node->label()->join_for_break(); |
| + } |
| + } |
| + // No continue label allowed. |
| + ASSERT((node->label() == NULL) || |
| + (node->label()->join_for_continue() == NULL)); |
| } |
| +// A case node contains zero or more case expressions, can contain default |
| +// and a case statement body. |
| +// Compose fragment as follows: |
| +// - if no case expressions, must have default: |
| +// a) target |
| +// b) [ case-statements ] |
| +// |
| +// - if has 1 or more case statements |
| +// a) target-0 |
| +// b) [ case-expression-0 ] -> (true-target-0, target-1) |
| +// c) target-1 |
| +// d) [ case-expression-1 ] -> (true-target-1, exit-target) |
| +// e) true-target-0 -> case-statements-join |
| +// f) true-target-1 -> case-statements-join |
| +// g) case-statements-join |
| +// h) [ case-statements ] -> exit-join |
| +// i) exit-target -> exit-join |
| +// j) exit-join |
| +// |
| +// Note: The specification of switch/case is under discussion and may change |
| +// drastically. |
| void EffectGraphVisitor::VisitCaseNode(CaseNode* node) { |
| - Bailout("EffectGraphVisitor::VisitCaseNode"); |
| + const intptr_t len = node->case_expressions()->length(); |
| + // Create case statements instructions. |
| + const bool needs_join_at_statement_entry = |
| + (len > 1) || ((len > 0) && (node->contains_default())); |
| + EffectGraphVisitor for_case_statements(owner(), temp_index()); |
| + // Compute start of statements fragment. |
| + BlockEntryInstr* statement_start = NULL; |
| + if ((node->label() != NULL) && (node->label()->is_continue_target())) { |
| + // Since a labeled jump continue statement occur in a different case node, |
| + // allocate JoinNode here and use it as statement start. |
| + if (node->label()->join_for_continue() == NULL) { |
| + node->label()->set_join_for_continue(new JoinEntryInstr()); |
| + } |
| + statement_start = node->label()->join_for_continue(); |
| + } else if (needs_join_at_statement_entry) { |
| + statement_start = new JoinEntryInstr(); |
| + } else { |
| + statement_start = new TargetEntryInstr(); |
| + } |
| + for_case_statements.AddInstruction(statement_start); |
| + node->statements()->Visit(&for_case_statements); |
| + if (is_open() && (len == 0)) { |
| + ASSERT(node->contains_default()); |
| + // Default only case node. |
| + Append(for_case_statements); |
| + return; |
| + } |
| + |
| + // Generate instructions for all case expressions and collect data to |
| + // connect them. |
| + GrowableArray<TargetEntryInstr**> case_true_addresses; |
| + GrowableArray<TargetEntryInstr**> case_false_addresses; |
| + GrowableArray<TargetEntryInstr*> case_entries; |
| + for (intptr_t i = 0; i < len; i++) { |
| + AstNode* case_expr = node->case_expressions()->NodeAt(i); |
| + TestGraphVisitor for_case_expression(owner(), temp_index()); |
| + if (i == 0) { |
| + case_entries.Add(NULL); // Not to be used |
| + case_expr->Visit(&for_case_expression); |
| + // Append only the first one, everything else is connected from it. |
| + Append(for_case_expression); |
| + } else { |
| + TargetEntryInstr* case_entry_target = new TargetEntryInstr(); |
| + case_entries.Add(case_entry_target); |
| + for_case_expression.AddInstruction(case_entry_target); |
| + case_expr->Visit(&for_case_expression); |
| + } |
| + case_true_addresses.Add(for_case_expression.true_successor_address()); |
| + case_false_addresses.Add(for_case_expression.false_successor_address()); |
| + } |
| + |
| + // Once a test fragment has been added, this fragment is closed. |
| + ASSERT(!is_open()); |
|
regis
2012/04/04 01:31:08
Can you move this ASSERT up inside the loop? Each
srdjan
2012/04/04 17:33:00
Discussed offline, leaving it here. Checking that
|
| + |
| + // Connect all test cases except the last one. |
| + for (intptr_t i = 0; i < (len - 1); i++) { |
| + ASSERT(needs_join_at_statement_entry); |
| + *case_false_addresses[i] = case_entries[i + 1]; |
| + TargetEntryInstr* true_target = new TargetEntryInstr(); |
| + *case_true_addresses[i] = true_target; |
| + true_target->SetSuccessor(statement_start); |
| + } |
| + |
| + BlockEntryInstr* exit_instruction = NULL; |
| + // Handle last (or only) case: false goes to exit or to statement if this |
| + // node contains default. |
| + if (len > 0) { |
| + if (statement_start->IsTargetEntry()) { |
| + *case_true_addresses[len - 1] = statement_start->AsTargetEntry(); |
| + } else { |
| + TargetEntryInstr* true_target = new TargetEntryInstr(); |
| + *case_true_addresses[len - 1] = true_target; |
| + true_target->SetSuccessor(statement_start); |
| + } |
| + TargetEntryInstr* false_target = new TargetEntryInstr(); |
| + *case_false_addresses[len - 1] = false_target; |
| + if (node->contains_default()) { |
| + // True and false go to statement start. |
| + false_target->SetSuccessor(statement_start); |
| + if (for_case_statements.is_open()) { |
| + exit_instruction = new TargetEntryInstr(); |
| + for_case_statements.exit()->SetSuccessor(exit_instruction); |
| + } |
| + } else { |
| + if (for_case_statements.is_open()) { |
| + exit_instruction = new JoinEntryInstr(); |
| + for_case_statements.exit()->SetSuccessor(exit_instruction); |
| + } else { |
| + exit_instruction = new TargetEntryInstr(); |
| + } |
| + false_target->SetSuccessor(exit_instruction); |
| + } |
| + } else { |
| + // A CaseNode without case expressions must contain default. |
| + ASSERT(node->contains_default()); |
| + AddInstruction(statement_start); |
| + } |
| + |
| + ASSERT(!is_open()); |
| + exit_ = exit_instruction; |
| } |