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/stl_util.h"
22 #include "ssa_phi_elimination.h"
23 
24 namespace art {
25 
MarkReachableBlocks(HGraph * graph,ArenaBitVector * visited)26 static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
27   ArenaVector<HBasicBlock*> worklist(graph->GetArena()->Adapter(kArenaAllocDCE));
28   constexpr size_t kDefaultWorlistSize = 8;
29   worklist.reserve(kDefaultWorlistSize);
30   visited->SetBit(graph->GetEntryBlock()->GetBlockId());
31   worklist.push_back(graph->GetEntryBlock());
32 
33   while (!worklist.empty()) {
34     HBasicBlock* block = worklist.back();
35     worklist.pop_back();
36     int block_id = block->GetBlockId();
37     DCHECK(visited->IsBitSet(block_id));
38 
39     ArrayRef<HBasicBlock* const> live_successors(block->GetSuccessors());
40     HInstruction* last_instruction = block->GetLastInstruction();
41     if (last_instruction->IsIf()) {
42       HIf* if_instruction = last_instruction->AsIf();
43       HInstruction* condition = if_instruction->InputAt(0);
44       if (condition->IsIntConstant()) {
45         if (condition->AsIntConstant()->IsTrue()) {
46           live_successors = live_successors.SubArray(0u, 1u);
47           DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
48         } else {
49           DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
50           live_successors = live_successors.SubArray(1u, 1u);
51           DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
52         }
53       }
54     } else if (last_instruction->IsPackedSwitch()) {
55       HPackedSwitch* switch_instruction = last_instruction->AsPackedSwitch();
56       HInstruction* switch_input = switch_instruction->InputAt(0);
57       if (switch_input->IsIntConstant()) {
58         int32_t switch_value = switch_input->AsIntConstant()->GetValue();
59         int32_t start_value = switch_instruction->GetStartValue();
60         // Note: Though the spec forbids packed-switch values to wrap around, we leave
61         // that task to the verifier and use unsigned arithmetic with it's "modulo 2^32"
62         // semantics to check if the value is in range, wrapped or not.
63         uint32_t switch_index =
64             static_cast<uint32_t>(switch_value) - static_cast<uint32_t>(start_value);
65         if (switch_index < switch_instruction->GetNumEntries()) {
66           live_successors = live_successors.SubArray(switch_index, 1u);
67           DCHECK_EQ(live_successors[0], block->GetSuccessors()[switch_index]);
68         } else {
69           live_successors = live_successors.SubArray(switch_instruction->GetNumEntries(), 1u);
70           DCHECK_EQ(live_successors[0], switch_instruction->GetDefaultBlock());
71         }
72       }
73     }
74 
75     for (HBasicBlock* successor : live_successors) {
76       // Add only those successors that have not been visited yet.
77       if (!visited->IsBitSet(successor->GetBlockId())) {
78         visited->SetBit(successor->GetBlockId());
79         worklist.push_back(successor);
80       }
81     }
82   }
83 }
84 
MaybeRecordDeadBlock(HBasicBlock * block)85 void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
86   if (stats_ != nullptr) {
87     stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
88                        block->GetPhis().CountSize() + block->GetInstructions().CountSize());
89   }
90 }
91 
MaybeRecordSimplifyIf()92 void HDeadCodeElimination::MaybeRecordSimplifyIf() {
93   if (stats_ != nullptr) {
94     stats_->RecordStat(MethodCompilationStat::kSimplifyIf);
95   }
96 }
97 
HasInput(HCondition * instruction,HInstruction * input)98 static bool HasInput(HCondition* instruction, HInstruction* input) {
99   return (instruction->InputAt(0) == input) ||
100          (instruction->InputAt(1) == input);
101 }
102 
HasEquality(IfCondition condition)103 static bool HasEquality(IfCondition condition) {
104   switch (condition) {
105     case kCondEQ:
106     case kCondLE:
107     case kCondGE:
108     case kCondBE:
109     case kCondAE:
110       return true;
111     case kCondNE:
112     case kCondLT:
113     case kCondGT:
114     case kCondB:
115     case kCondA:
116       return false;
117   }
118 }
119 
Evaluate(HCondition * condition,HInstruction * left,HInstruction * right)120 static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstruction* right) {
121   if (left == right && !Primitive::IsFloatingPointType(left->GetType())) {
122     return condition->GetBlock()->GetGraph()->GetIntConstant(
123         HasEquality(condition->GetCondition()) ? 1 : 0);
124   }
125 
126   if (!left->IsConstant() || !right->IsConstant()) {
127     return nullptr;
128   }
129 
130   if (left->IsIntConstant()) {
131     return condition->Evaluate(left->AsIntConstant(), right->AsIntConstant());
132   } else if (left->IsNullConstant()) {
133     return condition->Evaluate(left->AsNullConstant(), right->AsNullConstant());
134   } else if (left->IsLongConstant()) {
135     return condition->Evaluate(left->AsLongConstant(), right->AsLongConstant());
136   } else if (left->IsFloatConstant()) {
137     return condition->Evaluate(left->AsFloatConstant(), right->AsFloatConstant());
138   } else {
139     DCHECK(left->IsDoubleConstant());
140     return condition->Evaluate(left->AsDoubleConstant(), right->AsDoubleConstant());
141   }
142 }
143 
144 // Simplify the pattern:
145 //
146 //        B1    B2    ...
147 //       goto  goto  goto
148 //         \    |    /
149 //          \   |   /
150 //             B3
151 //     i1 = phi(input, input)
152 //     (i2 = condition on i1)
153 //        if i1 (or i2)
154 //          /     \
155 //         /       \
156 //        B4       B5
157 //
158 // Into:
159 //
160 //       B1      B2    ...
161 //        |      |      |
162 //       B4      B5    B?
163 //
164 // Note that individual edges can be redirected (for example B2->B3
165 // can be redirected as B2->B5) without applying this optimization
166 // to other incoming edges.
167 //
168 // This simplification cannot be applied to catch blocks, because
169 // exception handler edges do not represent normal control flow.
170 // Though in theory this could still apply to normal control flow
171 // going directly to a catch block, we cannot support it at the
172 // moment because the catch Phi's inputs do not correspond to the
173 // catch block's predecessors, so we cannot identify which
174 // predecessor corresponds to a given statically evaluated input.
175 //
176 // We do not apply this optimization to loop headers as this could
177 // create irreducible loops. We rely on the suspend check in the
178 // loop header to prevent the pattern match.
179 //
180 // Note that we rely on the dead code elimination to get rid of B3.
SimplifyIfs()181 bool HDeadCodeElimination::SimplifyIfs() {
182   bool simplified_one_or_more_ifs = false;
183   bool rerun_dominance_and_loop_analysis = false;
184 
185   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
186     HInstruction* last = block->GetLastInstruction();
187     HInstruction* first = block->GetFirstInstruction();
188     if (!block->IsCatchBlock() &&
189         last->IsIf() &&
190         block->HasSinglePhi() &&
191         block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
192       bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
193       bool has_only_phi_condition_and_if =
194           !has_only_phi_and_if &&
195           first->IsCondition() &&
196           HasInput(first->AsCondition(), block->GetFirstPhi()) &&
197           (first->GetNext() == last) &&
198           (last->InputAt(0) == first) &&
199           first->HasOnlyOneNonEnvironmentUse();
200 
201       if (has_only_phi_and_if || has_only_phi_condition_and_if) {
202         DCHECK(!block->IsLoopHeader());
203         HPhi* phi = block->GetFirstPhi()->AsPhi();
204         bool phi_input_is_left = (first->InputAt(0) == phi);
205 
206         // Walk over all inputs of the phis and update the control flow of
207         // predecessors feeding constants to the phi.
208         // Note that phi->InputCount() may change inside the loop.
209         for (size_t i = 0; i < phi->InputCount();) {
210           HInstruction* input = phi->InputAt(i);
211           HInstruction* value_to_check = nullptr;
212           if (has_only_phi_and_if) {
213             if (input->IsIntConstant()) {
214               value_to_check = input;
215             }
216           } else {
217             DCHECK(has_only_phi_condition_and_if);
218             if (phi_input_is_left) {
219               value_to_check = Evaluate(first->AsCondition(), input, first->InputAt(1));
220             } else {
221               value_to_check = Evaluate(first->AsCondition(), first->InputAt(0), input);
222             }
223           }
224           if (value_to_check == nullptr) {
225             // Could not evaluate to a constant, continue iterating over the inputs.
226             ++i;
227           } else {
228             HBasicBlock* predecessor_to_update = block->GetPredecessors()[i];
229             HBasicBlock* successor_to_update = nullptr;
230             if (value_to_check->AsIntConstant()->IsTrue()) {
231               successor_to_update = last->AsIf()->IfTrueSuccessor();
232             } else {
233               DCHECK(value_to_check->AsIntConstant()->IsFalse())
234                   << value_to_check->AsIntConstant()->GetValue();
235               successor_to_update = last->AsIf()->IfFalseSuccessor();
236             }
237             predecessor_to_update->ReplaceSuccessor(block, successor_to_update);
238             phi->RemoveInputAt(i);
239             simplified_one_or_more_ifs = true;
240             if (block->IsInLoop()) {
241               rerun_dominance_and_loop_analysis = true;
242             }
243             // For simplicity, don't create a dead block, let the dead code elimination
244             // pass deal with it.
245             if (phi->InputCount() == 1) {
246               break;
247             }
248           }
249         }
250         if (block->GetPredecessors().size() == 1) {
251           phi->ReplaceWith(phi->InputAt(0));
252           block->RemovePhi(phi);
253           if (has_only_phi_condition_and_if) {
254             // Evaluate here (and not wait for a constant folding pass) to open
255             // more opportunities for DCE.
256             HInstruction* result = first->AsCondition()->TryStaticEvaluation();
257             if (result != nullptr) {
258               first->ReplaceWith(result);
259               block->RemoveInstruction(first);
260             }
261           }
262         }
263         if (simplified_one_or_more_ifs) {
264           MaybeRecordSimplifyIf();
265         }
266       }
267     }
268   }
269   // We need to re-analyze the graph in order to run DCE afterwards.
270   if (simplified_one_or_more_ifs) {
271     if (rerun_dominance_and_loop_analysis) {
272       graph_->ClearLoopInformation();
273       graph_->ClearDominanceInformation();
274       graph_->BuildDominatorTree();
275     } else {
276       graph_->ClearDominanceInformation();
277       // We have introduced critical edges, remove them.
278       graph_->SimplifyCFG();
279       graph_->ComputeDominanceInformation();
280       graph_->ComputeTryBlockInformation();
281     }
282   }
283 
284   return simplified_one_or_more_ifs;
285 }
286 
ConnectSuccessiveBlocks()287 void HDeadCodeElimination::ConnectSuccessiveBlocks() {
288   // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
289   for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
290     HBasicBlock* block  = graph_->GetReversePostOrder()[i];
291     DCHECK(!block->IsEntryBlock());
292     while (block->GetLastInstruction()->IsGoto()) {
293       HBasicBlock* successor = block->GetSingleSuccessor();
294       if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
295         break;
296       }
297       DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
298       block->MergeWith(successor);
299       --size;
300       DCHECK_EQ(size, graph_->GetReversePostOrder().size());
301       DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
302       // Reiterate on this block in case it can be merged with its new successor.
303     }
304   }
305 }
306 
RemoveDeadBlocks()307 bool HDeadCodeElimination::RemoveDeadBlocks() {
308   // Classify blocks as reachable/unreachable.
309   ArenaAllocator* allocator = graph_->GetArena();
310   ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE);
311 
312   MarkReachableBlocks(graph_, &live_blocks);
313   bool removed_one_or_more_blocks = false;
314   bool rerun_dominance_and_loop_analysis = false;
315 
316   // Remove all dead blocks. Iterate in post order because removal needs the
317   // block's chain of dominators and nested loops need to be updated from the
318   // inside out.
319   for (HBasicBlock* block : graph_->GetPostOrder()) {
320     int id = block->GetBlockId();
321     if (!live_blocks.IsBitSet(id)) {
322       MaybeRecordDeadBlock(block);
323       block->DisconnectAndDelete();
324       removed_one_or_more_blocks = true;
325       if (block->IsInLoop()) {
326         rerun_dominance_and_loop_analysis = true;
327       }
328     }
329   }
330 
331   // If we removed at least one block, we need to recompute the full
332   // dominator tree and try block membership.
333   if (removed_one_or_more_blocks) {
334     if (rerun_dominance_and_loop_analysis) {
335       graph_->ClearLoopInformation();
336       graph_->ClearDominanceInformation();
337       graph_->BuildDominatorTree();
338     } else {
339       graph_->ClearDominanceInformation();
340       graph_->ComputeDominanceInformation();
341       graph_->ComputeTryBlockInformation();
342     }
343   }
344   return removed_one_or_more_blocks;
345 }
346 
RemoveDeadInstructions()347 void HDeadCodeElimination::RemoveDeadInstructions() {
348   // Process basic blocks in post-order in the dominator tree, so that
349   // a dead instruction depending on another dead instruction is removed.
350   for (HBasicBlock* block : graph_->GetPostOrder()) {
351     // Traverse this block's instructions in backward order and remove
352     // the unused ones.
353     HBackwardInstructionIterator i(block->GetInstructions());
354     // Skip the first iteration, as the last instruction of a block is
355     // a branching instruction.
356     DCHECK(i.Current()->IsControlFlow());
357     for (i.Advance(); !i.Done(); i.Advance()) {
358       HInstruction* inst = i.Current();
359       DCHECK(!inst->IsControlFlow());
360       if (inst->IsDeadAndRemovable()) {
361         block->RemoveInstruction(inst);
362         MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction);
363       }
364     }
365   }
366 }
367 
Run()368 void HDeadCodeElimination::Run() {
369   // Do not eliminate dead blocks if the graph has irreducible loops. We could
370   // support it, but that would require changes in our loop representation to handle
371   // multiple entry points. We decided it was not worth the complexity.
372   if (!graph_->HasIrreducibleLoops()) {
373     // Simplify graph to generate more dead block patterns.
374     ConnectSuccessiveBlocks();
375     bool did_any_simplification = false;
376     did_any_simplification |= SimplifyIfs();
377     did_any_simplification |= RemoveDeadBlocks();
378     if (did_any_simplification) {
379       // Connect successive blocks created by dead branches.
380       ConnectSuccessiveBlocks();
381     }
382   }
383   SsaRedundantPhiElimination(graph_).Run();
384   RemoveDeadInstructions();
385 }
386 
387 }  // namespace art
388