Index: runtime/vm/flow_graph.cc |
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc |
index 3a4cfe8ae5c955a391ccc1eff3094d38af94f6be..5bcd56ad460e42f12874f43365209c22162f5edb 100644 |
--- a/runtime/vm/flow_graph.cc |
+++ b/runtime/vm/flow_graph.cc |
@@ -30,7 +30,8 @@ FlowGraph::FlowGraph(const FlowGraphBuilder& builder, |
preorder_(), |
postorder_(), |
reverse_postorder_(), |
- exits_(NULL) { |
+ exits_(NULL), |
+ invalid_dominator_tree_(true) { |
DiscoverBlocks(); |
} |
@@ -304,6 +305,7 @@ void FlowGraph::ComputeSSA(intptr_t next_virtual_register_number) { |
// (preorder block numbers of) blocks in the dominance frontier. |
void FlowGraph::ComputeDominators( |
GrowableArray<BitVector*>* dominance_frontier) { |
+ invalid_dominator_tree_ = false; |
// Use the SEMI-NCA algorithm to compute dominators. This is a two-pass |
// version of the Lengauer-Tarjan algorithm (LT is normally three passes) |
// that eliminates a pass by using nearest-common ancestor (NCA) to |
@@ -725,37 +727,68 @@ void FlowGraph::Bailout(const char* reason) const { |
} |
-// Helper to reorder phis after splitting a block. The last instruction(s) of |
-// the split block will now have a larger block id than any previously known |
-// blocks. If the last instruction jumps to a join, we must reorder phi inputs |
-// according to the block order, ie, we move this predecessor to the end. |
-static void ReorderPhis(BlockEntryInstr* block) { |
- GotoInstr* jump = block->last_instruction()->AsGoto(); |
- if (jump == NULL) return; |
- JoinEntryInstr* join = jump->successor(); |
- intptr_t pred_index = join->IndexOfPredecessor(block); |
- intptr_t pred_count = join->PredecessorCount(); |
- ASSERT(pred_index >= 0); |
- ASSERT(pred_index < pred_count); |
- // If the predecessor index is the last index there is nothing to update. |
- if ((join->phis() == NULL) || (pred_index + 1 == pred_count)) return; |
- // Otherwise, move the predecessor use to the end in each phi. |
- for (intptr_t i = 0; i < join->phis()->length(); ++i) { |
- PhiInstr* phi = (*join->phis())[i]; |
- if (phi == NULL) continue; |
- ASSERT(pred_count == phi->InputCount()); |
- // Save the predecessor use. |
- Value* pred_use = phi->InputAt(pred_index); |
- // Move each of the following uses back by one. |
- ASSERT(pred_index < pred_count - 1); // Will move at least one index. |
- for (intptr_t i = pred_index; i < pred_count - 1; ++i) { |
- Value* use = phi->InputAt(i + 1); |
- phi->SetInputAt(i, use); |
- use->set_use_index(i); |
- } |
- // Write the predecessor use at the end. |
- phi->SetInputAt(pred_count - 1, pred_use); |
- pred_use->set_use_index(pred_count - 1); |
+// Helper to replace a block. For each successor of 'old_block', the |
+// predecessors will be reordered to preserve block order sorting as well as the |
+// phis if the successor is a join. |
+void FlowGraph::ReplaceBlock(BlockEntryInstr* old_block, |
Kevin Millikin (Google)
2012/10/22 14:13:11
I suggest ReplacePredecessor. It replaces old_blo
|
+ BlockEntryInstr* new_block) { |
+ // Set the last instruction of the new block to that of the old block. |
+ Instruction* last = old_block->last_instruction(); |
+ new_block->set_last_instruction(last); |
+ // For each successor, update the predecessors. |
+ for (intptr_t sidx = 0; sidx < last->SuccessorCount(); ++sidx) { |
+ // If the successor is a target, update its predecessor. |
+ TargetEntryInstr* target = last->SuccessorAt(sidx)->AsTargetEntry(); |
+ if (target != NULL) { |
+ target->predecessor_ = new_block; |
+ continue; |
+ } |
+ // If the successor is a join, update each predecessor and the phis. |
+ JoinEntryInstr* join = last->SuccessorAt(sidx)->AsJoinEntry(); |
+ ASSERT(join != NULL); |
+ // Find the old predecessor index. |
+ intptr_t old_index = join->IndexOfPredecessor(old_block); |
+ intptr_t pred_count = join->PredecessorCount(); |
+ ASSERT(old_index >= 0); |
+ ASSERT(old_index < pred_count); |
+ // Determine the iteration direction. |
+ intptr_t new_id = new_block->block_id(); |
+ intptr_t step, limit; |
+ if (old_block->block_id() < new_id) { |
+ step = 1; |
+ limit = pred_count - 1; |
+ } else { |
+ step = -1; |
+ limit = 0; |
+ } |
+ // Find the new predecessor index while reordering the predecessors. |
+ intptr_t new_index = old_index; |
+ for (; new_index != limit; new_index += step) { |
+ if (join->predecessors_[new_index + step]->block_id() > new_id) break; |
Kevin Millikin (Google)
2012/10/22 14:13:11
Split this loop so that the comparison works for b
|
+ join->predecessors_[new_index] = join->predecessors_[new_index + step]; |
+ } |
+ join->predecessors_[new_index] = new_block; |
+ // If the new and old predecessor index match there is nothing to update. |
+ if ((join->phis() == NULL) || (old_index == new_index)) return; |
+ // Otherwise, reorder the predecessor uses in each phi. |
+ for (intptr_t i = 0; i < join->phis()->length(); ++i) { |
+ PhiInstr* phi = (*join->phis())[i]; |
+ if (phi == NULL) continue; |
+ ASSERT(pred_count == phi->InputCount()); |
+ // Save the predecessor use. |
+ Value* pred_use = phi->InputAt(old_index); |
+ // Move uses between old and new. |
+ for (intptr_t use_idx = old_index; |
+ use_idx != new_index; |
+ use_idx += step) { |
+ Value* use = phi->InputAt(use_idx + step); |
+ phi->SetInputAt(use_idx, use); |
+ use->set_use_index(use_idx); |
+ } |
+ // Write the predecessor use. |
+ phi->SetInputAt(new_index, pred_use); |
+ pred_use->set_use_index(new_index); |
+ } |
} |
} |
@@ -820,15 +853,35 @@ void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { |
call->ReplaceUsesWith(exit->value()->definition()); |
call->previous()->LinkTo(callee_entry->next()); |
exit->previous()->LinkTo(call->next()); |
- // In case of control flow, locally update the dominator tree. |
+ // In case of control flow, locally update the predecessors, phis and |
+ // dominator tree. |
+ // TODO(zerny): should we leave the dominator tree since we recompute it |
+ // after a full inlining pass? |
if (callee_graph->preorder().length() > 2) { |
- // The caller block is split and the new block id is that of the exit |
- // block. If the caller block had outgoing edges, reorder the phis so they |
- // are still ordered by block id. |
- ReorderPhis(caller_entry); |
- // The callee return is now the immediate dominator of blocks whose |
- // immediate dominator was the caller entry. |
BlockEntryInstr* exit_block = exit->GetBlock(); |
+ // Pictorially, the graph structure is: |
+ // |
+ // Bc : caller_entry Bi : callee_entry |
+ // before_call inlined_head |
+ // call ... other blocks ... |
+ // after_call Be : exit_block |
+ // inlined_foot |
+ // And becomes: |
+ // |
+ // Bc : caller_entry |
+ // before_call |
+ // inlined_head |
+ // ... other blocks ... |
+ // Be : exit_block |
+ // inlined_foot |
+ // after_call |
+ // |
+ // For 'after_call', caller entry (Bc) is replaced by callee exit (Be). |
+ ReplaceBlock(caller_entry, exit_block); |
+ // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc). |
+ ReplaceBlock(callee_entry, caller_entry); |
+ // The callee exit is now the immediate dominator of blocks whose |
+ // immediate dominator was the caller entry. |
ASSERT(exit_block->dominated_blocks().is_empty()); |
for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { |
BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; |
@@ -843,8 +896,6 @@ void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { |
block->set_dominator(caller_entry); |
caller_entry->AddDominatedBlock(block); |
} |
- // Recompute the block orders. |
- DiscoverBlocks(); |
} |
} else { |
// Sort the list of exits by block id. |
@@ -884,16 +935,27 @@ void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { |
// Replace uses of the call with the phi. |
call->ReplaceUsesWith(phi); |
} |
- // Remove the call from the graph. |
+ // Remove the call from the graph. |
call->previous()->LinkTo(callee_entry->next()); |
join->LinkTo(call->next()); |
- // The caller block is split and the new block id is that of the join |
- // block. If the caller block had outgoing edges, reorder the phis so they |
- // are still ordered by block id. |
- ReorderPhis(caller_entry); |
- // Adjust pre/post orders and update the dominator tree. |
- DiscoverBlocks(); |
+ // Replace the blocks after splitting (see comment in the len=1 case above). |
+ ReplaceBlock(caller_entry, join); |
+ ReplaceBlock(callee_entry, caller_entry); |
+ // Update the last instruction pointers on each exit (ie, to the new goto). |
+ for (intptr_t i = 0; i < exits.length(); ++i) { |
+ exits[i]->set_last_instruction( |
+ exits[i]->last_instruction()->previous()->next()); |
+ } |
+ // Mark that the dominator tree is invalid. |
// TODO(zerny): Compute the dominator frontier locally. |
+ invalid_dominator_tree_ = true; |
+ } |
+} |
+ |
+ |
+void FlowGraph::RepairGraphAfterInlining() { |
+ DiscoverBlocks(); |
+ if (invalid_dominator_tree_) { |
GrowableArray<BitVector*> dominance_frontier; |
ComputeDominators(&dominance_frontier); |
} |