Chromium Code Reviews| Index: runtime/vm/flow_graph_builder.cc |
| diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc |
| index 37426ec14210d768b57a0501a2bc25481076ed3f..d25e5853ac527f68115de598ae420a0876a86a01 100644 |
| --- a/runtime/vm/flow_graph_builder.cc |
| +++ b/runtime/vm/flow_graph_builder.cc |
| @@ -42,6 +42,92 @@ DEFINE_FLAG(bool, trace_type_check_elimination, false, |
| DECLARE_FLAG(bool, enable_type_checks); |
| +JoinEntryInstr* NestedStatement::BreakTargetFor(SourceLabel* label) { |
| + if (label != label_) return NULL; |
| + if (break_target_ == NULL) { |
| + break_target_ = |
| + new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| + } |
| + return break_target_; |
| +} |
| + |
| + |
| +JoinEntryInstr* NestedStatement::ContinueTargetFor(SourceLabel* label) { |
| + return NULL; |
| +} |
| + |
| + |
| +// A nested statement that can be the target of a continue as well as a |
| +// break. |
| +class NestedLoop : public NestedStatement { |
| + public: |
| + NestedLoop(FlowGraphBuilder* owner, SourceLabel* label) |
| + : NestedStatement(owner, label), continue_target_(NULL) { } |
| + |
| + JoinEntryInstr* continue_target() const { return continue_target_; } |
| + |
| + virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
| + |
| + private: |
| + JoinEntryInstr* continue_target_; |
| +}; |
| + |
| + |
| +JoinEntryInstr* NestedLoop::ContinueTargetFor(SourceLabel* label) { |
| + if (label != this->label()) return NULL; |
| + if (continue_target_ == NULL) { |
| + continue_target_ = |
| + new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| + } |
| + return continue_target_; |
| +} |
| + |
| + |
| +// A nested switch which can be the target of a break if labeled, and whose |
| +// cases can be the targets of continues. |
| +class NestedSwitch : public NestedStatement { |
| + public: |
| + NestedSwitch(FlowGraphBuilder* owner, SwitchNode* node); |
| + |
| + virtual JoinEntryInstr* ContinueTargetFor(SourceLabel* label); |
| + |
| + private: |
| + GrowableArray<SourceLabel*> case_labels_; |
| + GrowableArray<JoinEntryInstr*> case_targets_; |
| +}; |
| + |
| + |
| +NestedSwitch::NestedSwitch(FlowGraphBuilder* owner, SwitchNode* node) |
| + : NestedStatement(owner, node->label()), |
| + case_labels_(node->body()->length()), |
| + case_targets_(node->body()->length()) { |
| + SequenceNode* body = node->body(); |
| + for (intptr_t i = 0; i < body->length(); ++i) { |
| + CaseNode* case_node = body->NodeAt(i)->AsCaseNode(); |
| + if (case_node != NULL) { |
| + case_labels_.Add(case_node->label()); |
| + case_targets_.Add(NULL); |
| + } |
| + } |
| +} |
| + |
| + |
| +JoinEntryInstr* NestedSwitch::ContinueTargetFor(SourceLabel* label) { |
| + // Allocate a join for a case clause that matches the label. This block |
| + // is not necessarily targeted by a continue, but we always use a join in |
| + // the graph anyway. |
| + for (intptr_t i = 0; i < case_labels_.length(); ++i) { |
| + if (label != case_labels_[i]) continue; |
| + if (case_targets_[i] == NULL) { |
| + case_targets_[i] = |
| + new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| + } |
| + return case_targets_[i]; |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| FlowGraphBuilder::FlowGraphBuilder(ParsedFunction* parsed_function, |
| const Array& ic_data_array, |
| InlineExitCollector* exit_collector, |
| @@ -64,6 +150,7 @@ FlowGraphBuilder::FlowGraphBuilder(ParsedFunction* parsed_function, |
| graph_entry_(NULL), |
| temp_count_(0), |
| args_pushed_(0), |
| + nesting_stack_(NULL), |
| osr_id_(osr_id) { } |
| @@ -1567,16 +1654,14 @@ void EffectGraphVisitor::VisitIfNode(IfNode* node) { |
| void EffectGraphVisitor::VisitSwitchNode(SwitchNode* node) { |
| + NestedSwitch nested_switch(owner(), node); |
| EffectGraphVisitor switch_body(owner()); |
| node->body()->Visit(&switch_body); |
| Append(switch_body); |
| - if ((node->label() != NULL) && (node->label()->join_for_break() != NULL)) { |
| - if (is_open()) Goto(node->label()->join_for_break()); |
| - exit_ = node->label()->join_for_break(); |
| + if (nested_switch.break_target() != NULL) { |
| + if (is_open()) Goto(nested_switch.break_target()); |
| + exit_ = nested_switch.break_target(); |
| } |
| - // No continue label allowed. |
| - ASSERT((node->label() == NULL) || |
| - (node->label()->join_for_continue() == NULL)); |
| } |
| @@ -1605,21 +1690,19 @@ void EffectGraphVisitor::VisitCaseNode(CaseNode* node) { |
| const intptr_t len = node->case_expressions()->length(); |
| // Create case statements instructions. |
| EffectGraphVisitor for_case_statements(owner()); |
| - // Compute start of statements fragment. |
| + // Compute the start of the statements fragment. |
| JoinEntryInstr* 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. |
| - statement_start = node->label()->join_for_continue(); |
| - if (statement_start == NULL) { |
| - statement_start = new JoinEntryInstr(owner()->AllocateBlockId(), |
| - owner()->try_index()); |
| - node->label()->set_join_for_continue(statement_start); |
| - } |
| - } else { |
| + if (node->label() == NULL) { |
| statement_start = new JoinEntryInstr(owner()->AllocateBlockId(), |
| owner()->try_index()); |
| + } else { |
| + // The case nodes are nested inside a SequenceNode that is the body of a |
| + // SwitchNode. The SwitchNode on the nesting stack contains the |
| + // continue labels for all the case clauses. |
| + statement_start = |
| + owner()->nesting_stack()->outer()->ContinueTargetFor(node->label()); |
| } |
| + ASSERT(statement_start != NULL); |
| node->statements()->Visit(&for_case_statements); |
| Instruction* statement_exit = |
| AppendFragment(statement_start, for_case_statements); |
| @@ -1695,7 +1778,9 @@ void EffectGraphVisitor::VisitCaseNode(CaseNode* node) { |
| // f) loop-exit-target |
| // g) break-join (optional) |
| void EffectGraphVisitor::VisitWhileNode(WhileNode* node) { |
| + NestedLoop nested_loop(owner(), node->label()); |
| owner()->IncrementLoopDepth(); |
| + |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| ASSERT(!for_test.is_empty()); // Language spec. |
| @@ -1704,15 +1789,13 @@ void EffectGraphVisitor::VisitWhileNode(WhileNode* node) { |
| node->body()->Visit(&for_body); |
| // Labels are set after body traversal. |
| - SourceLabel* lbl = node->label(); |
| - ASSERT(lbl != NULL); |
| - JoinEntryInstr* join = lbl->join_for_continue(); |
| + JoinEntryInstr* join = nested_loop.continue_target(); |
| if (join != NULL) { |
| if (for_body.is_open()) for_body.Goto(join); |
| for_body.exit_ = join; |
| } |
| TieLoop(node->token_pos(), for_test, for_body); |
| - join = lbl->join_for_break(); |
| + join = nested_loop.break_target(); |
| if (join != NULL) { |
| Goto(join); |
| exit_ = join; |
| @@ -1730,8 +1813,10 @@ void EffectGraphVisitor::VisitWhileNode(WhileNode* node) { |
| // f) loop-exit-target |
| // g) break-join |
| void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) { |
| + NestedLoop nested_loop(owner(), node->label()); |
| owner()->IncrementLoopDepth(); |
| - // Traverse body first in order to generate continue and break labels. |
| + |
| + // Traverse the body first in order to generate continue and break labels. |
| EffectGraphVisitor for_body(owner()); |
| node->body()->Visit(&for_body); |
| @@ -1746,7 +1831,7 @@ void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) { |
| Goto(body_entry_join); |
| Instruction* body_exit = AppendFragment(body_entry_join, for_body); |
| - JoinEntryInstr* join = node->label()->join_for_continue(); |
| + JoinEntryInstr* join = nested_loop.continue_target(); |
| if ((body_exit != NULL) || (join != NULL)) { |
| if (join == NULL) { |
| join = new JoinEntryInstr(owner()->AllocateBlockId(), |
| @@ -1762,7 +1847,7 @@ void EffectGraphVisitor::VisitDoWhileNode(DoWhileNode* node) { |
| } |
| for_test.IfTrueGoto(body_entry_join); |
| - join = node->label()->join_for_break(); |
| + join = nested_loop.break_target(); |
| if (join == NULL) { |
| exit_ = for_test.CreateFalseSuccessor(); |
| } else { |
| @@ -1791,6 +1876,7 @@ void EffectGraphVisitor::VisitForNode(ForNode* node) { |
| Append(for_initializer); |
| ASSERT(is_open()); |
| + NestedLoop nested_loop(owner(), node->label()); |
| owner()->IncrementLoopDepth(); |
| // Compose body to set any jump labels. |
| EffectGraphVisitor for_body(owner()); |
| @@ -1800,7 +1886,7 @@ void EffectGraphVisitor::VisitForNode(ForNode* node) { |
| node->increment()->Visit(&for_increment); |
| // Join the loop body and increment and then tie the loop. |
| - JoinEntryInstr* continue_join = node->label()->join_for_continue(); |
| + JoinEntryInstr* continue_join = nested_loop.continue_target(); |
| if ((continue_join != NULL) || for_body.is_open()) { |
| JoinEntryInstr* loop_entry = |
| new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index()); |
| @@ -1821,7 +1907,7 @@ void EffectGraphVisitor::VisitForNode(ForNode* node) { |
| if (node->condition() == NULL) { |
| // Endless loop, no test. |
| Append(for_body); |
| - exit_ = node->label()->join_for_break(); // May be NULL. |
| + exit_ = nested_loop.break_target(); // May be NULL. |
| } else { |
| TestGraphVisitor for_test(owner(), node->condition()->token_pos()); |
| node->condition()->Visit(&for_test); |
| @@ -1830,11 +1916,11 @@ void EffectGraphVisitor::VisitForNode(ForNode* node) { |
| BlockEntryInstr* body_entry = for_test.CreateTrueSuccessor(); |
| AppendFragment(body_entry, for_body); |
| - if (node->label()->join_for_break() == NULL) { |
| + if (nested_loop.break_target() == NULL) { |
| exit_ = for_test.CreateFalseSuccessor(); |
| } else { |
| - for_test.IfFalseGoto(node->label()->join_for_break()); |
| - exit_ = node->label()->join_for_break(); |
| + for_test.IfFalseGoto(nested_loop.break_target()); |
| + exit_ = nested_loop.break_target(); |
| } |
| } |
| owner()->DecrementLoopDepth(); |
| @@ -1879,18 +1965,21 @@ void EffectGraphVisitor::VisitJumpNode(JumpNode* node) { |
| JoinEntryInstr* jump_target = NULL; |
| if (node->kind() == Token::kBREAK) { |
|
Florian Schneider
2013/11/13 17:42:58
For less duplicated code maybe move this condition
Kevin Millikin (Google)
2013/11/13 17:47:22
That sounds good to me. I'll do it.
|
| - if (node->label()->join_for_break() == NULL) { |
| - node->label()->set_join_for_break( |
| - new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index())); |
| + NestedStatement* current = owner()->nesting_stack(); |
| + while (current != NULL) { |
| + jump_target = current->BreakTargetFor(node->label()); |
| + if (jump_target != NULL) break; |
| + current = current->outer(); |
| } |
| - jump_target = node->label()->join_for_break(); |
| } else { |
| - if (node->label()->join_for_continue() == NULL) { |
| - node->label()->set_join_for_continue( |
| - new JoinEntryInstr(owner()->AllocateBlockId(), owner()->try_index())); |
| + NestedStatement* current = owner()->nesting_stack(); |
| + while (current != NULL) { |
| + jump_target = current->ContinueTargetFor(node->label()); |
| + if (jump_target != NULL) break; |
| + current = current->outer(); |
| } |
| - jump_target = node->label()->join_for_continue(); |
| } |
| + ASSERT(jump_target != NULL); |
| Goto(jump_target); |
| } |
| @@ -3386,6 +3475,11 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| const intptr_t num_context_variables = |
| (scope != NULL) ? scope->num_context_variables() : 0; |
| int previous_context_level = owner()->context_level(); |
| + // The outermost function sequence cannot contain a label. |
| + ASSERT((node->label() == NULL) || |
| + (node != owner()->parsed_function()->node_sequence())); |
| + NestedStatement nested_block(owner(), node->label()); |
| + |
| if (num_context_variables > 0) { |
| // The loop local scope declares variables that are captured. |
| // Allocate and chain a new context. |
| @@ -3415,6 +3509,7 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| AddInstruction( |
| new StoreContextInstr(Bind(ExitTempLocalScope(tmp_var)))); |
| } |
| + |
| owner()->set_context_level(scope->context_level()); |
| // If this node_sequence is the body of the function being compiled, copy |
| @@ -3509,20 +3604,13 @@ void EffectGraphVisitor::VisitSequenceNode(SequenceNode* node) { |
| } |
| } |
| - // No continue on sequence allowed. |
| - ASSERT((node->label() == NULL) || |
| - (node->label()->join_for_continue() == NULL)); |
| // If this node sequence is labeled, a break out of the sequence will have |
| // taken care of unchaining the context. |
| - if ((node->label() != NULL) && |
| - (node->label()->join_for_break() != NULL)) { |
| - if (is_open()) Goto(node->label()->join_for_break()); |
| - exit_ = node->label()->join_for_break(); |
| + if (nested_block.break_target() != NULL) { |
| + if (is_open()) Goto(nested_block.break_target()); |
| + exit_ = nested_block.break_target(); |
| } |
| - // The outermost function sequence cannot contain a label. |
| - ASSERT((node->label() == NULL) || |
| - (node != owner()->parsed_function()->node_sequence())); |
| owner()->set_context_level(previous_context_level); |
| } |