| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 #include "vm/branch_optimizer.h" |
| 6 |
| 7 #include "vm/flow_graph.h" |
| 8 #include "vm/intermediate_language.h" |
| 9 |
| 10 namespace dart { |
| 11 |
| 12 // Returns true if the given phi has a single input use and |
| 13 // is used in the environments either at the corresponding block entry or |
| 14 // at the same instruction where input use is. |
| 15 static bool PhiHasSingleUse(PhiInstr* phi, Value* use) { |
| 16 if ((use->next_use() != NULL) || (phi->input_use_list() != use)) { |
| 17 return false; |
| 18 } |
| 19 |
| 20 BlockEntryInstr* block = phi->block(); |
| 21 for (Value* env_use = phi->env_use_list(); |
| 22 env_use != NULL; |
| 23 env_use = env_use->next_use()) { |
| 24 if ((env_use->instruction() != block) && |
| 25 (env_use->instruction() != use->instruction())) { |
| 26 return false; |
| 27 } |
| 28 } |
| 29 |
| 30 return true; |
| 31 } |
| 32 |
| 33 |
| 34 bool BranchSimplifier::Match(JoinEntryInstr* block) { |
| 35 // Match the pattern of a branch on a comparison whose left operand is a |
| 36 // phi from the same block, and whose right operand is a constant. |
| 37 // |
| 38 // Branch(Comparison(kind, Phi, Constant)) |
| 39 // |
| 40 // These are the branches produced by inlining in a test context. Also, |
| 41 // the phi has no other uses so they can simply be eliminated. The block |
| 42 // has no other phis and no instructions intervening between the phi and |
| 43 // branch so the block can simply be eliminated. |
| 44 BranchInstr* branch = block->last_instruction()->AsBranch(); |
| 45 ASSERT(branch != NULL); |
| 46 ComparisonInstr* comparison = branch->comparison(); |
| 47 Value* left = comparison->left(); |
| 48 PhiInstr* phi = left->definition()->AsPhi(); |
| 49 Value* right = comparison->right(); |
| 50 ConstantInstr* constant = |
| 51 (right == NULL) ? NULL : right->definition()->AsConstant(); |
| 52 return (phi != NULL) && |
| 53 (constant != NULL) && |
| 54 (phi->GetBlock() == block) && |
| 55 PhiHasSingleUse(phi, left) && |
| 56 (block->next() == branch) && |
| 57 (block->phis()->length() == 1); |
| 58 } |
| 59 |
| 60 |
| 61 JoinEntryInstr* BranchSimplifier::ToJoinEntry(Zone* zone, |
| 62 TargetEntryInstr* target) { |
| 63 // Convert a target block into a join block. Branches will be duplicated |
| 64 // so the former true and false targets become joins of the control flows |
| 65 // from all the duplicated branches. |
| 66 JoinEntryInstr* join = |
| 67 new(zone) JoinEntryInstr(target->block_id(), target->try_index()); |
| 68 join->InheritDeoptTarget(zone, target); |
| 69 join->LinkTo(target->next()); |
| 70 join->set_last_instruction(target->last_instruction()); |
| 71 target->UnuseAllInputs(); |
| 72 return join; |
| 73 } |
| 74 |
| 75 |
| 76 BranchInstr* BranchSimplifier::CloneBranch(Zone* zone, |
| 77 BranchInstr* branch, |
| 78 Value* new_left, |
| 79 Value* new_right) { |
| 80 ComparisonInstr* comparison = branch->comparison(); |
| 81 ComparisonInstr* new_comparison = |
| 82 comparison->CopyWithNewOperands(new_left, new_right); |
| 83 BranchInstr* new_branch = new(zone) BranchInstr(new_comparison); |
| 84 new_branch->set_is_checked(branch->is_checked()); |
| 85 return new_branch; |
| 86 } |
| 87 |
| 88 |
| 89 void BranchSimplifier::Simplify(FlowGraph* flow_graph) { |
| 90 // Optimize some branches that test the value of a phi. When it is safe |
| 91 // to do so, push the branch to each of the predecessor blocks. This is |
| 92 // an optimization when (a) it can avoid materializing a boolean object at |
| 93 // the phi only to test its value, and (b) it can expose opportunities for |
| 94 // constant propagation and unreachable code elimination. This |
| 95 // optimization is intended to run after inlining which creates |
| 96 // opportunities for optimization (a) and before constant folding which |
| 97 // can perform optimization (b). |
| 98 |
| 99 // Begin with a worklist of join blocks ending in branches. They are |
| 100 // candidates for the pattern below. |
| 101 Zone* zone = flow_graph->zone(); |
| 102 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder(); |
| 103 GrowableArray<BlockEntryInstr*> worklist(postorder.length()); |
| 104 for (BlockIterator it(postorder); !it.Done(); it.Advance()) { |
| 105 BlockEntryInstr* block = it.Current(); |
| 106 if (block->IsJoinEntry() && block->last_instruction()->IsBranch()) { |
| 107 worklist.Add(block); |
| 108 } |
| 109 } |
| 110 |
| 111 // Rewrite until no more instance of the pattern exists. |
| 112 bool changed = false; |
| 113 while (!worklist.is_empty()) { |
| 114 // All blocks in the worklist are join blocks (ending with a branch). |
| 115 JoinEntryInstr* block = worklist.RemoveLast()->AsJoinEntry(); |
| 116 ASSERT(block != NULL); |
| 117 |
| 118 if (Match(block)) { |
| 119 changed = true; |
| 120 |
| 121 // The branch will be copied and pushed to all the join's |
| 122 // predecessors. Convert the true and false target blocks into join |
| 123 // blocks to join the control flows from all of the true |
| 124 // (respectively, false) targets of the copied branches. |
| 125 // |
| 126 // The converted join block will have no phis, so it cannot be another |
| 127 // instance of the pattern. There is thus no need to add it to the |
| 128 // worklist. |
| 129 BranchInstr* branch = block->last_instruction()->AsBranch(); |
| 130 ASSERT(branch != NULL); |
| 131 JoinEntryInstr* join_true = |
| 132 ToJoinEntry(zone, branch->true_successor()); |
| 133 JoinEntryInstr* join_false = |
| 134 ToJoinEntry(zone, branch->false_successor()); |
| 135 |
| 136 ComparisonInstr* comparison = branch->comparison(); |
| 137 PhiInstr* phi = comparison->left()->definition()->AsPhi(); |
| 138 ConstantInstr* constant = comparison->right()->definition()->AsConstant(); |
| 139 ASSERT(constant != NULL); |
| 140 // Copy the constant and branch and push it to all the predecessors. |
| 141 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) { |
| 142 GotoInstr* old_goto = |
| 143 block->PredecessorAt(i)->last_instruction()->AsGoto(); |
| 144 ASSERT(old_goto != NULL); |
| 145 |
| 146 // Replace the goto in each predecessor with a rewritten branch, |
| 147 // rewritten to use the corresponding phi input instead of the phi. |
| 148 Value* new_left = phi->InputAt(i)->Copy(zone); |
| 149 Value* new_right = new(zone) Value(constant); |
| 150 BranchInstr* new_branch = |
| 151 CloneBranch(zone, branch, new_left, new_right); |
| 152 if (branch->env() == NULL) { |
| 153 new_branch->InheritDeoptTarget(zone, old_goto); |
| 154 } else { |
| 155 // Take the environment from the branch if it has one. |
| 156 new_branch->InheritDeoptTarget(zone, branch); |
| 157 // InheritDeoptTarget gave the new branch's comparison the same |
| 158 // deopt id that it gave the new branch. The id should be the |
| 159 // deopt id of the original comparison. |
| 160 new_branch->comparison()->SetDeoptId(*comparison); |
| 161 // The phi can be used in the branch's environment. Rename such |
| 162 // uses. |
| 163 for (Environment::DeepIterator it(new_branch->env()); |
| 164 !it.Done(); |
| 165 it.Advance()) { |
| 166 Value* use = it.CurrentValue(); |
| 167 if (use->definition() == phi) { |
| 168 Definition* replacement = phi->InputAt(i)->definition(); |
| 169 use->RemoveFromUseList(); |
| 170 use->set_definition(replacement); |
| 171 replacement->AddEnvUse(use); |
| 172 } |
| 173 } |
| 174 } |
| 175 |
| 176 new_branch->InsertBefore(old_goto); |
| 177 new_branch->set_next(NULL); // Detaching the goto from the graph. |
| 178 old_goto->UnuseAllInputs(); |
| 179 |
| 180 // Update the predecessor block. We may have created another |
| 181 // instance of the pattern so add it to the worklist if necessary. |
| 182 BlockEntryInstr* branch_block = new_branch->GetBlock(); |
| 183 branch_block->set_last_instruction(new_branch); |
| 184 if (branch_block->IsJoinEntry()) worklist.Add(branch_block); |
| 185 |
| 186 // Connect the branch to the true and false joins, via empty target |
| 187 // blocks. |
| 188 TargetEntryInstr* true_target = |
| 189 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 1, |
| 190 block->try_index()); |
| 191 true_target->InheritDeoptTarget(zone, join_true); |
| 192 TargetEntryInstr* false_target = |
| 193 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 2, |
| 194 block->try_index()); |
| 195 false_target->InheritDeoptTarget(zone, join_false); |
| 196 flow_graph->set_max_block_id(flow_graph->max_block_id() + 2); |
| 197 *new_branch->true_successor_address() = true_target; |
| 198 *new_branch->false_successor_address() = false_target; |
| 199 GotoInstr* goto_true = new(zone) GotoInstr(join_true); |
| 200 goto_true->InheritDeoptTarget(zone, join_true); |
| 201 true_target->LinkTo(goto_true); |
| 202 true_target->set_last_instruction(goto_true); |
| 203 GotoInstr* goto_false = new(zone) GotoInstr(join_false); |
| 204 goto_false->InheritDeoptTarget(zone, join_false); |
| 205 false_target->LinkTo(goto_false); |
| 206 false_target->set_last_instruction(goto_false); |
| 207 } |
| 208 // When all predecessors have been rewritten, the original block is |
| 209 // unreachable from the graph. |
| 210 phi->UnuseAllInputs(); |
| 211 branch->UnuseAllInputs(); |
| 212 block->UnuseAllInputs(); |
| 213 ASSERT(!phi->HasUses()); |
| 214 } |
| 215 } |
| 216 |
| 217 if (changed) { |
| 218 // We may have changed the block order and the dominator tree. |
| 219 flow_graph->DiscoverBlocks(); |
| 220 GrowableArray<BitVector*> dominance_frontier; |
| 221 flow_graph->ComputeDominators(&dominance_frontier); |
| 222 } |
| 223 } |
| 224 |
| 225 |
| 226 static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) { |
| 227 return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) && |
| 228 ((block->next() == block->last_instruction()) || |
| 229 ((block->next() == defn) && (defn->next() == block->last_instruction()))); |
| 230 } |
| 231 |
| 232 |
| 233 static void EliminateTrivialBlock(BlockEntryInstr* block, |
| 234 Definition* instr, |
| 235 IfThenElseInstr* before) { |
| 236 block->UnuseAllInputs(); |
| 237 block->last_instruction()->UnuseAllInputs(); |
| 238 |
| 239 if ((block->next() == instr) && |
| 240 (instr->next() == block->last_instruction())) { |
| 241 before->previous()->LinkTo(instr); |
| 242 instr->LinkTo(before); |
| 243 } |
| 244 } |
| 245 |
| 246 |
| 247 void IfConverter::Simplify(FlowGraph* flow_graph) { |
| 248 Zone* zone = flow_graph->zone(); |
| 249 bool changed = false; |
| 250 |
| 251 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder(); |
| 252 for (BlockIterator it(postorder); !it.Done(); it.Advance()) { |
| 253 BlockEntryInstr* block = it.Current(); |
| 254 JoinEntryInstr* join = block->AsJoinEntry(); |
| 255 |
| 256 // Detect diamond control flow pattern which materializes a value depending |
| 257 // on the result of the comparison: |
| 258 // |
| 259 // B_pred: |
| 260 // ... |
| 261 // Branch if COMP goto (B_pred1, B_pred2) |
| 262 // B_pred1: -- trivial block that contains at most one definition |
| 263 // v1 = Constant(...) |
| 264 // goto B_block |
| 265 // B_pred2: -- trivial block that contains at most one definition |
| 266 // v2 = Constant(...) |
| 267 // goto B_block |
| 268 // B_block: |
| 269 // v3 = phi(v1, v2) -- single phi |
| 270 // |
| 271 // and replace it with |
| 272 // |
| 273 // Ba: |
| 274 // v3 = IfThenElse(COMP ? v1 : v2) |
| 275 // |
| 276 if ((join != NULL) && |
| 277 (join->phis() != NULL) && |
| 278 (join->phis()->length() == 1) && |
| 279 (block->PredecessorCount() == 2)) { |
| 280 BlockEntryInstr* pred1 = block->PredecessorAt(0); |
| 281 BlockEntryInstr* pred2 = block->PredecessorAt(1); |
| 282 |
| 283 PhiInstr* phi = (*join->phis())[0]; |
| 284 Value* v1 = phi->InputAt(0); |
| 285 Value* v2 = phi->InputAt(1); |
| 286 |
| 287 if (IsTrivialBlock(pred1, v1->definition()) && |
| 288 IsTrivialBlock(pred2, v2->definition()) && |
| 289 (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) { |
| 290 BlockEntryInstr* pred = pred1->PredecessorAt(0); |
| 291 BranchInstr* branch = pred->last_instruction()->AsBranch(); |
| 292 ComparisonInstr* comparison = branch->comparison(); |
| 293 |
| 294 // Check if the platform supports efficient branchless IfThenElseInstr |
| 295 // for the given combination of comparison and values flowing from |
| 296 // false and true paths. |
| 297 if (IfThenElseInstr::Supports(comparison, v1, v2)) { |
| 298 Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2; |
| 299 Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2; |
| 300 |
| 301 ComparisonInstr* new_comparison = |
| 302 comparison->CopyWithNewOperands( |
| 303 comparison->left()->Copy(zone), |
| 304 comparison->right()->Copy(zone)); |
| 305 IfThenElseInstr* if_then_else = new(zone) IfThenElseInstr( |
| 306 new_comparison, |
| 307 if_true->Copy(zone), |
| 308 if_false->Copy(zone)); |
| 309 flow_graph->InsertBefore(branch, |
| 310 if_then_else, |
| 311 NULL, |
| 312 FlowGraph::kValue); |
| 313 |
| 314 phi->ReplaceUsesWith(if_then_else); |
| 315 |
| 316 // Connect IfThenElseInstr to the first instruction in the merge block |
| 317 // effectively eliminating diamond control flow. |
| 318 // Current block as well as pred1 and pred2 blocks are no longer in |
| 319 // the graph at this point. |
| 320 if_then_else->LinkTo(join->next()); |
| 321 pred->set_last_instruction(join->last_instruction()); |
| 322 |
| 323 // Resulting block must inherit block id from the eliminated current |
| 324 // block to guarantee that ordering of phi operands in its successor |
| 325 // stays consistent. |
| 326 pred->set_block_id(block->block_id()); |
| 327 |
| 328 // If v1 and v2 were defined inside eliminated blocks pred1/pred2 |
| 329 // move them out to the place before inserted IfThenElse instruction. |
| 330 EliminateTrivialBlock(pred1, v1->definition(), if_then_else); |
| 331 EliminateTrivialBlock(pred2, v2->definition(), if_then_else); |
| 332 |
| 333 // Update use lists to reflect changes in the graph. |
| 334 phi->UnuseAllInputs(); |
| 335 branch->UnuseAllInputs(); |
| 336 block->UnuseAllInputs(); |
| 337 |
| 338 // The graph has changed. Recompute dominators and block orders after |
| 339 // this pass is finished. |
| 340 changed = true; |
| 341 } |
| 342 } |
| 343 } |
| 344 } |
| 345 |
| 346 if (changed) { |
| 347 // We may have changed the block order and the dominator tree. |
| 348 flow_graph->DiscoverBlocks(); |
| 349 GrowableArray<BitVector*> dominance_frontier; |
| 350 flow_graph->ComputeDominators(&dominance_frontier); |
| 351 } |
| 352 } |
| 353 |
| 354 |
| 355 } // namespace dart |
| OLD | NEW |