| Index: runtime/vm/flow_graph_builder.cc
|
| ===================================================================
|
| --- runtime/vm/flow_graph_builder.cc (revision 6179)
|
| +++ runtime/vm/flow_graph_builder.cc (working copy)
|
| @@ -779,12 +779,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());
|
| +
|
| + // 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;
|
| }
|
|
|
|
|
|
|