• 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 "base/array_ref.h"
20 #include "base/bit_vector-inl.h"
21 #include "base/scoped_arena_allocator.h"
22 #include "base/scoped_arena_containers.h"
23 #include "base/stl_util.h"
24 #include "ssa_phi_elimination.h"
25 
26 namespace art {
27 
MarkReachableBlocks(HGraph * graph,ArenaBitVector * visited)28 static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
29   // Use local allocator for allocating memory.
30   ScopedArenaAllocator allocator(graph->GetArenaStack());
31 
32   ScopedArenaVector<HBasicBlock*> worklist(allocator.Adapter(kArenaAllocDCE));
33   constexpr size_t kDefaultWorlistSize = 8;
34   worklist.reserve(kDefaultWorlistSize);
35   visited->SetBit(graph->GetEntryBlock()->GetBlockId());
36   worklist.push_back(graph->GetEntryBlock());
37 
38   while (!worklist.empty()) {
39     HBasicBlock* block = worklist.back();
40     worklist.pop_back();
41     int block_id = block->GetBlockId();
42     DCHECK(visited->IsBitSet(block_id));
43 
44     ArrayRef<HBasicBlock* const> live_successors(block->GetSuccessors());
45     HInstruction* last_instruction = block->GetLastInstruction();
46     if (last_instruction->IsIf()) {
47       HIf* if_instruction = last_instruction->AsIf();
48       HInstruction* condition = if_instruction->InputAt(0);
49       if (condition->IsIntConstant()) {
50         if (condition->AsIntConstant()->IsTrue()) {
51           live_successors = live_successors.SubArray(0u, 1u);
52           DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
53         } else {
54           DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
55           live_successors = live_successors.SubArray(1u, 1u);
56           DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
57         }
58       }
59     } else if (last_instruction->IsPackedSwitch()) {
60       HPackedSwitch* switch_instruction = last_instruction->AsPackedSwitch();
61       HInstruction* switch_input = switch_instruction->InputAt(0);
62       if (switch_input->IsIntConstant()) {
63         int32_t switch_value = switch_input->AsIntConstant()->GetValue();
64         int32_t start_value = switch_instruction->GetStartValue();
65         // Note: Though the spec forbids packed-switch values to wrap around, we leave
66         // that task to the verifier and use unsigned arithmetic with it's "modulo 2^32"
67         // semantics to check if the value is in range, wrapped or not.
68         uint32_t switch_index =
69             static_cast<uint32_t>(switch_value) - static_cast<uint32_t>(start_value);
70         if (switch_index < switch_instruction->GetNumEntries()) {
71           live_successors = live_successors.SubArray(switch_index, 1u);
72           DCHECK_EQ(live_successors[0], block->GetSuccessors()[switch_index]);
73         } else {
74           live_successors = live_successors.SubArray(switch_instruction->GetNumEntries(), 1u);
75           DCHECK_EQ(live_successors[0], switch_instruction->GetDefaultBlock());
76         }
77       }
78     }
79 
80     for (HBasicBlock* successor : live_successors) {
81       // Add only those successors that have not been visited yet.
82       if (!visited->IsBitSet(successor->GetBlockId())) {
83         visited->SetBit(successor->GetBlockId());
84         worklist.push_back(successor);
85       }
86     }
87   }
88 }
89 
MaybeRecordDeadBlock(HBasicBlock * block)90 void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
91   if (stats_ != nullptr) {
92     stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
93                        block->GetPhis().CountSize() + block->GetInstructions().CountSize());
94   }
95 }
96 
MaybeRecordSimplifyIf()97 void HDeadCodeElimination::MaybeRecordSimplifyIf() {
98   if (stats_ != nullptr) {
99     stats_->RecordStat(MethodCompilationStat::kSimplifyIf);
100   }
101 }
102 
HasInput(HCondition * instruction,HInstruction * input)103 static bool HasInput(HCondition* instruction, HInstruction* input) {
104   return (instruction->InputAt(0) == input) ||
105          (instruction->InputAt(1) == input);
106 }
107 
HasEquality(IfCondition condition)108 static bool HasEquality(IfCondition condition) {
109   switch (condition) {
110     case kCondEQ:
111     case kCondLE:
112     case kCondGE:
113     case kCondBE:
114     case kCondAE:
115       return true;
116     case kCondNE:
117     case kCondLT:
118     case kCondGT:
119     case kCondB:
120     case kCondA:
121       return false;
122   }
123 }
124 
Evaluate(HCondition * condition,HInstruction * left,HInstruction * right)125 static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstruction* right) {
126   if (left == right && !DataType::IsFloatingPointType(left->GetType())) {
127     return condition->GetBlock()->GetGraph()->GetIntConstant(
128         HasEquality(condition->GetCondition()) ? 1 : 0);
129   }
130 
131   if (!left->IsConstant() || !right->IsConstant()) {
132     return nullptr;
133   }
134 
135   if (left->IsIntConstant()) {
136     return condition->Evaluate(left->AsIntConstant(), right->AsIntConstant());
137   } else if (left->IsNullConstant()) {
138     return condition->Evaluate(left->AsNullConstant(), right->AsNullConstant());
139   } else if (left->IsLongConstant()) {
140     return condition->Evaluate(left->AsLongConstant(), right->AsLongConstant());
141   } else if (left->IsFloatConstant()) {
142     return condition->Evaluate(left->AsFloatConstant(), right->AsFloatConstant());
143   } else {
144     DCHECK(left->IsDoubleConstant());
145     return condition->Evaluate(left->AsDoubleConstant(), right->AsDoubleConstant());
146   }
147 }
148 
RemoveNonNullControlDependences(HBasicBlock * block,HBasicBlock * throws)149 static bool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) {
150   // Test for an if as last statement.
151   if (!block->EndsWithIf()) {
152     return false;
153   }
154   HIf* ifs = block->GetLastInstruction()->AsIf();
155   // Find either:
156   //   if obj == null
157   //     throws
158   //   else
159   //     not_throws
160   // or:
161   //   if obj != null
162   //     not_throws
163   //   else
164   //     throws
165   HInstruction* cond = ifs->InputAt(0);
166   HBasicBlock* not_throws = nullptr;
167   if (throws == ifs->IfTrueSuccessor() && cond->IsEqual()) {
168     not_throws = ifs->IfFalseSuccessor();
169   } else if (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
170     not_throws = ifs->IfTrueSuccessor();
171   } else {
172     return false;
173   }
174   DCHECK(cond->IsEqual() || cond->IsNotEqual());
175   HInstruction* obj = cond->InputAt(1);
176   if (obj->IsNullConstant()) {
177     obj = cond->InputAt(0);
178   } else if (!cond->InputAt(0)->IsNullConstant()) {
179     return false;
180   }
181   // Scan all uses of obj and find null check under control dependence.
182   HBoundType* bound = nullptr;
183   const HUseList<HInstruction*>& uses = obj->GetUses();
184   for (auto it = uses.begin(), end = uses.end(); it != end;) {
185     HInstruction* user = it->GetUser();
186     ++it;  // increment before possibly replacing
187     if (user->IsNullCheck()) {
188       HBasicBlock* user_block = user->GetBlock();
189       if (user_block != block &&
190           user_block != throws &&
191           block->Dominates(user_block)) {
192         if (bound == nullptr) {
193           ReferenceTypeInfo ti = obj->GetReferenceTypeInfo();
194           bound = new (obj->GetBlock()->GetGraph()->GetAllocator()) HBoundType(obj);
195           bound->SetUpperBound(ti, /*can_be_null*/ false);
196           bound->SetReferenceTypeInfo(ti);
197           bound->SetCanBeNull(false);
198           not_throws->InsertInstructionBefore(bound, not_throws->GetFirstInstruction());
199         }
200         user->ReplaceWith(bound);
201         user_block->RemoveInstruction(user);
202       }
203     }
204   }
205   return bound != nullptr;
206 }
207 
208 // Simplify the pattern:
209 //
210 //           B1
211 //          /  \
212 //          |   instr_1
213 //          |   ...
214 //          |   instr_n
215 //          |   foo()  // always throws
216 //          \   goto B2
217 //           \ /
218 //            B2
219 //
220 // Into:
221 //
222 //           B1
223 //          /  \
224 //          |  instr_1
225 //          |  ...
226 //          |  instr_n
227 //          |  foo()
228 //          |  goto Exit
229 //          |   |
230 //         B2  Exit
231 //
232 // Rationale:
233 // Removal of the never taken edge to B2 may expose
234 // other optimization opportunities, such as code sinking.
SimplifyAlwaysThrows()235 bool HDeadCodeElimination::SimplifyAlwaysThrows() {
236   HBasicBlock* exit = graph_->GetExitBlock();
237   if (exit == nullptr) {
238     return false;
239   }
240 
241   bool rerun_dominance_and_loop_analysis = false;
242 
243   // Order does not matter, just pick one.
244   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
245     if (block->GetTryCatchInformation() != nullptr) {
246       // We don't want to perform the simplify always throws optimizations for throws inside of
247       // tries since those throws might not go to the exit block. We do that by checking the
248       // TryCatchInformation of the blocks.
249       //
250       // As a special case the `catch_block` is the first block of the catch and it has
251       // TryCatchInformation. Other blocks in the catch don't have try catch information (as long as
252       // they are not part of an outer try). Knowing if a `catch_block` is part of an outer try is
253       // possible by checking its successors, but other restrictions of the simplify always throws
254       // optimization will block `catch_block` nevertheless (e.g. only one predecessor) so it is not
255       // worth the effort.
256 
257       // TODO(solanes): Maybe we can do a `goto catch` if inside of a try catch instead of going to
258       // the exit. If we do so, we have to take into account that we should go to the nearest valid
259       // catch i.e. one that would accept our exception type.
260       continue;
261     }
262 
263     HInstruction* last = block->GetLastInstruction();
264     HInstruction* prev = last->GetPrevious();
265     if (prev == nullptr) {
266       DCHECK_EQ(block->GetFirstInstruction(), block->GetLastInstruction());
267       continue;
268     }
269 
270     if (prev->AlwaysThrows() &&
271         last->IsGoto() &&
272         block->GetPhis().IsEmpty() &&
273         block->GetPredecessors().size() == 1u) {
274       HBasicBlock* pred = block->GetSinglePredecessor();
275       HBasicBlock* succ = block->GetSingleSuccessor();
276       // Ensure no computations are merged through throwing block.
277       // This does not prevent the optimization per se, but would
278       // require an elaborate clean up of the SSA graph.
279       if (succ != exit &&
280           !block->Dominates(pred) &&
281           pred->Dominates(succ) &&
282           succ->GetPredecessors().size() > 1u &&
283           succ->GetPhis().IsEmpty()) {
284         block->ReplaceSuccessor(succ, exit);
285         rerun_dominance_and_loop_analysis = true;
286         MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
287         // Perform a quick follow up optimization on object != null control dependences
288         // that is much cheaper to perform now than in a later phase.
289         if (RemoveNonNullControlDependences(pred, block)) {
290           MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
291         }
292       }
293     }
294   }
295 
296   // We need to re-analyze the graph in order to run DCE afterwards.
297   if (rerun_dominance_and_loop_analysis) {
298     graph_->ClearLoopInformation();
299     graph_->ClearDominanceInformation();
300     graph_->BuildDominatorTree();
301     return true;
302   }
303   return false;
304 }
305 
306 // Simplify the pattern:
307 //
308 //        B1    B2    ...
309 //       goto  goto  goto
310 //         \    |    /
311 //          \   |   /
312 //             B3
313 //     i1 = phi(input, input)
314 //     (i2 = condition on i1)
315 //        if i1 (or i2)
316 //          /     \
317 //         /       \
318 //        B4       B5
319 //
320 // Into:
321 //
322 //       B1      B2    ...
323 //        |      |      |
324 //       B4      B5    B?
325 //
326 // Note that individual edges can be redirected (for example B2->B3
327 // can be redirected as B2->B5) without applying this optimization
328 // to other incoming edges.
329 //
330 // This simplification cannot be applied to catch blocks, because
331 // exception handler edges do not represent normal control flow.
332 // Though in theory this could still apply to normal control flow
333 // going directly to a catch block, we cannot support it at the
334 // moment because the catch Phi's inputs do not correspond to the
335 // catch block's predecessors, so we cannot identify which
336 // predecessor corresponds to a given statically evaluated input.
337 //
338 // We do not apply this optimization to loop headers as this could
339 // create irreducible loops. We rely on the suspend check in the
340 // loop header to prevent the pattern match.
341 //
342 // Note that we rely on the dead code elimination to get rid of B3.
SimplifyIfs()343 bool HDeadCodeElimination::SimplifyIfs() {
344   bool simplified_one_or_more_ifs = false;
345   bool rerun_dominance_and_loop_analysis = false;
346 
347   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
348     HInstruction* last = block->GetLastInstruction();
349     HInstruction* first = block->GetFirstInstruction();
350     if (!block->IsCatchBlock() &&
351         last->IsIf() &&
352         block->HasSinglePhi() &&
353         block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
354       bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
355       bool has_only_phi_condition_and_if =
356           !has_only_phi_and_if &&
357           first->IsCondition() &&
358           HasInput(first->AsCondition(), block->GetFirstPhi()) &&
359           (first->GetNext() == last) &&
360           (last->InputAt(0) == first) &&
361           first->HasOnlyOneNonEnvironmentUse();
362 
363       if (has_only_phi_and_if || has_only_phi_condition_and_if) {
364         DCHECK(!block->IsLoopHeader());
365         HPhi* phi = block->GetFirstPhi()->AsPhi();
366         bool phi_input_is_left = (first->InputAt(0) == phi);
367 
368         // Walk over all inputs of the phis and update the control flow of
369         // predecessors feeding constants to the phi.
370         // Note that phi->InputCount() may change inside the loop.
371         for (size_t i = 0; i < phi->InputCount();) {
372           HInstruction* input = phi->InputAt(i);
373           HInstruction* value_to_check = nullptr;
374           if (has_only_phi_and_if) {
375             if (input->IsIntConstant()) {
376               value_to_check = input;
377             }
378           } else {
379             DCHECK(has_only_phi_condition_and_if);
380             if (phi_input_is_left) {
381               value_to_check = Evaluate(first->AsCondition(), input, first->InputAt(1));
382             } else {
383               value_to_check = Evaluate(first->AsCondition(), first->InputAt(0), input);
384             }
385           }
386           if (value_to_check == nullptr) {
387             // Could not evaluate to a constant, continue iterating over the inputs.
388             ++i;
389           } else {
390             HBasicBlock* predecessor_to_update = block->GetPredecessors()[i];
391             HBasicBlock* successor_to_update = nullptr;
392             if (value_to_check->AsIntConstant()->IsTrue()) {
393               successor_to_update = last->AsIf()->IfTrueSuccessor();
394             } else {
395               DCHECK(value_to_check->AsIntConstant()->IsFalse())
396                   << value_to_check->AsIntConstant()->GetValue();
397               successor_to_update = last->AsIf()->IfFalseSuccessor();
398             }
399             predecessor_to_update->ReplaceSuccessor(block, successor_to_update);
400             phi->RemoveInputAt(i);
401             simplified_one_or_more_ifs = true;
402             if (block->IsInLoop()) {
403               rerun_dominance_and_loop_analysis = true;
404             }
405             // For simplicity, don't create a dead block, let the dead code elimination
406             // pass deal with it.
407             if (phi->InputCount() == 1) {
408               break;
409             }
410           }
411         }
412         if (block->GetPredecessors().size() == 1) {
413           phi->ReplaceWith(phi->InputAt(0));
414           block->RemovePhi(phi);
415           if (has_only_phi_condition_and_if) {
416             // Evaluate here (and not wait for a constant folding pass) to open
417             // more opportunities for DCE.
418             HInstruction* result = first->AsCondition()->TryStaticEvaluation();
419             if (result != nullptr) {
420               first->ReplaceWith(result);
421               block->RemoveInstruction(first);
422             }
423           }
424         }
425         if (simplified_one_or_more_ifs) {
426           MaybeRecordSimplifyIf();
427         }
428       }
429     }
430   }
431   // We need to re-analyze the graph in order to run DCE afterwards.
432   if (simplified_one_or_more_ifs) {
433     if (rerun_dominance_and_loop_analysis) {
434       graph_->ClearLoopInformation();
435       graph_->ClearDominanceInformation();
436       graph_->BuildDominatorTree();
437     } else {
438       graph_->ClearDominanceInformation();
439       // We have introduced critical edges, remove them.
440       graph_->SimplifyCFG();
441       graph_->ComputeDominanceInformation();
442       graph_->ComputeTryBlockInformation();
443     }
444   }
445 
446   return simplified_one_or_more_ifs;
447 }
448 
ConnectSuccessiveBlocks()449 void HDeadCodeElimination::ConnectSuccessiveBlocks() {
450   // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
451   for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
452     HBasicBlock* block  = graph_->GetReversePostOrder()[i];
453     DCHECK(!block->IsEntryBlock());
454     while (block->GetLastInstruction()->IsGoto()) {
455       HBasicBlock* successor = block->GetSingleSuccessor();
456       if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
457         break;
458       }
459       DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
460       block->MergeWith(successor);
461       --size;
462       DCHECK_EQ(size, graph_->GetReversePostOrder().size());
463       DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
464       // Reiterate on this block in case it can be merged with its new successor.
465     }
466   }
467 }
468 
RemoveDeadBlocks()469 bool HDeadCodeElimination::RemoveDeadBlocks() {
470   // Use local allocator for allocating memory.
471   ScopedArenaAllocator allocator(graph_->GetArenaStack());
472 
473   // Classify blocks as reachable/unreachable.
474   ArenaBitVector live_blocks(&allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE);
475   live_blocks.ClearAllBits();
476 
477   MarkReachableBlocks(graph_, &live_blocks);
478   bool removed_one_or_more_blocks = false;
479   bool rerun_dominance_and_loop_analysis = false;
480 
481   // Remove all dead blocks. Iterate in post order because removal needs the
482   // block's chain of dominators and nested loops need to be updated from the
483   // inside out.
484   for (HBasicBlock* block : graph_->GetPostOrder()) {
485     int id = block->GetBlockId();
486     if (!live_blocks.IsBitSet(id)) {
487       MaybeRecordDeadBlock(block);
488       block->DisconnectAndDelete();
489       removed_one_or_more_blocks = true;
490       if (block->IsInLoop()) {
491         rerun_dominance_and_loop_analysis = true;
492       }
493     }
494   }
495 
496   // If we removed at least one block, we need to recompute the full
497   // dominator tree and try block membership.
498   if (removed_one_or_more_blocks) {
499     if (rerun_dominance_and_loop_analysis) {
500       graph_->ClearLoopInformation();
501       graph_->ClearDominanceInformation();
502       graph_->BuildDominatorTree();
503     } else {
504       graph_->ClearDominanceInformation();
505       graph_->ComputeDominanceInformation();
506       graph_->ComputeTryBlockInformation();
507     }
508   }
509   return removed_one_or_more_blocks;
510 }
511 
RemoveDeadInstructions()512 void HDeadCodeElimination::RemoveDeadInstructions() {
513   // Process basic blocks in post-order in the dominator tree, so that
514   // a dead instruction depending on another dead instruction is removed.
515   for (HBasicBlock* block : graph_->GetPostOrder()) {
516     // Traverse this block's instructions in backward order and remove
517     // the unused ones.
518     HBackwardInstructionIterator i(block->GetInstructions());
519     // Skip the first iteration, as the last instruction of a block is
520     // a branching instruction.
521     DCHECK(i.Current()->IsControlFlow());
522     for (i.Advance(); !i.Done(); i.Advance()) {
523       HInstruction* inst = i.Current();
524       DCHECK(!inst->IsControlFlow());
525       if (inst->IsDeadAndRemovable()) {
526         block->RemoveInstruction(inst);
527         MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadInstruction);
528       }
529     }
530   }
531 }
532 
Run()533 bool HDeadCodeElimination::Run() {
534   // Do not eliminate dead blocks if the graph has irreducible loops. We could
535   // support it, but that would require changes in our loop representation to handle
536   // multiple entry points. We decided it was not worth the complexity.
537   if (!graph_->HasIrreducibleLoops()) {
538     // Simplify graph to generate more dead block patterns.
539     ConnectSuccessiveBlocks();
540     bool did_any_simplification = false;
541     did_any_simplification |= SimplifyAlwaysThrows();
542     did_any_simplification |= SimplifyIfs();
543     did_any_simplification |= RemoveDeadBlocks();
544     if (did_any_simplification) {
545       // Connect successive blocks created by dead branches.
546       ConnectSuccessiveBlocks();
547     }
548   }
549   SsaRedundantPhiElimination(graph_).Run();
550   RemoveDeadInstructions();
551   return true;
552 }
553 
554 }  // namespace art
555