| 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* callee_graph) { | 65 void InliningContext::PrepareGraphs(FlowGraph* caller_graph, |
| 66 Definition* call, |
| 67 FlowGraph* callee_graph) { |
| 66 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | 68 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
| 67 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); | 69 ASSERT(callee_graph->max_block_id() > caller_graph->max_block_id()); |
| 68 ASSERT(callee_graph->max_virtual_register_number() > | 70 ASSERT(callee_graph->max_virtual_register_number() > |
| 69 caller_graph_->max_virtual_register_number()); | 71 caller_graph->max_virtual_register_number()); |
| 70 | 72 |
| 71 // Adjust the caller's maximum block id and current SSA temp index. | 73 // Adjust the caller's maximum block id and current SSA temp index. |
| 72 caller_graph_->set_max_block_id(callee_graph->max_block_id()); | 74 caller_graph->set_max_block_id(callee_graph->max_block_id()); |
| 73 caller_graph_->set_current_ssa_temp_index( | 75 caller_graph->set_current_ssa_temp_index( |
| 74 callee_graph->max_virtual_register_number()); | 76 callee_graph->max_virtual_register_number()); |
| 75 | 77 |
| 76 // Attach the outer environment on each instruction in the callee graph. | 78 // Attach the outer environment on each instruction in the callee graph. |
| 77 for (BlockIterator block_it = callee_graph->postorder_iterator(); | 79 for (BlockIterator block_it = callee_graph->postorder_iterator(); |
| 78 !block_it.Done(); | 80 !block_it.Done(); |
| 79 block_it.Advance()) { | 81 block_it.Advance()) { |
| 80 for (ForwardInstructionIterator it(block_it.Current()); | 82 for (ForwardInstructionIterator it(block_it.Current()); |
| 81 !it.Done(); | 83 !it.Done(); |
| 82 it.Advance()) { | 84 it.Advance()) { |
| 83 Instruction* instr = it.Current(); | 85 Instruction* instr = it.Current(); |
| 84 // TODO(zerny): Avoid creating unnecessary environments. Note that some | 86 // TODO(zerny): Avoid creating unnecessary environments. Note that some |
| 85 // optimizations need deoptimization info for non-deoptable instructions, | 87 // optimizations need deoptimization info for non-deoptable instructions, |
| 86 // eg, LICM on GOTOs. | 88 // eg, LICM on GOTOs. |
| 87 if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr); | 89 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); |
| 88 } | 90 } |
| 89 } | 91 } |
| 90 } | 92 } |
| 91 | 93 |
| 92 | 94 |
| 93 void InliningContext::AddExit(ReturnInstr* exit) { | 95 void InliningContext::AddExit(ReturnInstr* exit) { |
| 94 Data data = { NULL, exit }; | 96 Data data = { NULL, exit }; |
| 95 exits_.Add(data); | 97 exits_.Add(data); |
| 96 } | 98 } |
| 97 | 99 |
| 98 | 100 |
| 99 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) { | 101 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) { |
| 100 return (a->exit_block->block_id() - b->exit_block->block_id()); | 102 return (a->exit_block->block_id() - b->exit_block->block_id()); |
| 101 } | 103 } |
| 102 | 104 |
| 103 | 105 |
| 104 void InliningContext::SortExits() { | 106 void InliningContext::SortExits() { |
| 105 // Assign block entries here because we did not necessarily know them when | 107 // Assign block entries here because we did not necessarily know them when |
| 106 // the return exit was added to the array. | 108 // the return exit was added to the array. |
| 107 for (int i = 0; i < exits_.length(); ++i) { | 109 for (int i = 0; i < exits_.length(); ++i) { |
| 108 exits_[i].exit_block = exits_[i].exit_return->GetBlock(); | 110 exits_[i].exit_block = exits_[i].exit_return->GetBlock(); |
| 109 } | 111 } |
| 110 exits_.Sort(LowestBlockIdFirst); | 112 exits_.Sort(LowestBlockIdFirst); |
| 111 } | 113 } |
| 112 | 114 |
| 113 | 115 |
| 114 Definition* InliningContext::JoinReturns(BlockEntryInstr** exit_block, | 116 void InliningContext::ReplaceCall(FlowGraph* caller_graph, |
| 115 Instruction** last_instruction) { | 117 Definition* call, |
| 116 // First sort the list of exits by block id (caching return instruction | 118 FlowGraph* callee_graph) { |
| 117 // block entries as a side effect). | 119 ASSERT(call->previous() != NULL); |
| 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). |
| 118 SortExits(); | 128 SortExits(); |
| 119 intptr_t num_exits = exits_.length(); | 129 intptr_t num_exits = exits_.length(); |
| 120 if (num_exits == 0) { | 130 if (num_exits == 0) { |
| 121 // TODO(zerny): Add support for non-local exits, such as throw. | 131 // TODO(zerny): Add support for non-local exits, such as throw. |
| 122 UNREACHABLE(); | 132 UNREACHABLE(); |
| 123 return NULL; | |
| 124 } else if (num_exits == 1) { | 133 } 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()); |
| 125 ValueAt(0)->RemoveFromUseList(); | 136 ValueAt(0)->RemoveFromUseList(); |
| 126 *exit_block = ExitBlockAt(0); | 137 call->previous()->LinkTo(callee_entry->next()); |
| 127 *last_instruction = LastInstructionAt(0); | 138 LastInstructionAt(0)->LinkTo(call->next()); |
| 128 return call_->HasUses() ? ValueAt(0)->definition() : NULL; | 139 // In case of control flow, locally update the predecessors, phis and |
| 140 // dominator tree. |
| 141 // TODO(zerny): should we leave the dominator tree since we recompute it |
| 142 // after a full inlining pass? |
| 143 if (callee_graph->preorder().length() > 2) { |
| 144 BlockEntryInstr* exit_block = ExitBlockAt(0); |
| 145 // Pictorially, the graph structure is: |
| 146 // |
| 147 // Bc : caller_entry Bi : callee_entry |
| 148 // before_call inlined_head |
| 149 // call ... other blocks ... |
| 150 // after_call Be : exit_block |
| 151 // inlined_foot |
| 152 // And becomes: |
| 153 // |
| 154 // Bc : caller_entry |
| 155 // before_call |
| 156 // inlined_head |
| 157 // ... other blocks ... |
| 158 // Be : exit_block |
| 159 // inlined_foot |
| 160 // after_call |
| 161 // |
| 162 // For 'after_call', caller entry (Bc) is replaced by callee exit (Be). |
| 163 caller_entry->ReplaceAsPredecessorWith(exit_block); |
| 164 // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc). |
| 165 callee_entry->ReplaceAsPredecessorWith(caller_entry); |
| 166 // The callee exit is now the immediate dominator of blocks whose |
| 167 // immediate dominator was the caller entry. |
| 168 ASSERT(exit_block->dominated_blocks().is_empty()); |
| 169 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { |
| 170 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; |
| 171 block->set_dominator(exit_block); |
| 172 exit_block->AddDominatedBlock(block); |
| 173 } |
| 174 // The caller entry is now the immediate dominator of blocks whose |
| 175 // immediate dominator was the callee entry. |
| 176 caller_entry->ClearDominatedBlocks(); |
| 177 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { |
| 178 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; |
| 179 block->set_dominator(caller_entry); |
| 180 caller_entry->AddDominatedBlock(block); |
| 181 } |
| 182 } |
| 129 } else { | 183 } else { |
| 130 // Create a join of the returns. | 184 // Create a join of the returns. |
| 131 intptr_t join_id = caller_graph_->max_block_id() + 1; | 185 intptr_t join_id = caller_graph->max_block_id() + 1; |
| 132 caller_graph_->set_max_block_id(join_id); | 186 caller_graph->set_max_block_id(join_id); |
| 133 JoinEntryInstr* join = | 187 JoinEntryInstr* join = |
| 134 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); | 188 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); |
| 135 | |
| 136 // The dominator set of the join is the intersection of the dominator | |
| 137 // sets of all the predecessors. If we keep the dominator sets ordered | |
| 138 // by height in the dominator tree, we can also get the immediate | |
| 139 // dominator of the join node from the intersection. | |
| 140 // | |
| 141 // block_dominators is the dominator set for each block, ordered from | |
| 142 // the immediate dominator to the root of the dominator tree. This is | |
| 143 // the order we collect them in (adding at the end). | |
| 144 // | |
| 145 // join_dominators is the join's dominators ordered from the root of the | |
| 146 // dominator tree to the immediate dominator. This order supports | |
| 147 // removing during intersection by truncating the list. | |
| 148 GrowableArray<BlockEntryInstr*> block_dominators; | |
| 149 GrowableArray<BlockEntryInstr*> join_dominators; | |
| 150 for (intptr_t i = 0; i < num_exits; ++i) { | 189 for (intptr_t i = 0; i < num_exits; ++i) { |
| 151 // Add the control-flow edge. | |
| 152 LastInstructionAt(i)->Goto(join); | 190 LastInstructionAt(i)->Goto(join); |
| 153 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next()); | 191 // Directly add the predecessors of the join in ascending block id order. |
| 154 join->predecessors_.Add(ExitBlockAt(i)); | 192 join->predecessors_.Add(ExitBlockAt(i)); |
| 155 | |
| 156 // Collect the block's dominators. | |
| 157 block_dominators.Clear(); | |
| 158 BlockEntryInstr* dominator = ExitBlockAt(i)->dominator(); | |
| 159 while (dominator != NULL) { | |
| 160 block_dominators.Add(dominator); | |
| 161 dominator = dominator->dominator(); | |
| 162 } | |
| 163 | |
| 164 if (i == 0) { | |
| 165 // The initial dominator set is the first predecessor's dominator | |
| 166 // set. Reverse it. | |
| 167 for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) { | |
| 168 join_dominators.Add(block_dominators[j]); | |
| 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 >= 1); | |
| 180 join_dominators.TruncateTo(j - 1); | |
| 181 break; | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 } | 193 } |
| 186 // The immediate dominator of the join is the last one in the ordered | |
| 187 // intersection. | |
| 188 join->set_dominator(join_dominators.Last()); | |
| 189 join_dominators.Last()->AddDominatedBlock(join); | |
| 190 *exit_block = join; | |
| 191 *last_instruction = join; | |
| 192 | |
| 193 // If the call has uses, create a phi of the returns. | 194 // If the call has uses, create a phi of the returns. |
| 194 if (call_->HasUses()) { | 195 if (call->HasUses()) { |
| 195 // Add a phi of the return values. | 196 // Add a phi of the return values. |
| 196 PhiInstr* phi = new PhiInstr(join, num_exits); | 197 PhiInstr* phi = new PhiInstr(join, num_exits); |
| 197 phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index()); | 198 phi->set_ssa_temp_index(caller_graph->alloc_ssa_temp_index()); |
| 198 phi->mark_alive(); | 199 phi->mark_alive(); |
| 199 for (intptr_t i = 0; i < num_exits; ++i) { | 200 for (intptr_t i = 0; i < num_exits; ++i) { |
| 200 phi->SetInputAt(i, ValueAt(i)); | 201 phi->SetInputAt(i, ValueAt(i)); |
| 201 } | 202 } |
| 202 join->InsertPhi(phi); | 203 join->InsertPhi(phi); |
| 203 return phi; | 204 // Replace uses of the call with the phi. |
| 205 call->ReplaceUsesWith(phi); |
| 204 } else { | 206 } else { |
| 205 // In the case that the result is unused, remove the return value uses | 207 // In the case that the result is unused, remove the return value uses |
| 206 // from their definition's use list. | 208 // from their definition's use list. |
| 207 for (intptr_t i = 0; i < num_exits; ++i) { | 209 for (intptr_t i = 0; i < num_exits; ++i) { |
| 208 ValueAt(i)->RemoveFromUseList(); | 210 ValueAt(i)->RemoveFromUseList(); |
| 209 } | 211 } |
| 210 return NULL; | |
| 211 } | 212 } |
| 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(); |
| 212 } | 226 } |
| 213 } | 227 } |
| 214 | 228 |
| 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 | |
| 288 | 229 |
| 289 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { | 230 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { |
| 290 ASSERT(is_open()); | 231 ASSERT(is_open()); |
| 291 if (other_fragment.is_empty()) return; | 232 if (other_fragment.is_empty()) return; |
| 292 if (is_empty()) { | 233 if (is_empty()) { |
| 293 entry_ = other_fragment.entry(); | 234 entry_ = other_fragment.entry(); |
| 294 exit_ = other_fragment.exit(); | 235 exit_ = other_fragment.exit(); |
| 295 } else { | 236 } else { |
| 296 exit()->LinkTo(other_fragment.entry()); | 237 exit()->LinkTo(other_fragment.entry()); |
| 297 exit_ = other_fragment.exit(); | 238 exit_ = other_fragment.exit(); |
| (...skipping 3105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3403 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; | 3344 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; |
| 3404 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); | 3345 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
| 3405 OS::SNPrint(chars, len, kFormat, function_name, reason); | 3346 OS::SNPrint(chars, len, kFormat, function_name, reason); |
| 3406 const Error& error = Error::Handle( | 3347 const Error& error = Error::Handle( |
| 3407 LanguageError::New(String::Handle(String::New(chars)))); | 3348 LanguageError::New(String::Handle(String::New(chars)))); |
| 3408 Isolate::Current()->long_jump_base()->Jump(1, error); | 3349 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 3409 } | 3350 } |
| 3410 | 3351 |
| 3411 | 3352 |
| 3412 } // namespace dart | 3353 } // namespace dart |
| OLD | NEW |