| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_builder.h" | 5 #include "vm/flow_graph_builder.h" |
| 6 | 6 |
| 7 #include "lib/invocation_mirror.h" | 7 #include "lib/invocation_mirror.h" |
| 8 #include "vm/ast_printer.h" | 8 #include "vm/ast_printer.h" |
| 9 #include "vm/code_descriptors.h" | 9 #include "vm/code_descriptors.h" |
| 10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 last_used_try_index_(CatchClauseNode::kInvalidTryIndex), | 55 last_used_try_index_(CatchClauseNode::kInvalidTryIndex), |
| 56 try_index_(CatchClauseNode::kInvalidTryIndex), | 56 try_index_(CatchClauseNode::kInvalidTryIndex), |
| 57 graph_entry_(NULL) { } | 57 graph_entry_(NULL) { } |
| 58 | 58 |
| 59 | 59 |
| 60 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { | 60 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
| 61 graph_entry_->AddCatchEntry(entry); | 61 graph_entry_->AddCatchEntry(entry); |
| 62 } | 62 } |
| 63 | 63 |
| 64 | 64 |
| 65 void InliningContext::PrepareGraphs(FlowGraph* caller_graph, | 65 void InliningContext::PrepareGraphs(FlowGraph* callee_graph) { |
| 66 Definition* call, | |
| 67 FlowGraph* callee_graph) { | |
| 68 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | 66 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
| 69 ASSERT(callee_graph->max_block_id() > caller_graph->max_block_id()); | 67 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); |
| 70 ASSERT(callee_graph->max_virtual_register_number() > | 68 ASSERT(callee_graph->max_virtual_register_number() > |
| 71 caller_graph->max_virtual_register_number()); | 69 caller_graph_->max_virtual_register_number()); |
| 72 | 70 |
| 73 // Adjust the caller's maximum block id and current SSA temp index. | 71 // Adjust the caller's maximum block id and current SSA temp index. |
| 74 caller_graph->set_max_block_id(callee_graph->max_block_id()); | 72 caller_graph_->set_max_block_id(callee_graph->max_block_id()); |
| 75 caller_graph->set_current_ssa_temp_index( | 73 caller_graph_->set_current_ssa_temp_index( |
| 76 callee_graph->max_virtual_register_number()); | 74 callee_graph->max_virtual_register_number()); |
| 77 | 75 |
| 78 // Attach the outer environment on each instruction in the callee graph. | 76 // Attach the outer environment on each instruction in the callee graph. |
| 79 for (BlockIterator block_it = callee_graph->postorder_iterator(); | 77 for (BlockIterator block_it = callee_graph->postorder_iterator(); |
| 80 !block_it.Done(); | 78 !block_it.Done(); |
| 81 block_it.Advance()) { | 79 block_it.Advance()) { |
| 82 for (ForwardInstructionIterator it(block_it.Current()); | 80 for (ForwardInstructionIterator it(block_it.Current()); |
| 83 !it.Done(); | 81 !it.Done(); |
| 84 it.Advance()) { | 82 it.Advance()) { |
| 85 Instruction* instr = it.Current(); | 83 Instruction* instr = it.Current(); |
| 86 // TODO(zerny): Avoid creating unnecessary environments. Note that some | 84 // TODO(zerny): Avoid creating unnecessary environments. Note that some |
| 87 // optimizations need deoptimization info for non-deoptable instructions, | 85 // optimizations need deoptimization info for non-deoptable instructions, |
| 88 // eg, LICM on GOTOs. | 86 // eg, LICM on GOTOs. |
| 89 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); | 87 if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr); |
| 90 } | 88 } |
| 91 } | 89 } |
| 92 } | 90 } |
| 93 | 91 |
| 94 | 92 |
| 95 void InliningContext::AddExit(ReturnInstr* exit) { | 93 void InliningContext::AddExit(ReturnInstr* exit) { |
| 96 Data data = { NULL, exit }; | 94 Data data = { NULL, exit }; |
| 97 exits_.Add(data); | 95 exits_.Add(data); |
| 98 } | 96 } |
| 99 | 97 |
| 100 | 98 |
| 101 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) { | 99 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) { |
| 102 return (a->exit_block->block_id() - b->exit_block->block_id()); | 100 return (a->exit_block->block_id() - b->exit_block->block_id()); |
| 103 } | 101 } |
| 104 | 102 |
| 105 | 103 |
| 106 void InliningContext::SortExits() { | 104 void InliningContext::SortExits() { |
| 107 // Assign block entries here because we did not necessarily know them when | 105 // Assign block entries here because we did not necessarily know them when |
| 108 // the return exit was added to the array. | 106 // the return exit was added to the array. |
| 109 for (int i = 0; i < exits_.length(); ++i) { | 107 for (int i = 0; i < exits_.length(); ++i) { |
| 110 exits_[i].exit_block = exits_[i].exit_return->GetBlock(); | 108 exits_[i].exit_block = exits_[i].exit_return->GetBlock(); |
| 111 } | 109 } |
| 112 exits_.Sort(LowestBlockIdFirst); | 110 exits_.Sort(LowestBlockIdFirst); |
| 113 } | 111 } |
| 114 | 112 |
| 115 | 113 |
| 116 void InliningContext::ReplaceCall(FlowGraph* caller_graph, | 114 Definition* InliningContext::JoinReturns(BlockEntryInstr** exit_block, |
| 117 Definition* call, | 115 Instruction** last_instruction) { |
| 118 FlowGraph* callee_graph) { | 116 // First sort the list of exits by block id (caching return instruction |
| 119 ASSERT(call->previous() != NULL); | 117 // block entries as a side effect). |
| 120 ASSERT(call->next() != NULL); | |
| 121 PrepareGraphs(caller_graph, call, callee_graph); | |
| 122 | |
| 123 BlockEntryInstr* caller_entry = call->GetBlock(); | |
| 124 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); | |
| 125 | |
| 126 // Insert the callee graph into the caller graph. First sort the list of | |
| 127 // exits by block id (recording block entries as a side effect). | |
| 128 SortExits(); | 118 SortExits(); |
| 129 intptr_t num_exits = exits_.length(); | 119 intptr_t num_exits = exits_.length(); |
| 130 if (num_exits == 0) { | 120 if (num_exits == 0) { |
| 131 // TODO(zerny): Add support for non-local exits, such as throw. | 121 // TODO(zerny): Add support for non-local exits, such as throw. |
| 132 UNREACHABLE(); | 122 UNREACHABLE(); |
| 123 return NULL; |
| 133 } else if (num_exits == 1) { | 124 } else if (num_exits == 1) { |
| 134 // For just one exit, replace the uses and remove the call from the graph. | |
| 135 call->ReplaceUsesWith(ValueAt(0)->definition()); | |
| 136 ValueAt(0)->RemoveFromUseList(); | 125 ValueAt(0)->RemoveFromUseList(); |
| 137 call->previous()->LinkTo(callee_entry->next()); | 126 *exit_block = ExitBlockAt(0); |
| 138 LastInstructionAt(0)->LinkTo(call->next()); | 127 *last_instruction = LastInstructionAt(0); |
| 139 // In case of control flow, locally update the predecessors, phis and | 128 return call_->HasUses() ? ValueAt(0)->definition() : NULL; |
| 140 // dominator tree. | 129 } else { |
| 141 // TODO(zerny): should we leave the dominator tree since we recompute it | 130 // Create a join of the returns. |
| 142 // after a full inlining pass? | 131 intptr_t join_id = caller_graph_->max_block_id() + 1; |
| 143 if (callee_graph->preorder().length() > 2) { | 132 caller_graph_->set_max_block_id(join_id); |
| 144 BlockEntryInstr* exit_block = ExitBlockAt(0); | 133 JoinEntryInstr* join = |
| 145 // Pictorially, the graph structure is: | 134 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); |
| 146 // | 135 |
| 147 // Bc : caller_entry Bi : callee_entry | 136 // The dominator set of the join is the intersection of the dominator |
| 148 // before_call inlined_head | 137 // sets of all the predecessors. If we keep the dominator sets ordered |
| 149 // call ... other blocks ... | 138 // by height in the dominator tree, we can also get the immediate |
| 150 // after_call Be : exit_block | 139 // dominator of the join node from the intersection. |
| 151 // inlined_foot | 140 // |
| 152 // And becomes: | 141 // block_dominators is the dominator set for each block, ordered from |
| 153 // | 142 // the immediate dominator to the root of the dominator tree. This is |
| 154 // Bc : caller_entry | 143 // the order we collect them in (adding at the end). |
| 155 // before_call | 144 // |
| 156 // inlined_head | 145 // join_dominators is the join's dominators ordered from the root of the |
| 157 // ... other blocks ... | 146 // dominator tree to the immediate dominator. This order supports |
| 158 // Be : exit_block | 147 // removing during intersection by truncating the list. |
| 159 // inlined_foot | 148 GrowableArray<BlockEntryInstr*> block_dominators; |
| 160 // after_call | 149 GrowableArray<BlockEntryInstr*> join_dominators; |
| 161 // | 150 for (intptr_t i = 0; i < num_exits; ++i) { |
| 162 // For 'after_call', caller entry (Bc) is replaced by callee exit (Be). | 151 // Add the control-flow edge. |
| 163 caller_entry->ReplaceAsPredecessorWith(exit_block); | 152 LastInstructionAt(i)->Goto(join); |
| 164 // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc). | 153 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next()); |
| 165 callee_entry->ReplaceAsPredecessorWith(caller_entry); | 154 join->predecessors_.Add(ExitBlockAt(i)); |
| 166 // The callee exit is now the immediate dominator of blocks whose | 155 |
| 167 // immediate dominator was the caller entry. | 156 // Collect the block's dominators. |
| 168 ASSERT(exit_block->dominated_blocks().is_empty()); | 157 block_dominators.Clear(); |
| 169 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { | 158 BlockEntryInstr* dominator = ExitBlockAt(i)->dominator(); |
| 170 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; | 159 while (dominator != NULL) { |
| 171 block->set_dominator(exit_block); | 160 block_dominators.Add(dominator); |
| 172 exit_block->AddDominatedBlock(block); | 161 dominator = dominator->dominator(); |
| 173 } | 162 } |
| 174 // The caller entry is now the immediate dominator of blocks whose | 163 |
| 175 // immediate dominator was the callee entry. | 164 if (i == 0) { |
| 176 caller_entry->ClearDominatedBlocks(); | 165 // The initial dominator set is the first predecessor's dominator |
| 177 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { | 166 // set. Reverse it. |
| 178 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; | 167 for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) { |
| 179 block->set_dominator(caller_entry); | 168 join_dominators.Add(block_dominators[j]); |
| 180 caller_entry->AddDominatedBlock(block); | 169 } |
| 170 } else { |
| 171 // Intersect the block's dominators with the join's dominators so far. |
| 172 intptr_t last = block_dominators.length() - 1; |
| 173 for (intptr_t j = 0; j < join_dominators.length(); ++j) { |
| 174 intptr_t k = last - j; // Corresponding index in block_dominators. |
| 175 if ((k < 0) || (join_dominators[j] != block_dominators[k])) { |
| 176 // We either exhausted the dominators for this block before |
| 177 // exhausting the current intersection, or else we found a block |
| 178 // on the path from the root of the tree that is not in common. |
| 179 ASSERT(j >= 2); |
| 180 join_dominators.TruncateTo(j); |
| 181 break; |
| 182 } |
| 183 } |
| 181 } | 184 } |
| 182 } | 185 } |
| 183 } else { | 186 // The immediate dominator of the join is the last one in the ordered |
| 184 // Create a join of the returns. | 187 // intersection. |
| 185 intptr_t join_id = caller_graph->max_block_id() + 1; | 188 join->set_dominator(join_dominators.Last()); |
| 186 caller_graph->set_max_block_id(join_id); | 189 join_dominators.Last()->AddDominatedBlock(join); |
| 187 JoinEntryInstr* join = | 190 *exit_block = join; |
| 188 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); | 191 *last_instruction = join; |
| 189 for (intptr_t i = 0; i < num_exits; ++i) { | 192 |
| 190 LastInstructionAt(i)->Goto(join); | |
| 191 // Directly add the predecessors of the join in ascending block id order. | |
| 192 join->predecessors_.Add(ExitBlockAt(i)); | |
| 193 } | |
| 194 // If the call has uses, create a phi of the returns. | 193 // If the call has uses, create a phi of the returns. |
| 195 if (call->HasUses()) { | 194 if (call_->HasUses()) { |
| 196 // Add a phi of the return values. | 195 // Add a phi of the return values. |
| 197 PhiInstr* phi = new PhiInstr(join, num_exits); | 196 PhiInstr* phi = new PhiInstr(join, num_exits); |
| 198 phi->set_ssa_temp_index(caller_graph->alloc_ssa_temp_index()); | 197 phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index()); |
| 199 phi->mark_alive(); | 198 phi->mark_alive(); |
| 200 for (intptr_t i = 0; i < num_exits; ++i) { | 199 for (intptr_t i = 0; i < num_exits; ++i) { |
| 201 phi->SetInputAt(i, ValueAt(i)); | 200 phi->SetInputAt(i, ValueAt(i)); |
| 202 } | 201 } |
| 203 join->InsertPhi(phi); | 202 join->InsertPhi(phi); |
| 204 // Replace uses of the call with the phi. | 203 return phi; |
| 205 call->ReplaceUsesWith(phi); | |
| 206 } else { | 204 } else { |
| 207 // In the case that the result is unused, remove the return value uses | 205 // In the case that the result is unused, remove the return value uses |
| 208 // from their definition's use list. | 206 // from their definition's use list. |
| 209 for (intptr_t i = 0; i < num_exits; ++i) { | 207 for (intptr_t i = 0; i < num_exits; ++i) { |
| 210 ValueAt(i)->RemoveFromUseList(); | 208 ValueAt(i)->RemoveFromUseList(); |
| 211 } | 209 } |
| 210 return NULL; |
| 212 } | 211 } |
| 213 // Remove the call from the graph. | |
| 214 call->previous()->LinkTo(callee_entry->next()); | |
| 215 join->LinkTo(call->next()); | |
| 216 // Replace the blocks after splitting (see comment in the len=1 case above). | |
| 217 caller_entry->ReplaceAsPredecessorWith(join); | |
| 218 callee_entry->ReplaceAsPredecessorWith(caller_entry); | |
| 219 // Update the last instruction pointers on each exit block to the new goto. | |
| 220 for (intptr_t i = 0; i < num_exits; ++i) { | |
| 221 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next()); | |
| 222 } | |
| 223 // Mark that the dominator tree is invalid. | |
| 224 // TODO(zerny): Compute the dominator frontier locally. | |
| 225 caller_graph->InvalidateDominatorTree(); | |
| 226 } | 212 } |
| 227 } | 213 } |
| 228 | 214 |
| 215 |
| 216 void InliningContext::ReplaceCall(FlowGraph* callee_graph) { |
| 217 ASSERT(call_->previous() != NULL); |
| 218 ASSERT(call_->next() != NULL); |
| 219 PrepareGraphs(callee_graph); |
| 220 |
| 221 BlockEntryInstr* call_block = call_->GetBlock(); |
| 222 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); |
| 223 |
| 224 // Insert the callee graph into the caller graph. |
| 225 BlockEntryInstr* callee_exit = NULL; |
| 226 Instruction* callee_last_instruction = NULL; |
| 227 Definition* callee_result = JoinReturns(&callee_exit, |
| 228 &callee_last_instruction); |
| 229 if (callee_result != NULL) { |
| 230 call_->ReplaceUsesWith(callee_result); |
| 231 } |
| 232 if (callee_last_instruction == callee_entry) { |
| 233 // There are no instructions in the inlined function (e.g., it might be |
| 234 // a return of a parameter or a return of a constant defined in the |
| 235 // initial definitions). |
| 236 call_->previous()->LinkTo(call_->next()); |
| 237 } else { |
| 238 call_->previous()->LinkTo(callee_entry->next()); |
| 239 callee_last_instruction->LinkTo(call_->next()); |
| 240 } |
| 241 if (callee_exit != callee_entry) { |
| 242 // In case of control flow, locally update the predecessors, phis and |
| 243 // dominator tree. |
| 244 // |
| 245 // Pictorially, the graph structure is: |
| 246 // |
| 247 // Bc : call_block Bi : callee_entry |
| 248 // before_call inlined_head |
| 249 // call ... other blocks ... |
| 250 // after_call Be : callee_exit |
| 251 // inlined_foot |
| 252 // And becomes: |
| 253 // |
| 254 // Bc : call_block |
| 255 // before_call |
| 256 // inlined_head |
| 257 // ... other blocks ... |
| 258 // Be : callee_exit |
| 259 // inlined_foot |
| 260 // after_call |
| 261 // |
| 262 // For successors of 'after_call', the call block (Bc) is replaced as a |
| 263 // predecessor by the callee exit (Be). |
| 264 call_block->ReplaceAsPredecessorWith(callee_exit); |
| 265 // For successors of 'inlined_head', the callee entry (Bi) is replaced |
| 266 // as a predecessor by the call block (Bc). |
| 267 callee_entry->ReplaceAsPredecessorWith(call_block); |
| 268 |
| 269 // The callee exit is now the immediate dominator of blocks whose |
| 270 // immediate dominator was the call block. |
| 271 ASSERT(callee_exit->dominated_blocks().is_empty()); |
| 272 for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) { |
| 273 BlockEntryInstr* block = call_block->dominated_blocks()[i]; |
| 274 block->set_dominator(callee_exit); |
| 275 callee_exit->AddDominatedBlock(block); |
| 276 } |
| 277 // The call block is now the immediate dominator of blocks whose |
| 278 // immediate dominator was the callee entry. |
| 279 call_block->ClearDominatedBlocks(); |
| 280 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { |
| 281 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; |
| 282 block->set_dominator(call_block); |
| 283 call_block->AddDominatedBlock(block); |
| 284 } |
| 285 } |
| 286 } |
| 287 |
| 229 | 288 |
| 230 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { | 289 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { |
| 231 ASSERT(is_open()); | 290 ASSERT(is_open()); |
| 232 if (other_fragment.is_empty()) return; | 291 if (other_fragment.is_empty()) return; |
| 233 if (is_empty()) { | 292 if (is_empty()) { |
| 234 entry_ = other_fragment.entry(); | 293 entry_ = other_fragment.entry(); |
| 235 exit_ = other_fragment.exit(); | 294 exit_ = other_fragment.exit(); |
| 236 } else { | 295 } else { |
| 237 exit()->LinkTo(other_fragment.entry()); | 296 exit()->LinkTo(other_fragment.entry()); |
| 238 exit_ = other_fragment.exit(); | 297 exit_ = other_fragment.exit(); |
| (...skipping 3105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3344 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; | 3403 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; |
| 3345 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); | 3404 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
| 3346 OS::SNPrint(chars, len, kFormat, function_name, reason); | 3405 OS::SNPrint(chars, len, kFormat, function_name, reason); |
| 3347 const Error& error = Error::Handle( | 3406 const Error& error = Error::Handle( |
| 3348 LanguageError::New(String::Handle(String::New(chars)))); | 3407 LanguageError::New(String::Handle(String::New(chars)))); |
| 3349 Isolate::Current()->long_jump_base()->Jump(1, error); | 3408 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 3350 } | 3409 } |
| 3351 | 3410 |
| 3352 | 3411 |
| 3353 } // namespace dart | 3412 } // namespace dart |
| OLD | NEW |