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