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