Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1552)

Unified Diff: runtime/vm/flow_graph_builder.cc

Issue 14067002: Incrementally recompute dominators when inlining. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/flow_graph_builder.h ('k') | runtime/vm/flow_graph_inliner.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/flow_graph_builder.cc
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 337eebe96c78aed74b28426ccd94e994bba614ce..dc402426cde1567b4fb206ae3fd5ab840fcc4dfd 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -62,17 +62,15 @@ void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) {
}
-void InliningContext::PrepareGraphs(FlowGraph* caller_graph,
- Definition* call,
- FlowGraph* callee_graph) {
+void InliningContext::PrepareGraphs(FlowGraph* callee_graph) {
ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
- ASSERT(callee_graph->max_block_id() > caller_graph->max_block_id());
+ ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id());
ASSERT(callee_graph->max_virtual_register_number() >
- caller_graph->max_virtual_register_number());
+ caller_graph_->max_virtual_register_number());
// Adjust the caller's maximum block id and current SSA temp index.
- caller_graph->set_max_block_id(callee_graph->max_block_id());
- caller_graph->set_current_ssa_temp_index(
+ caller_graph_->set_max_block_id(callee_graph->max_block_id());
+ caller_graph_->set_current_ssa_temp_index(
callee_graph->max_virtual_register_number());
// Attach the outer environment on each instruction in the callee graph.
@@ -86,7 +84,7 @@ void InliningContext::PrepareGraphs(FlowGraph* caller_graph,
// TODO(zerny): Avoid creating unnecessary environments. Note that some
// optimizations need deoptimization info for non-deoptable instructions,
// eg, LICM on GOTOs.
- if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr);
+ if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr);
}
}
}
@@ -113,116 +111,177 @@ void InliningContext::SortExits() {
}
-void InliningContext::ReplaceCall(FlowGraph* caller_graph,
- Definition* call,
- FlowGraph* callee_graph) {
- ASSERT(call->previous() != NULL);
- ASSERT(call->next() != NULL);
- PrepareGraphs(caller_graph, call, callee_graph);
-
- BlockEntryInstr* caller_entry = call->GetBlock();
- TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
-
- // Insert the callee graph into the caller graph. First sort the list of
- // exits by block id (recording block entries as a side effect).
+Definition* InliningContext::JoinReturns(BlockEntryInstr** exit_block,
+ Instruction** last_instruction) {
+ // First sort the list of exits by block id (caching return instruction
+ // block entries as a side effect).
SortExits();
intptr_t num_exits = exits_.length();
if (num_exits == 0) {
// TODO(zerny): Add support for non-local exits, such as throw.
UNREACHABLE();
+ return NULL;
} else if (num_exits == 1) {
- // For just one exit, replace the uses and remove the call from the graph.
- call->ReplaceUsesWith(ValueAt(0)->definition());
ValueAt(0)->RemoveFromUseList();
- call->previous()->LinkTo(callee_entry->next());
- LastInstructionAt(0)->LinkTo(call->next());
- // 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) {
- BlockEntryInstr* exit_block = ExitBlockAt(0);
- // 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).
- caller_entry->ReplaceAsPredecessorWith(exit_block);
- // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc).
- callee_entry->ReplaceAsPredecessorWith(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];
- block->set_dominator(exit_block);
- exit_block->AddDominatedBlock(block);
- }
- // The caller entry is now the immediate dominator of blocks whose
- // immediate dominator was the callee entry.
- caller_entry->ClearDominatedBlocks();
- for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
- BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
- block->set_dominator(caller_entry);
- caller_entry->AddDominatedBlock(block);
- }
- }
+ *exit_block = ExitBlockAt(0);
+ *last_instruction = LastInstructionAt(0);
+ return call_->HasUses() ? ValueAt(0)->definition() : NULL;
} else {
// Create a join of the returns.
- intptr_t join_id = caller_graph->max_block_id() + 1;
- caller_graph->set_max_block_id(join_id);
+ intptr_t join_id = caller_graph_->max_block_id() + 1;
+ caller_graph_->set_max_block_id(join_id);
JoinEntryInstr* join =
new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex);
+
+ // The dominator set of the join is the intersection of the dominator
+ // sets of all the predecessors. If we keep the dominator sets ordered
+ // by height in the dominator tree, we can also get the immediate
+ // dominator of the join node from the intersection.
+ //
+ // block_dominators is the dominator set for each block, ordered from
+ // the immediate dominator to the root of the dominator tree. This is
+ // the order we collect them in (adding at the end).
+ //
+ // join_dominators is the join's dominators ordered from the root of the
+ // dominator tree to the immediate dominator. This order supports
+ // removing during intersection by truncating the list.
+ GrowableArray<BlockEntryInstr*> block_dominators;
+ GrowableArray<BlockEntryInstr*> join_dominators;
for (intptr_t i = 0; i < num_exits; ++i) {
+ // Add the control-flow edge.
LastInstructionAt(i)->Goto(join);
- // Directly add the predecessors of the join in ascending block id order.
+ ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
join->predecessors_.Add(ExitBlockAt(i));
+
+ // Collect the block's dominators.
+ block_dominators.Clear();
+ BlockEntryInstr* dominator = ExitBlockAt(i)->dominator();
+ while (dominator != NULL) {
+ block_dominators.Add(dominator);
+ dominator = dominator->dominator();
+ }
+
+ if (i == 0) {
+ // The initial dominator set is the first predecessor's dominator
+ // set. Reverse it.
+ for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) {
+ join_dominators.Add(block_dominators[j]);
+ }
+ } else {
+ // Intersect the block's dominators with the join's dominators so far.
+ intptr_t last = block_dominators.length() - 1;
+ for (intptr_t j = 0; j < join_dominators.length(); ++j) {
+ intptr_t k = last - j; // Corresponding index in block_dominators.
+ if ((k < 0) || (join_dominators[j] != block_dominators[k])) {
+ // We either exhausted the dominators for this block before
+ // exhausting the current intersection, or else we found a block
+ // on the path from the root of the tree that is not in common.
+ ASSERT(j >= 1);
Florian Schneider 2013/04/11 11:30:18 I think you can even assert that there are always
+ join_dominators.TruncateTo(j - 1);
+ break;
+ }
+ }
+ }
}
+ // The immediate dominator of the join is the last one in the ordered
+ // intersection.
+ join->set_dominator(join_dominators.Last());
+ join_dominators.Last()->AddDominatedBlock(join);
+ *exit_block = join;
+ *last_instruction = join;
+
// If the call has uses, create a phi of the returns.
- if (call->HasUses()) {
+ if (call_->HasUses()) {
// Add a phi of the return values.
PhiInstr* phi = new PhiInstr(join, num_exits);
- phi->set_ssa_temp_index(caller_graph->alloc_ssa_temp_index());
+ phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index());
phi->mark_alive();
for (intptr_t i = 0; i < num_exits; ++i) {
phi->SetInputAt(i, ValueAt(i));
}
join->InsertPhi(phi);
- // Replace uses of the call with the phi.
- call->ReplaceUsesWith(phi);
+ return phi;
} else {
// In the case that the result is unused, remove the return value uses
// from their definition's use list.
for (intptr_t i = 0; i < num_exits; ++i) {
ValueAt(i)->RemoveFromUseList();
}
+ return NULL;
}
- // Remove the call from the graph.
- call->previous()->LinkTo(callee_entry->next());
- join->LinkTo(call->next());
- // Replace the blocks after splitting (see comment in the len=1 case above).
- caller_entry->ReplaceAsPredecessorWith(join);
- callee_entry->ReplaceAsPredecessorWith(caller_entry);
- // Update the last instruction pointers on each exit block to the new goto.
- for (intptr_t i = 0; i < num_exits; ++i) {
- ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
+ }
+}
+
+
+void InliningContext::ReplaceCall(FlowGraph* callee_graph) {
+ ASSERT(call_->previous() != NULL);
+ ASSERT(call_->next() != NULL);
+ PrepareGraphs(callee_graph);
+
+ BlockEntryInstr* call_block = call_->GetBlock();
+ TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
+
+ // Insert the callee graph into the caller graph.
+ BlockEntryInstr* callee_exit = NULL;
+ Instruction* callee_last_instruction = NULL;
+ Definition* callee_result = JoinReturns(&callee_exit,
+ &callee_last_instruction);
+ if (callee_result != NULL) {
+ call_->ReplaceUsesWith(callee_result);
+ }
+ if (callee_last_instruction == callee_entry) {
+ // There are no instructions in the inlined function (e.g., it might be
+ // a return of a parameter or a return of a constant defined in the
+ // initial definitions).
+ call_->previous()->LinkTo(call_->next());
+ } else {
+ call_->previous()->LinkTo(callee_entry->next());
+ callee_last_instruction->LinkTo(call_->next());
+ }
+ if (callee_exit != callee_entry) {
+ // In case of control flow, locally update the predecessors, phis and
+ // dominator tree.
+ //
+ // Pictorially, the graph structure is:
+ //
+ // Bc : call_block Bi : callee_entry
+ // before_call inlined_head
+ // call ... other blocks ...
+ // after_call Be : callee_exit
+ // inlined_foot
+ // And becomes:
+ //
+ // Bc : call_block
+ // before_call
+ // inlined_head
+ // ... other blocks ...
+ // Be : callee_exit
+ // inlined_foot
+ // after_call
+ //
+ // For successors of 'after_call', the call block (Bc) is replaced as a
+ // predecessor by the callee exit (Be).
+ call_block->ReplaceAsPredecessorWith(callee_exit);
+ // For successors of 'inlined_head', the callee entry (Bi) is replaced
+ // as a predecessor by the call block (Bc).
+ callee_entry->ReplaceAsPredecessorWith(call_block);
+
+ // The callee exit is now the immediate dominator of blocks whose
+ // immediate dominator was the call block.
+ ASSERT(callee_exit->dominated_blocks().is_empty());
+ for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) {
+ BlockEntryInstr* block = call_block->dominated_blocks()[i];
+ block->set_dominator(callee_exit);
+ callee_exit->AddDominatedBlock(block);
+ }
+ // The call block is now the immediate dominator of blocks whose
+ // immediate dominator was the callee entry.
+ call_block->ClearDominatedBlocks();
+ for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
+ BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
+ block->set_dominator(call_block);
+ call_block->AddDominatedBlock(block);
}
- // Mark that the dominator tree is invalid.
- // TODO(zerny): Compute the dominator frontier locally.
- caller_graph->InvalidateDominatorTree();
}
}
« no previous file with comments | « runtime/vm/flow_graph_builder.h ('k') | runtime/vm/flow_graph_inliner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698