• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dead_code_elimination.h"
18 
19 #include "android-base/logging.h"
20 #include "base/array_ref.h"
21 #include "base/bit_vector-inl.h"
22 #include "base/logging.h"
23 #include "base/scoped_arena_allocator.h"
24 #include "base/scoped_arena_containers.h"
25 #include "base/stl_util.h"
26 #include "optimizing/nodes.h"
27 #include "ssa_phi_elimination.h"
28 
29 namespace art HIDDEN {
30 
MarkReachableBlocks(HGraph * graph,ArenaBitVector * visited)31 static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
32   // Use local allocator for allocating memory.
33   ScopedArenaAllocator allocator(graph->GetArenaStack());
34 
35   ScopedArenaVector<HBasicBlock*> worklist(allocator.Adapter(kArenaAllocDCE));
36   constexpr size_t kDefaultWorlistSize = 8;
37   worklist.reserve(kDefaultWorlistSize);
38   visited->SetBit(graph->GetEntryBlock()->GetBlockId());
39   worklist.push_back(graph->GetEntryBlock());
40 
41   while (!worklist.empty()) {
42     HBasicBlock* block = worklist.back();
43     worklist.pop_back();
44     int block_id = block->GetBlockId();
45     DCHECK(visited->IsBitSet(block_id));
46 
47     ArrayRef<HBasicBlock* const> live_successors(block->GetSuccessors());
48     HInstruction* last_instruction = block->GetLastInstruction();
49     if (last_instruction->IsIf()) {
50       HIf* if_instruction = last_instruction->AsIf();
51       HInstruction* condition = if_instruction->InputAt(0);
52       if (condition->IsIntConstant()) {
53         if (condition->AsIntConstant()->IsTrue()) {
54           live_successors = live_successors.SubArray(0u, 1u);
55           DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
56         } else {
57           DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
58           live_successors = live_successors.SubArray(1u, 1u);
59           DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
60         }
61       }
62     } else if (last_instruction->IsPackedSwitch()) {
63       HPackedSwitch* switch_instruction = last_instruction->AsPackedSwitch();
64       HInstruction* switch_input = switch_instruction->InputAt(0);
65       if (switch_input->IsIntConstant()) {
66         int32_t switch_value = switch_input->AsIntConstant()->GetValue();
67         int32_t start_value = switch_instruction->GetStartValue();
68         // Note: Though the spec forbids packed-switch values to wrap around, we leave
69         // that task to the verifier and use unsigned arithmetic with it's "modulo 2^32"
70         // semantics to check if the value is in range, wrapped or not.
71         uint32_t switch_index =
72             static_cast<uint32_t>(switch_value) - static_cast<uint32_t>(start_value);
73         if (switch_index < switch_instruction->GetNumEntries()) {
74           live_successors = live_successors.SubArray(switch_index, 1u);
75           DCHECK_EQ(live_successors[0], block->GetSuccessors()[switch_index]);
76         } else {
77           live_successors = live_successors.SubArray(switch_instruction->GetNumEntries(), 1u);
78           DCHECK_EQ(live_successors[0], switch_instruction->GetDefaultBlock());
79         }
80       }
81     }
82 
83     for (HBasicBlock* successor : live_successors) {
84       // Add only those successors that have not been visited yet.
85       if (!visited->IsBitSet(successor->GetBlockId())) {
86         visited->SetBit(successor->GetBlockId());
87         worklist.push_back(successor);
88       }
89     }
90   }
91 }
92 
MaybeRecordDeadBlock(HBasicBlock * block)93 void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
94   if (stats_ != nullptr) {
95     stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
96                        block->GetPhis().CountSize() + block->GetInstructions().CountSize());
97   }
98 }
99 
MaybeRecordSimplifyIf()100 void HDeadCodeElimination::MaybeRecordSimplifyIf() {
101   if (stats_ != nullptr) {
102     stats_->RecordStat(MethodCompilationStat::kSimplifyIf);
103   }
104 }
105 
HasInput(HCondition * instruction,HInstruction * input)106 static bool HasInput(HCondition* instruction, HInstruction* input) {
107   return (instruction->InputAt(0) == input) ||
108          (instruction->InputAt(1) == input);
109 }
110 
HasEquality(IfCondition condition)111 static bool HasEquality(IfCondition condition) {
112   switch (condition) {
113     case kCondEQ:
114     case kCondLE:
115     case kCondGE:
116     case kCondBE:
117     case kCondAE:
118       return true;
119     case kCondNE:
120     case kCondLT:
121     case kCondGT:
122     case kCondB:
123     case kCondA:
124       return false;
125   }
126 }
127 
Evaluate(HCondition * condition,HInstruction * left,HInstruction * right)128 static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstruction* right) {
129   if (left == right && !DataType::IsFloatingPointType(left->GetType())) {
130     return condition->GetBlock()->GetGraph()->GetIntConstant(
131         HasEquality(condition->GetCondition()) ? 1 : 0);
132   }
133 
134   if (!left->IsConstant() || !right->IsConstant()) {
135     return nullptr;
136   }
137 
138   if (left->IsIntConstant()) {
139     return condition->Evaluate(left->AsIntConstant(), right->AsIntConstant());
140   } else if (left->IsNullConstant()) {
141     return condition->Evaluate(left->AsNullConstant(), right->AsNullConstant());
142   } else if (left->IsLongConstant()) {
143     return condition->Evaluate(left->AsLongConstant(), right->AsLongConstant());
144   } else if (left->IsFloatConstant()) {
145     return condition->Evaluate(left->AsFloatConstant(), right->AsFloatConstant());
146   } else {
147     DCHECK(left->IsDoubleConstant());
148     return condition->Evaluate(left->AsDoubleConstant(), right->AsDoubleConstant());
149   }
150 }
151 
RemoveNonNullControlDependences(HBasicBlock * block,HBasicBlock * throws)152 static bool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) {
153   // Test for an if as last statement.
154   if (!block->EndsWithIf()) {
155     return false;
156   }
157   HIf* ifs = block->GetLastInstruction()->AsIf();
158   // Find either:
159   //   if obj == null
160   //     throws
161   //   else
162   //     not_throws
163   // or:
164   //   if obj != null
165   //     not_throws
166   //   else
167   //     throws
168   HInstruction* cond = ifs->InputAt(0);
169   HBasicBlock* not_throws = nullptr;
170   if (throws == ifs->IfTrueSuccessor() && cond->IsEqual()) {
171     not_throws = ifs->IfFalseSuccessor();
172   } else if (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
173     not_throws = ifs->IfTrueSuccessor();
174   } else {
175     return false;
176   }
177   DCHECK(cond->IsEqual() || cond->IsNotEqual());
178   HInstruction* obj = cond->InputAt(1);
179   if (obj->IsNullConstant()) {
180     obj = cond->InputAt(0);
181   } else if (!cond->InputAt(0)->IsNullConstant()) {
182     return false;
183   }
184 
185   // We can't create a BoundType for an object with an invalid RTI.
186   const ReferenceTypeInfo ti = obj->GetReferenceTypeInfo();
187   if (!ti.IsValid()) {
188     return false;
189   }
190 
191   // Scan all uses of obj and find null check under control dependence.
192   HBoundType* bound = nullptr;
193   const HUseList<HInstruction*>& uses = obj->GetUses();
194   for (auto it = uses.begin(), end = uses.end(); it != end;) {
195     HInstruction* user = it->GetUser();
196     ++it;  // increment before possibly replacing
197     if (user->IsNullCheck()) {
198       HBasicBlock* user_block = user->GetBlock();
199       if (user_block != block &&
200           user_block != throws &&
201           block->Dominates(user_block)) {
202         if (bound == nullptr) {
203           bound = new (obj->GetBlock()->GetGraph()->GetAllocator()) HBoundType(obj);
204           bound->SetUpperBound(ti, /*can_be_null*/ false);
205           bound->SetReferenceTypeInfo(ti);
206           bound->SetCanBeNull(false);
207           not_throws->InsertInstructionBefore(bound, not_throws->GetFirstInstruction());
208         }
209         user->ReplaceWith(bound);
210         user_block->RemoveInstruction(user);
211       }
212     }
213   }
214   return bound != nullptr;
215 }
216 
217 // Simplify the pattern:
218 //
219 //           B1
220 //          /  \
221 //          |   instr_1
222 //          |   ...
223 //          |   instr_n
224 //          |   foo()  // always throws
225 //          |   instr_n+2
226 //          |   ...
227 //          |   instr_n+m
228 //          \   goto B2
229 //           \ /
230 //            B2
231 //
232 // Into:
233 //
234 //           B1
235 //          /  \
236 //          |  instr_1
237 //          |  ...
238 //          |  instr_n
239 //          |  foo()
240 //          |  goto Exit
241 //          |   |
242 //         B2  Exit
243 //
244 // Rationale:
245 // Removal of the never taken edge to B2 may expose other optimization opportunities, such as code
246 // sinking.
247 //
248 // Note: The example above is a simple one that uses a `goto` but we could end the block with an If,
249 // for example.
SimplifyAlwaysThrows()250 bool HDeadCodeElimination::SimplifyAlwaysThrows() {
251   HBasicBlock* exit = graph_->GetExitBlock();
252   if (!graph_->HasAlwaysThrowingInvokes() || exit == nullptr) {
253     return false;
254   }
255 
256   bool rerun_dominance_and_loop_analysis = false;
257 
258   // Order does not matter, just pick one.
259   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
260     if (block->IsTryBlock()) {
261       // We don't want to perform the simplify always throws optimizations for throws inside of
262       // tries since those throws might not go to the exit block.
263       continue;
264     }
265 
266     // We iterate to find the first instruction that always throws. If two instructions always
267     // throw, the first one will throw and the second one will never be reached.
268     HInstruction* throwing_invoke = nullptr;
269     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
270       if (it.Current()->IsInvoke() && it.Current()->AsInvoke()->AlwaysThrows()) {
271         throwing_invoke = it.Current();
272         break;
273       }
274     }
275 
276     if (throwing_invoke == nullptr) {
277       // No always-throwing instruction found. Continue with the rest of the blocks.
278       continue;
279     }
280 
281     // If we are already pointing at the exit block we could still remove the instructions
282     // between the always throwing instruction, and the exit block. If we have no other
283     // instructions, just continue since there's nothing to do.
284     if (block->GetSuccessors().size() == 1 &&
285         block->GetSingleSuccessor() == exit &&
286         block->GetLastInstruction()->GetPrevious() == throwing_invoke) {
287       continue;
288     }
289 
290     // We split the block at the throwing instruction, and the instructions after the throwing
291     // instructions will be disconnected from the graph after `block` points to the exit.
292     // `RemoveDeadBlocks` will take care of removing this new block and its instructions.
293     // Even though `SplitBefore` doesn't guarantee the graph to remain in SSA form, it is fine
294     // since we do not break it.
295     HBasicBlock* new_block = block->SplitBefore(throwing_invoke->GetNext(),
296                                                 /* require_graph_not_in_ssa_form= */ false);
297     DCHECK_EQ(block->GetSingleSuccessor(), new_block);
298     block->ReplaceSuccessor(new_block, exit);
299 
300     rerun_dominance_and_loop_analysis = true;
301     MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
302     // Perform a quick follow up optimization on object != null control dependences
303     // that is much cheaper to perform now than in a later phase.
304     // If there are multiple predecessors, none may end with a HIf as required in
305     // RemoveNonNullControlDependences because we split critical edges.
306     if (block->GetPredecessors().size() == 1u &&
307         RemoveNonNullControlDependences(block->GetSinglePredecessor(), block)) {
308       MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
309     }
310   }
311 
312   // We need to re-analyze the graph in order to run DCE afterwards.
313   if (rerun_dominance_and_loop_analysis) {
314     graph_->ClearLoopInformation();
315     graph_->ClearDominanceInformation();
316     graph_->BuildDominatorTree();
317     return true;
318   }
319   return false;
320 }
321 
SimplifyIfs()322 bool HDeadCodeElimination::SimplifyIfs() {
323   bool simplified_one_or_more_ifs = false;
324   bool rerun_dominance_and_loop_analysis = false;
325 
326   // Iterating in PostOrder it's better for MaybeAddPhi as it can add a Phi for multiple If
327   // instructions in a chain without updating the dominator chain. The branch redirection itself can
328   // work in PostOrder or ReversePostOrder without issues.
329   for (HBasicBlock* block : graph_->GetPostOrder()) {
330     if (block->IsCatchBlock()) {
331       // This simplification cannot be applied to catch blocks, because exception handler edges do
332       // not represent normal control flow. Though in theory this could still apply to normal
333       // control flow going directly to a catch block, we cannot support it at the moment because
334       // the catch Phi's inputs do not correspond to the catch block's predecessors, so we cannot
335       // identify which predecessor corresponds to a given statically evaluated input.
336       continue;
337     }
338 
339     HInstruction* last = block->GetLastInstruction();
340     if (!last->IsIf()) {
341       continue;
342     }
343 
344     if (block->IsLoopHeader()) {
345       // We do not apply this optimization to loop headers as this could create irreducible loops.
346       continue;
347     }
348 
349     // We will add a Phi which allows the simplification to take place in cases where it wouldn't.
350     MaybeAddPhi(block);
351 
352     // TODO(solanes): Investigate support for multiple phis in `block`. We can potentially "push
353     // downwards" existing Phis into the true/false branches. For example, let's say we have another
354     // Phi: Phi(x1,x2,x3,x4,x5,x6). This could turn into Phi(x1,x2) in the true branch, Phi(x3,x4)
355     // in the false branch, and remain as Phi(x5,x6) in `block` (for edges that we couldn't
356     // redirect). We might even be able to remove some phis altogether as they will have only one
357     // value.
358     if (block->HasSinglePhi() &&
359         block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
360       HInstruction* first = block->GetFirstInstruction();
361       bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
362       bool has_only_phi_condition_and_if =
363           !has_only_phi_and_if &&
364           first->IsCondition() &&
365           HasInput(first->AsCondition(), block->GetFirstPhi()) &&
366           (first->GetNext() == last) &&
367           (last->InputAt(0) == first) &&
368           first->HasOnlyOneNonEnvironmentUse();
369 
370       if (has_only_phi_and_if || has_only_phi_condition_and_if) {
371         HPhi* phi = block->GetFirstPhi()->AsPhi();
372         bool phi_input_is_left = (first->InputAt(0) == phi);
373 
374         // Walk over all inputs of the phis and update the control flow of
375         // predecessors feeding constants to the phi.
376         // Note that phi->InputCount() may change inside the loop.
377         for (size_t i = 0; i < phi->InputCount();) {
378           HInstruction* input = phi->InputAt(i);
379           HInstruction* value_to_check = nullptr;
380           if (has_only_phi_and_if) {
381             if (input->IsIntConstant()) {
382               value_to_check = input;
383             }
384           } else {
385             DCHECK(has_only_phi_condition_and_if);
386             if (phi_input_is_left) {
387               value_to_check = Evaluate(first->AsCondition(), input, first->InputAt(1));
388             } else {
389               value_to_check = Evaluate(first->AsCondition(), first->InputAt(0), input);
390             }
391           }
392           if (value_to_check == nullptr) {
393             // Could not evaluate to a constant, continue iterating over the inputs.
394             ++i;
395           } else {
396             HBasicBlock* predecessor_to_update = block->GetPredecessors()[i];
397             HBasicBlock* successor_to_update = nullptr;
398             if (value_to_check->AsIntConstant()->IsTrue()) {
399               successor_to_update = last->AsIf()->IfTrueSuccessor();
400             } else {
401               DCHECK(value_to_check->AsIntConstant()->IsFalse())
402                   << value_to_check->AsIntConstant()->GetValue();
403               successor_to_update = last->AsIf()->IfFalseSuccessor();
404             }
405             predecessor_to_update->ReplaceSuccessor(block, successor_to_update);
406             phi->RemoveInputAt(i);
407             simplified_one_or_more_ifs = true;
408             if (block->IsInLoop()) {
409               rerun_dominance_and_loop_analysis = true;
410             }
411             // For simplicity, don't create a dead block, let the dead code elimination
412             // pass deal with it.
413             if (phi->InputCount() == 1) {
414               break;
415             }
416           }
417         }
418         if (block->GetPredecessors().size() == 1) {
419           phi->ReplaceWith(phi->InputAt(0));
420           block->RemovePhi(phi);
421           if (has_only_phi_condition_and_if) {
422             // Evaluate here (and not wait for a constant folding pass) to open
423             // more opportunities for DCE.
424             HInstruction* result = first->AsCondition()->TryStaticEvaluation();
425             if (result != nullptr) {
426               first->ReplaceWith(result);
427               block->RemoveInstruction(first);
428             }
429           }
430         }
431         if (simplified_one_or_more_ifs) {
432           MaybeRecordSimplifyIf();
433         }
434       }
435     }
436   }
437   // We need to re-analyze the graph in order to run DCE afterwards.
438   if (simplified_one_or_more_ifs) {
439     if (rerun_dominance_and_loop_analysis) {
440       graph_->ClearLoopInformation();
441       graph_->ClearDominanceInformation();
442       graph_->BuildDominatorTree();
443     } else {
444       graph_->ClearDominanceInformation();
445       // We have introduced critical edges, remove them.
446       graph_->SimplifyCFG();
447       graph_->ComputeDominanceInformation();
448       graph_->ComputeTryBlockInformation();
449     }
450   }
451 
452   return simplified_one_or_more_ifs;
453 }
454 
MaybeAddPhi(HBasicBlock * block)455 void HDeadCodeElimination::MaybeAddPhi(HBasicBlock* block) {
456   DCHECK(block->GetLastInstruction()->IsIf());
457   HIf* if_instruction = block->GetLastInstruction()->AsIf();
458   if (if_instruction->InputAt(0)->IsConstant()) {
459     // Constant values are handled in RemoveDeadBlocks.
460     return;
461   }
462 
463   if (block->GetNumberOfPredecessors() < 2u) {
464     // Nothing to redirect.
465     return;
466   }
467 
468   if (!block->GetPhis().IsEmpty()) {
469     // SimplifyIf doesn't currently work with multiple phis. Adding a phi here won't help that
470     // optimization.
471     return;
472   }
473 
474   HBasicBlock* dominator = block->GetDominator();
475   if (!dominator->EndsWithIf()) {
476     return;
477   }
478 
479   HInstruction* input = if_instruction->InputAt(0);
480   HInstruction* dominator_input = dominator->GetLastInstruction()->AsIf()->InputAt(0);
481   const bool same_input = dominator_input == input;
482   if (!same_input) {
483     // Try to see if the dominator has the opposite input (e.g. if(cond) and if(!cond)). If that's
484     // the case, we can perform the optimization with the false and true branches reversed.
485     if (!dominator_input->IsCondition() || !input->IsCondition()) {
486       return;
487     }
488 
489     HCondition* block_cond = input->AsCondition();
490     HCondition* dominator_cond = dominator_input->AsCondition();
491 
492     if (block_cond->GetLeft() != dominator_cond->GetLeft() ||
493         block_cond->GetRight() != dominator_cond->GetRight() ||
494         block_cond->GetOppositeCondition() != dominator_cond->GetCondition()) {
495       return;
496     }
497   }
498 
499   if (kIsDebugBuild) {
500     // `block`'s successors should have only one predecessor. Otherwise, we have a critical edge in
501     // the graph.
502     for (HBasicBlock* succ : block->GetSuccessors()) {
503       DCHECK_EQ(succ->GetNumberOfPredecessors(), 1u);
504     }
505   }
506 
507   const size_t pred_size = block->GetNumberOfPredecessors();
508   HPhi* new_phi = new (graph_->GetAllocator())
509       HPhi(graph_->GetAllocator(), kNoRegNumber, pred_size, DataType::Type::kInt32);
510 
511   for (size_t index = 0; index < pred_size; index++) {
512     HBasicBlock* pred = block->GetPredecessors()[index];
513     const bool dominated_by_true =
514         dominator->GetLastInstruction()->AsIf()->IfTrueSuccessor()->Dominates(pred);
515     const bool dominated_by_false =
516         dominator->GetLastInstruction()->AsIf()->IfFalseSuccessor()->Dominates(pred);
517     if (dominated_by_true == dominated_by_false) {
518       // In this case, we can't know if we are coming from the true branch, or the false branch. It
519       // happens in cases like:
520       //      1 (outer if)
521       //     / \
522       //    2   3 (inner if)
523       //    |  / \
524       //    | 4  5
525       //     \/  |
526       //      6  |
527       //       \ |
528       //         7 (has the same if(cond) as 1)
529       //         |
530       //         8
531       // `7` (which would be `block` in this example), and `6` will come from both the true path and
532       // the false path of `1`. We bumped into something similar in SelectGenerator. See
533       // HSelectGenerator::TryFixupDoubleDiamondPattern.
534       // TODO(solanes): Figure out if we can fix up the graph into a double diamond in a generic way
535       // so that DeadCodeElimination and SelectGenerator can take advantage of it.
536 
537       if (!same_input) {
538         // `1` and `7` having the opposite condition is a case we are missing. We could potentially
539         // add a BooleanNot instruction to be able to add the Phi, but it seems like overkill since
540         // this case is not that common.
541         return;
542       }
543 
544       // The Phi will have `0`, `1`, and `cond` as inputs. If SimplifyIf redirects 0s and 1s, we
545       // will end up with Phi(cond,...,cond) which will be replaced by `cond`. Effectively, we will
546       // redirect edges that we are able to redirect and the rest will remain as before (i.e. we
547       // won't have an extra Phi).
548       new_phi->SetRawInputAt(index, input);
549     } else {
550       // Redirect to either the true branch (1), or the false branch (0).
551       // Given that `dominated_by_true` is the exact opposite of `dominated_by_false`,
552       // `(same_input && dominated_by_true) || (!same_input && dominated_by_false)` is equivalent to
553       // `same_input == dominated_by_true`.
554       new_phi->SetRawInputAt(
555           index,
556           same_input == dominated_by_true ? graph_->GetIntConstant(1) : graph_->GetIntConstant(0));
557     }
558   }
559 
560   block->AddPhi(new_phi);
561   if_instruction->ReplaceInput(new_phi, 0);
562 
563   // Remove the old input now, if possible. This allows the branch redirection in SimplifyIf to
564   // work without waiting for another pass of DCE.
565   if (input->IsDeadAndRemovable()) {
566     DCHECK(!same_input)
567         << " if both blocks have the same condition, it shouldn't be dead and removable since the "
568         << "dominator block's If instruction would be using that condition.";
569     input->GetBlock()->RemoveInstruction(input);
570   }
571   MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyIfAddedPhi);
572 }
573 
ConnectSuccessiveBlocks()574 void HDeadCodeElimination::ConnectSuccessiveBlocks() {
575   // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
576   for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
577     HBasicBlock* block  = graph_->GetReversePostOrder()[i];
578     DCHECK(!block->IsEntryBlock());
579     while (block->GetLastInstruction()->IsGoto()) {
580       HBasicBlock* successor = block->GetSingleSuccessor();
581       if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
582         break;
583       }
584       DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
585       block->MergeWith(successor);
586       --size;
587       DCHECK_EQ(size, graph_->GetReversePostOrder().size());
588       DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
589       // Reiterate on this block in case it can be merged with its new successor.
590     }
591   }
592 }
593 
594 struct HDeadCodeElimination::TryBelongingInformation {
TryBelongingInformationart::HDeadCodeElimination::TryBelongingInformation595   explicit TryBelongingInformation(ScopedArenaAllocator* allocator)
596       : blocks_in_try(allocator->Adapter(kArenaAllocDCE)),
597         coalesced_try_entries(allocator->Adapter(kArenaAllocDCE)) {}
598 
599   // Which blocks belong in the try.
600   ScopedArenaSet<HBasicBlock*> blocks_in_try;
601   // Which other try entries are referencing this same try.
602   ScopedArenaSet<HBasicBlock*> coalesced_try_entries;
603 };
604 
CanPerformTryRemoval(const TryBelongingInformation & try_belonging_info)605 bool HDeadCodeElimination::CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info) {
606   for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
607     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
608       if (it.Current()->CanThrow()) {
609         return false;
610       }
611     }
612   }
613   return true;
614 }
615 
DisconnectHandlersAndUpdateTryBoundary(HBasicBlock * block,bool * any_block_in_loop)616 void HDeadCodeElimination::DisconnectHandlersAndUpdateTryBoundary(
617     HBasicBlock* block,
618     /* out */ bool* any_block_in_loop) {
619   if (block->IsInLoop()) {
620     *any_block_in_loop = true;
621   }
622 
623   // Disconnect the handlers.
624   while (block->GetSuccessors().size() > 1) {
625     HBasicBlock* handler = block->GetSuccessors()[1];
626     DCHECK(handler->IsCatchBlock());
627     block->RemoveSuccessor(handler);
628     handler->RemovePredecessor(block);
629     if (handler->IsInLoop()) {
630       *any_block_in_loop = true;
631     }
632   }
633 
634   // Change TryBoundary to Goto.
635   DCHECK(block->EndsWithTryBoundary());
636   HInstruction* last = block->GetLastInstruction();
637   block->RemoveInstruction(last);
638   block->AddInstruction(new (graph_->GetAllocator()) HGoto(last->GetDexPc()));
639   DCHECK_EQ(block->GetSuccessors().size(), 1u);
640 }
641 
RemoveTry(HBasicBlock * try_entry,const TryBelongingInformation & try_belonging_info,bool * any_block_in_loop)642 void HDeadCodeElimination::RemoveTry(HBasicBlock* try_entry,
643                                      const TryBelongingInformation& try_belonging_info,
644                                      /* out */ bool* any_block_in_loop) {
645   // Update all try entries.
646   DCHECK(try_entry->EndsWithTryBoundary());
647   DCHECK(try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
648   DisconnectHandlersAndUpdateTryBoundary(try_entry, any_block_in_loop);
649 
650   for (HBasicBlock* other_try_entry : try_belonging_info.coalesced_try_entries) {
651     DCHECK(other_try_entry->EndsWithTryBoundary());
652     DCHECK(other_try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
653     DisconnectHandlersAndUpdateTryBoundary(other_try_entry, any_block_in_loop);
654   }
655 
656   // Update the blocks in the try.
657   for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
658     // Update the try catch information since now the try doesn't exist.
659     block->SetTryCatchInformation(nullptr);
660     if (block->IsInLoop()) {
661       *any_block_in_loop = true;
662     }
663 
664     if (block->EndsWithTryBoundary()) {
665       // Try exits.
666       DCHECK(!block->GetLastInstruction()->AsTryBoundary()->IsEntry());
667       DisconnectHandlersAndUpdateTryBoundary(block, any_block_in_loop);
668 
669       if (block->GetSingleSuccessor()->IsExitBlock()) {
670         // `block` used to be a single exit TryBoundary that got turned into a Goto. It
671         // is now pointing to the exit which we don't allow. To fix it, we disconnect
672         // `block` from its predecessor and RemoveDeadBlocks will remove it from the
673         // graph.
674         DCHECK(block->IsSingleGoto());
675         HBasicBlock* predecessor = block->GetSinglePredecessor();
676         predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());
677 
678         if (!block->GetDominatedBlocks().empty()) {
679           // Update domination tree if `block` dominates a block to keep the graph consistent.
680           DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
681           DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
682           predecessor->AddDominatedBlock(graph_->GetExitBlock());
683           graph_->GetExitBlock()->SetDominator(predecessor);
684           block->RemoveDominatedBlock(graph_->GetExitBlock());
685         }
686       }
687     }
688   }
689 }
690 
RemoveUnneededTries()691 bool HDeadCodeElimination::RemoveUnneededTries() {
692   if (!graph_->HasTryCatch()) {
693     return false;
694   }
695 
696   // Use local allocator for allocating memory.
697   ScopedArenaAllocator allocator(graph_->GetArenaStack());
698 
699   // Collect which blocks are part of which try.
700   std::unordered_map<HBasicBlock*, TryBelongingInformation> tries;
701   for (HBasicBlock* block : graph_->GetReversePostOrderSkipEntryBlock()) {
702     if (block->IsTryBlock()) {
703       HBasicBlock* key = block->GetTryCatchInformation()->GetTryEntry().GetBlock();
704       auto it = tries.find(key);
705       if (it == tries.end()) {
706         it = tries.insert({key, TryBelongingInformation(&allocator)}).first;
707       }
708       it->second.blocks_in_try.insert(block);
709     }
710   }
711 
712   // Deduplicate the tries which have different try entries but they are really the same try.
713   for (auto it = tries.begin(); it != tries.end(); it++) {
714     DCHECK(it->first->EndsWithTryBoundary());
715     HTryBoundary* try_boundary = it->first->GetLastInstruction()->AsTryBoundary();
716     for (auto other_it = next(it); other_it != tries.end(); /*other_it++ in the loop*/) {
717       DCHECK(other_it->first->EndsWithTryBoundary());
718       HTryBoundary* other_try_boundary = other_it->first->GetLastInstruction()->AsTryBoundary();
719       if (try_boundary->HasSameExceptionHandlersAs(*other_try_boundary)) {
720         // Merge the entries as they are really the same one.
721         // Block merging.
722         it->second.blocks_in_try.insert(other_it->second.blocks_in_try.begin(),
723                                         other_it->second.blocks_in_try.end());
724 
725         // Add the coalesced try entry to update it too.
726         it->second.coalesced_try_entries.insert(other_it->first);
727 
728         // Erase the other entry.
729         other_it = tries.erase(other_it);
730       } else {
731         other_it++;
732       }
733     }
734   }
735 
736   size_t removed_tries = 0;
737   bool any_block_in_loop = false;
738 
739   // Check which tries contain throwing instructions.
740   for (const auto& entry : tries) {
741     if (CanPerformTryRemoval(entry.second)) {
742       ++removed_tries;
743       RemoveTry(entry.first, entry.second, &any_block_in_loop);
744     }
745   }
746 
747   if (removed_tries != 0) {
748     // We want to:
749     //   1) Update the dominance information
750     //   2) Remove catch block subtrees, if they are now unreachable.
751     // If we run the dominance recomputation without removing the code, those catch blocks will
752     // not be part of the post order and won't be removed. If we don't run the dominance
753     // recomputation, we risk RemoveDeadBlocks not running it and leaving the graph in an
754     // inconsistent state. So, what we can do is run RemoveDeadBlocks and force a recomputation.
755     // Note that we are not guaranteed to remove a catch block if we have nested try blocks:
756     //
757     //   try {
758     //     ... nothing can throw. TryBoundary A ...
759     //     try {
760     //       ... can throw. TryBoundary B...
761     //     } catch (Error e) {}
762     //   } catch (Exception e) {}
763     //
764     // In the example above, we can remove the TryBoundary A but the Exception catch cannot be
765     // removed as the TryBoundary B might still throw into that catch. TryBoundary A and B don't get
766     // coalesced since they have different catch handlers.
767 
768     RemoveDeadBlocks(/* force_recomputation= */ true, any_block_in_loop);
769     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedTry, removed_tries);
770     return true;
771   } else {
772     return false;
773   }
774 }
775 
RemoveDeadBlocks(bool force_recomputation,bool force_loop_recomputation)776 bool HDeadCodeElimination::RemoveDeadBlocks(bool force_recomputation,
777                                             bool force_loop_recomputation) {
778   DCHECK_IMPLIES(force_loop_recomputation, force_recomputation);
779 
780   // Use local allocator for allocating memory.
781   ScopedArenaAllocator allocator(graph_->GetArenaStack());
782 
783   // Classify blocks as reachable/unreachable.
784   ArenaBitVector live_blocks(&allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE);
785   live_blocks.ClearAllBits();
786 
787   MarkReachableBlocks(graph_, &live_blocks);
788   bool removed_one_or_more_blocks = false;
789   bool rerun_dominance_and_loop_analysis = false;
790 
791   // Remove all dead blocks. Iterate in post order because removal needs the
792   // block's chain of dominators and nested loops need to be updated from the
793   // inside out.
794   for (HBasicBlock* block : graph_->GetPostOrder()) {
795     int id = block->GetBlockId();
796     if (!live_blocks.IsBitSet(id)) {
797       MaybeRecordDeadBlock(block);
798       block->DisconnectAndDelete();
799       removed_one_or_more_blocks = true;
800       if (block->IsInLoop()) {
801         rerun_dominance_and_loop_analysis = true;
802       }
803     }
804   }
805 
806   // If we removed at least one block, we need to recompute the full
807   // dominator tree and try block membership.
808   if (removed_one_or_more_blocks || force_recomputation) {
809     if (rerun_dominance_and_loop_analysis || force_loop_recomputation) {
810       graph_->ClearLoopInformation();
811       graph_->ClearDominanceInformation();
812       graph_->BuildDominatorTree();
813     } else {
814       graph_->ClearDominanceInformation();
815       graph_->ComputeDominanceInformation();
816       graph_->ComputeTryBlockInformation();
817     }
818   }
819   return removed_one_or_more_blocks;
820 }
821 
RemoveDeadInstructions()822 void HDeadCodeElimination::RemoveDeadInstructions() {
823   // Process basic blocks in post-order in the dominator tree, so that
824   // a dead instruction depending on another dead instruction is removed.
825   for (HBasicBlock* block : graph_->GetPostOrder()) {
826     // Traverse this block's instructions in backward order and remove
827     // the unused ones.
828     HBackwardInstructionIterator i(block->GetInstructions());
829     // Skip the first iteration, as the last instruction of a block is
830     // a branching instruction.
831     DCHECK(i.Current()->IsControlFlow());
832     for (i.Advance(); !i.Done(); i.Advance()) {
833       HInstruction* inst = i.Current();
834       DCHECK(!inst->IsControlFlow());
835       if (inst->IsDeadAndRemovable()) {
836         block->RemoveInstruction(inst);
837         MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadInstruction);
838       }
839     }
840   }
841 }
842 
UpdateGraphFlags()843 void HDeadCodeElimination::UpdateGraphFlags() {
844   bool has_monitor_operations = false;
845   bool has_simd = false;
846   bool has_bounds_checks = false;
847   bool has_always_throwing_invokes = false;
848 
849   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
850     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
851       HInstruction* instruction = it.Current();
852       if (instruction->IsMonitorOperation()) {
853         has_monitor_operations = true;
854       } else if (instruction->IsVecOperation()) {
855         has_simd = true;
856       } else if (instruction->IsBoundsCheck()) {
857         has_bounds_checks = true;
858       } else if (instruction->IsInvoke() && instruction->AsInvoke()->AlwaysThrows()) {
859         has_always_throwing_invokes = true;
860       }
861     }
862   }
863 
864   graph_->SetHasMonitorOperations(has_monitor_operations);
865   graph_->SetHasSIMD(has_simd);
866   graph_->SetHasBoundsChecks(has_bounds_checks);
867   graph_->SetHasAlwaysThrowingInvokes(has_always_throwing_invokes);
868 }
869 
Run()870 bool HDeadCodeElimination::Run() {
871   // Do not eliminate dead blocks if the graph has irreducible loops. We could
872   // support it, but that would require changes in our loop representation to handle
873   // multiple entry points. We decided it was not worth the complexity.
874   if (!graph_->HasIrreducibleLoops()) {
875     // Simplify graph to generate more dead block patterns.
876     ConnectSuccessiveBlocks();
877     bool did_any_simplification = false;
878     did_any_simplification |= SimplifyAlwaysThrows();
879     did_any_simplification |= SimplifyIfs();
880     did_any_simplification |= RemoveDeadBlocks();
881     // We call RemoveDeadBlocks before RemoveUnneededTries to remove the dead blocks from the
882     // previous optimizations. Otherwise, we might detect that a try has throwing instructions but
883     // they are actually dead code. RemoveUnneededTryBoundary will call RemoveDeadBlocks again if
884     // needed.
885     did_any_simplification |= RemoveUnneededTries();
886     if (did_any_simplification) {
887       // Connect successive blocks created by dead branches.
888       ConnectSuccessiveBlocks();
889     }
890   }
891   SsaRedundantPhiElimination(graph_).Run();
892   RemoveDeadInstructions();
893   UpdateGraphFlags();
894   return true;
895 }
896 
897 }  // namespace art
898