blob: cf49e398495dcc01876b186eec9917f45ab2953d [file] [log] [blame]
Roland Levillain72bceff2014-09-15 18:29:00 +01001/*
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
Santiago Aboy Solanes78f3d3a2022-07-15 14:30:05 +010019#include "android-base/logging.h"
David Brazdild9c90372016-09-14 16:53:55 +010020#include "base/array_ref.h"
Roland Levillain72bceff2014-09-15 18:29:00 +010021#include "base/bit_vector-inl.h"
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +000022#include "base/logging.h"
Vladimir Marko009d1662017-10-10 13:21:15 +010023#include "base/scoped_arena_allocator.h"
24#include "base/scoped_arena_containers.h"
Vladimir Marko2c45bc92016-10-25 16:54:12 +010025#include "base/stl_util.h"
Santiago Aboy Solanes78f3d3a2022-07-15 14:30:05 +010026#include "optimizing/nodes.h"
David Brazdil84daae52015-05-18 12:06:52 +010027#include "ssa_phi_elimination.h"
Roland Levillain72bceff2014-09-15 18:29:00 +010028
VladimĂ­r Marko434d9682022-11-04 14:04:17 +000029namespace art HIDDEN {
Roland Levillain72bceff2014-09-15 18:29:00 +010030
Vladimir Marko211c2112015-09-24 16:52:33 +010031static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
Vladimir Marko009d1662017-10-10 13:21:15 +010032 // Use local allocator for allocating memory.
33 ScopedArenaAllocator allocator(graph->GetArenaStack());
34
35 ScopedArenaVector<HBasicBlock*> worklist(allocator.Adapter(kArenaAllocDCE));
Vladimir Marko211c2112015-09-24 16:52:33 +010036 constexpr size_t kDefaultWorlistSize = 8;
37 worklist.reserve(kDefaultWorlistSize);
38 visited->SetBit(graph->GetEntryBlock()->GetBlockId());
39 worklist.push_back(graph->GetEntryBlock());
David Brazdil2d7352b2015-04-20 14:52:42 +010040
Vladimir Marko211c2112015-09-24 16:52:33 +010041 while (!worklist.empty()) {
42 HBasicBlock* block = worklist.back();
43 worklist.pop_back();
44 int block_id = block->GetBlockId();
45 DCHECK(visited->IsBitSet(block_id));
46
47 ArrayRef<HBasicBlock* const> live_successors(block->GetSuccessors());
48 HInstruction* last_instruction = block->GetLastInstruction();
49 if (last_instruction->IsIf()) {
50 HIf* if_instruction = last_instruction->AsIf();
51 HInstruction* condition = if_instruction->InputAt(0);
52 if (condition->IsIntConstant()) {
Roland Levillain1a653882016-03-18 18:05:57 +000053 if (condition->AsIntConstant()->IsTrue()) {
Vladimir Marko211c2112015-09-24 16:52:33 +010054 live_successors = live_successors.SubArray(0u, 1u);
55 DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
56 } else {
Roland Levillain1a653882016-03-18 18:05:57 +000057 DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
Vladimir Marko211c2112015-09-24 16:52:33 +010058 live_successors = live_successors.SubArray(1u, 1u);
59 DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
60 }
Mark Mendellfe57faa2015-09-18 09:26:15 -040061 }
Vladimir Marko211c2112015-09-24 16:52:33 +010062 } else if (last_instruction->IsPackedSwitch()) {
63 HPackedSwitch* switch_instruction = last_instruction->AsPackedSwitch();
64 HInstruction* switch_input = switch_instruction->InputAt(0);
65 if (switch_input->IsIntConstant()) {
66 int32_t switch_value = switch_input->AsIntConstant()->GetValue();
67 int32_t start_value = switch_instruction->GetStartValue();
Vladimir Marko430c4f52015-09-25 17:10:15 +010068 // Note: Though the spec forbids packed-switch values to wrap around, we leave
69 // that task to the verifier and use unsigned arithmetic with it's "modulo 2^32"
70 // semantics to check if the value is in range, wrapped or not.
71 uint32_t switch_index =
72 static_cast<uint32_t>(switch_value) - static_cast<uint32_t>(start_value);
Vladimir Marko211c2112015-09-24 16:52:33 +010073 if (switch_index < switch_instruction->GetNumEntries()) {
74 live_successors = live_successors.SubArray(switch_index, 1u);
Vladimir Markoec7802a2015-10-01 20:57:57 +010075 DCHECK_EQ(live_successors[0], block->GetSuccessors()[switch_index]);
Vladimir Marko211c2112015-09-24 16:52:33 +010076 } else {
77 live_successors = live_successors.SubArray(switch_instruction->GetNumEntries(), 1u);
78 DCHECK_EQ(live_successors[0], switch_instruction->GetDefaultBlock());
79 }
Mark Mendellfe57faa2015-09-18 09:26:15 -040080 }
81 }
Vladimir Marko211c2112015-09-24 16:52:33 +010082
83 for (HBasicBlock* successor : live_successors) {
84 // Add only those successors that have not been visited yet.
85 if (!visited->IsBitSet(successor->GetBlockId())) {
86 visited->SetBit(successor->GetBlockId());
87 worklist.push_back(successor);
88 }
David Brazdil2d7352b2015-04-20 14:52:42 +010089 }
90 }
91}
92
93void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
94 if (stats_ != nullptr) {
95 stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
96 block->GetPhis().CountSize() + block->GetInstructions().CountSize());
97 }
98}
99
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100100void HDeadCodeElimination::MaybeRecordSimplifyIf() {
101 if (stats_ != nullptr) {
102 stats_->RecordStat(MethodCompilationStat::kSimplifyIf);
Nicolas Geoffray09aa1472016-01-19 10:52:54 +0000103 }
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100104}
105
106static bool HasInput(HCondition* instruction, HInstruction* input) {
107 return (instruction->InputAt(0) == input) ||
108 (instruction->InputAt(1) == input);
109}
110
111static bool HasEquality(IfCondition condition) {
112 switch (condition) {
113 case kCondEQ:
114 case kCondLE:
115 case kCondGE:
116 case kCondBE:
117 case kCondAE:
118 return true;
119 case kCondNE:
120 case kCondLT:
121 case kCondGT:
122 case kCondB:
123 case kCondA:
124 return false;
125 }
126}
127
128static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstruction* right) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100129 if (left == right && !DataType::IsFloatingPointType(left->GetType())) {
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100130 return condition->GetBlock()->GetGraph()->GetIntConstant(
131 HasEquality(condition->GetCondition()) ? 1 : 0);
132 }
133
134 if (!left->IsConstant() || !right->IsConstant()) {
135 return nullptr;
136 }
137
138 if (left->IsIntConstant()) {
139 return condition->Evaluate(left->AsIntConstant(), right->AsIntConstant());
140 } else if (left->IsNullConstant()) {
141 return condition->Evaluate(left->AsNullConstant(), right->AsNullConstant());
142 } else if (left->IsLongConstant()) {
143 return condition->Evaluate(left->AsLongConstant(), right->AsLongConstant());
144 } else if (left->IsFloatConstant()) {
145 return condition->Evaluate(left->AsFloatConstant(), right->AsFloatConstant());
146 } else {
147 DCHECK(left->IsDoubleConstant());
148 return condition->Evaluate(left->AsDoubleConstant(), right->AsDoubleConstant());
149 }
150}
151
Aart Bik4c563ca2018-01-24 16:34:25 -0800152static bool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) {
153 // Test for an if as last statement.
154 if (!block->EndsWithIf()) {
155 return false;
156 }
157 HIf* ifs = block->GetLastInstruction()->AsIf();
158 // Find either:
159 // if obj == null
160 // throws
161 // else
162 // not_throws
163 // or:
164 // if obj != null
165 // not_throws
166 // else
167 // throws
168 HInstruction* cond = ifs->InputAt(0);
169 HBasicBlock* not_throws = nullptr;
170 if (throws == ifs->IfTrueSuccessor() && cond->IsEqual()) {
171 not_throws = ifs->IfFalseSuccessor();
172 } else if (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
173 not_throws = ifs->IfTrueSuccessor();
174 } else {
175 return false;
176 }
177 DCHECK(cond->IsEqual() || cond->IsNotEqual());
178 HInstruction* obj = cond->InputAt(1);
179 if (obj->IsNullConstant()) {
180 obj = cond->InputAt(0);
181 } else if (!cond->InputAt(0)->IsNullConstant()) {
182 return false;
183 }
Santiago Aboy Solanese05bc3e2023-02-20 14:26:23 +0000184
185 // We can't create a BoundType for an object with an invalid RTI.
186 const ReferenceTypeInfo ti = obj->GetReferenceTypeInfo();
187 if (!ti.IsValid()) {
188 return false;
189 }
190
Aart Bik4c563ca2018-01-24 16:34:25 -0800191 // Scan all uses of obj and find null check under control dependence.
192 HBoundType* bound = nullptr;
193 const HUseList<HInstruction*>& uses = obj->GetUses();
194 for (auto it = uses.begin(), end = uses.end(); it != end;) {
195 HInstruction* user = it->GetUser();
196 ++it; // increment before possibly replacing
197 if (user->IsNullCheck()) {
198 HBasicBlock* user_block = user->GetBlock();
199 if (user_block != block &&
200 user_block != throws &&
201 block->Dominates(user_block)) {
202 if (bound == nullptr) {
Aart Bik4c563ca2018-01-24 16:34:25 -0800203 bound = new (obj->GetBlock()->GetGraph()->GetAllocator()) HBoundType(obj);
204 bound->SetUpperBound(ti, /*can_be_null*/ false);
205 bound->SetReferenceTypeInfo(ti);
206 bound->SetCanBeNull(false);
207 not_throws->InsertInstructionBefore(bound, not_throws->GetFirstInstruction());
208 }
209 user->ReplaceWith(bound);
210 user_block->RemoveInstruction(user);
211 }
212 }
213 }
214 return bound != nullptr;
215}
216
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100217// Simplify the pattern:
218//
Aart Bika8b8e9b2018-01-09 11:01:02 -0800219// B1
220// / \
Santiago Aboy Solanescef72a62022-04-06 14:13:18 +0000221// | instr_1
222// | ...
223// | instr_n
Aart Bika8b8e9b2018-01-09 11:01:02 -0800224// | foo() // always throws
Santiago Aboy Solanes78f3d3a2022-07-15 14:30:05 +0100225// | instr_n+2
226// | ...
227// | instr_n+m
Aart Bika8b8e9b2018-01-09 11:01:02 -0800228// \ goto B2
229// \ /
230// B2
231//
232// Into:
233//
234// B1
235// / \
Santiago Aboy Solanescef72a62022-04-06 14:13:18 +0000236// | instr_1
237// | ...
238// | instr_n
Aart Bika8b8e9b2018-01-09 11:01:02 -0800239// | foo()
240// | goto Exit
241// | |
242// B2 Exit
243//
244// Rationale:
Santiago Aboy Solanesa3bd09c2022-07-29 14:09:28 +0100245// Removal of the never taken edge to B2 may expose other optimization opportunities, such as code
246// sinking.
247//
248// Note: The example above is a simple one that uses a `goto` but we could end the block with an If,
249// for example.
Aart Bika8b8e9b2018-01-09 11:01:02 -0800250bool HDeadCodeElimination::SimplifyAlwaysThrows() {
Aart Bika8b8e9b2018-01-09 11:01:02 -0800251 HBasicBlock* exit = graph_->GetExitBlock();
Santiago Aboy Solanes78f3d3a2022-07-15 14:30:05 +0100252 if (!graph_->HasAlwaysThrowingInvokes() || exit == nullptr) {
Aart Bika8b8e9b2018-01-09 11:01:02 -0800253 return false;
254 }
255
256 bool rerun_dominance_and_loop_analysis = false;
257
258 // Order does not matter, just pick one.
259 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Santiago Aboy Solanesa3bd09c2022-07-29 14:09:28 +0100260 if (block->IsTryBlock()) {
Santiago Aboy Solanescef72a62022-04-06 14:13:18 +0000261 // We don't want to perform the simplify always throws optimizations for throws inside of
Santiago Aboy Solanesa3bd09c2022-07-29 14:09:28 +0100262 // tries since those throws might not go to the exit block.
Santiago Aboy Solanescef72a62022-04-06 14:13:18 +0000263 continue;
264 }
265
Santiago Aboy Solanesa3bd09c2022-07-29 14:09:28 +0100266 // We iterate to find the first instruction that always throws. If two instructions always
267 // throw, the first one will throw and the second one will never be reached.
268 HInstruction* throwing_invoke = nullptr;
269 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
270 if (it.Current()->IsInvoke() && it.Current()->AsInvoke()->AlwaysThrows()) {
271 throwing_invoke = it.Current();
272 break;
Aart Bika8b8e9b2018-01-09 11:01:02 -0800273 }
274 }
Santiago Aboy Solanesa3bd09c2022-07-29 14:09:28 +0100275
276 if (throwing_invoke == nullptr) {
277 // No always-throwing instruction found. Continue with the rest of the blocks.
278 continue;
279 }
280
281 // If we are already pointing at the exit block we could still remove the instructions
282 // between the always throwing instruction, and the exit block. If we have no other
283 // instructions, just continue since there's nothing to do.
284 if (block->GetSuccessors().size() == 1 &&
285 block->GetSingleSuccessor() == exit &&
286 block->GetLastInstruction()->GetPrevious() == throwing_invoke) {
287 continue;
288 }
289
290 // We split the block at the throwing instruction, and the instructions after the throwing
291 // instructions will be disconnected from the graph after `block` points to the exit.
292 // `RemoveDeadBlocks` will take care of removing this new block and its instructions.
293 // Even though `SplitBefore` doesn't guarantee the graph to remain in SSA form, it is fine
294 // since we do not break it.
295 HBasicBlock* new_block = block->SplitBefore(throwing_invoke->GetNext(),
296 /* require_graph_not_in_ssa_form= */ false);
297 DCHECK_EQ(block->GetSingleSuccessor(), new_block);
298 block->ReplaceSuccessor(new_block, exit);
299
300 rerun_dominance_and_loop_analysis = true;
301 MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
302 // Perform a quick follow up optimization on object != null control dependences
303 // that is much cheaper to perform now than in a later phase.
304 // If there are multiple predecessors, none may end with a HIf as required in
305 // RemoveNonNullControlDependences because we split critical edges.
306 if (block->GetPredecessors().size() == 1u &&
307 RemoveNonNullControlDependences(block->GetSinglePredecessor(), block)) {
308 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
309 }
Aart Bika8b8e9b2018-01-09 11:01:02 -0800310 }
311
312 // We need to re-analyze the graph in order to run DCE afterwards.
313 if (rerun_dominance_and_loop_analysis) {
314 graph_->ClearLoopInformation();
315 graph_->ClearDominanceInformation();
316 graph_->BuildDominatorTree();
317 return true;
318 }
319 return false;
320}
321
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100322bool HDeadCodeElimination::SimplifyIfs() {
323 bool simplified_one_or_more_ifs = false;
324 bool rerun_dominance_and_loop_analysis = false;
325
Santiago Aboy Solanes10d68702023-01-24 18:05:10 +0000326 // Iterating in PostOrder it's better for MaybeAddPhi as it can add a Phi for multiple If
327 // instructions in a chain without updating the dominator chain. The branch redirection itself can
328 // work in PostOrder or ReversePostOrder without issues.
329 for (HBasicBlock* block : graph_->GetPostOrder()) {
330 if (block->IsCatchBlock()) {
331 // This simplification cannot be applied to catch blocks, because exception handler edges do
332 // not represent normal control flow. Though in theory this could still apply to normal
333 // control flow going directly to a catch block, we cannot support it at the moment because
334 // the catch Phi's inputs do not correspond to the catch block's predecessors, so we cannot
335 // identify which predecessor corresponds to a given statically evaluated input.
336 continue;
337 }
338
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100339 HInstruction* last = block->GetLastInstruction();
Santiago Aboy Solanes10d68702023-01-24 18:05:10 +0000340 if (!last->IsIf()) {
341 continue;
342 }
343
344 if (block->IsLoopHeader()) {
345 // We do not apply this optimization to loop headers as this could create irreducible loops.
346 continue;
347 }
348
349 // We will add a Phi which allows the simplification to take place in cases where it wouldn't.
350 MaybeAddPhi(block);
351
352 // TODO(solanes): Investigate support for multiple phis in `block`. We can potentially "push
353 // downwards" existing Phis into the true/false branches. For example, let's say we have another
354 // Phi: Phi(x1,x2,x3,x4,x5,x6). This could turn into Phi(x1,x2) in the true branch, Phi(x3,x4)
355 // in the false branch, and remain as Phi(x5,x6) in `block` (for edges that we couldn't
356 // redirect). We might even be able to remove some phis altogether as they will have only one
357 // value.
358 if (block->HasSinglePhi() &&
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100359 block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
Santiago Aboy Solanes10d68702023-01-24 18:05:10 +0000360 HInstruction* first = block->GetFirstInstruction();
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100361 bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
362 bool has_only_phi_condition_and_if =
363 !has_only_phi_and_if &&
364 first->IsCondition() &&
365 HasInput(first->AsCondition(), block->GetFirstPhi()) &&
366 (first->GetNext() == last) &&
367 (last->InputAt(0) == first) &&
368 first->HasOnlyOneNonEnvironmentUse();
369
370 if (has_only_phi_and_if || has_only_phi_condition_and_if) {
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100371 HPhi* phi = block->GetFirstPhi()->AsPhi();
372 bool phi_input_is_left = (first->InputAt(0) == phi);
373
374 // Walk over all inputs of the phis and update the control flow of
375 // predecessors feeding constants to the phi.
376 // Note that phi->InputCount() may change inside the loop.
377 for (size_t i = 0; i < phi->InputCount();) {
378 HInstruction* input = phi->InputAt(i);
379 HInstruction* value_to_check = nullptr;
380 if (has_only_phi_and_if) {
381 if (input->IsIntConstant()) {
382 value_to_check = input;
383 }
384 } else {
385 DCHECK(has_only_phi_condition_and_if);
386 if (phi_input_is_left) {
387 value_to_check = Evaluate(first->AsCondition(), input, first->InputAt(1));
388 } else {
389 value_to_check = Evaluate(first->AsCondition(), first->InputAt(0), input);
390 }
391 }
392 if (value_to_check == nullptr) {
393 // Could not evaluate to a constant, continue iterating over the inputs.
394 ++i;
395 } else {
396 HBasicBlock* predecessor_to_update = block->GetPredecessors()[i];
397 HBasicBlock* successor_to_update = nullptr;
398 if (value_to_check->AsIntConstant()->IsTrue()) {
399 successor_to_update = last->AsIf()->IfTrueSuccessor();
400 } else {
401 DCHECK(value_to_check->AsIntConstant()->IsFalse())
402 << value_to_check->AsIntConstant()->GetValue();
403 successor_to_update = last->AsIf()->IfFalseSuccessor();
404 }
405 predecessor_to_update->ReplaceSuccessor(block, successor_to_update);
406 phi->RemoveInputAt(i);
407 simplified_one_or_more_ifs = true;
408 if (block->IsInLoop()) {
409 rerun_dominance_and_loop_analysis = true;
410 }
411 // For simplicity, don't create a dead block, let the dead code elimination
412 // pass deal with it.
413 if (phi->InputCount() == 1) {
414 break;
415 }
416 }
417 }
418 if (block->GetPredecessors().size() == 1) {
419 phi->ReplaceWith(phi->InputAt(0));
420 block->RemovePhi(phi);
421 if (has_only_phi_condition_and_if) {
422 // Evaluate here (and not wait for a constant folding pass) to open
423 // more opportunities for DCE.
424 HInstruction* result = first->AsCondition()->TryStaticEvaluation();
425 if (result != nullptr) {
426 first->ReplaceWith(result);
427 block->RemoveInstruction(first);
428 }
429 }
430 }
431 if (simplified_one_or_more_ifs) {
432 MaybeRecordSimplifyIf();
433 }
434 }
435 }
436 }
437 // We need to re-analyze the graph in order to run DCE afterwards.
438 if (simplified_one_or_more_ifs) {
439 if (rerun_dominance_and_loop_analysis) {
440 graph_->ClearLoopInformation();
441 graph_->ClearDominanceInformation();
442 graph_->BuildDominatorTree();
443 } else {
444 graph_->ClearDominanceInformation();
445 // We have introduced critical edges, remove them.
446 graph_->SimplifyCFG();
447 graph_->ComputeDominanceInformation();
448 graph_->ComputeTryBlockInformation();
449 }
450 }
451
452 return simplified_one_or_more_ifs;
453}
454
Santiago Aboy Solanes10d68702023-01-24 18:05:10 +0000455void HDeadCodeElimination::MaybeAddPhi(HBasicBlock* block) {
456 DCHECK(block->GetLastInstruction()->IsIf());
457 HIf* if_instruction = block->GetLastInstruction()->AsIf();
458 if (if_instruction->InputAt(0)->IsConstant()) {
459 // Constant values are handled in RemoveDeadBlocks.
460 return;
461 }
462
463 if (block->GetNumberOfPredecessors() < 2u) {
464 // Nothing to redirect.
465 return;
466 }
467
468 if (!block->GetPhis().IsEmpty()) {
469 // SimplifyIf doesn't currently work with multiple phis. Adding a phi here won't help that
470 // optimization.
471 return;
472 }
473
474 HBasicBlock* dominator = block->GetDominator();
475 if (!dominator->EndsWithIf()) {
476 return;
477 }
478
479 HInstruction* input = if_instruction->InputAt(0);
480 HInstruction* dominator_input = dominator->GetLastInstruction()->AsIf()->InputAt(0);
481 const bool same_input = dominator_input == input;
482 if (!same_input) {
483 // Try to see if the dominator has the opposite input (e.g. if(cond) and if(!cond)). If that's
484 // the case, we can perform the optimization with the false and true branches reversed.
485 if (!dominator_input->IsCondition() || !input->IsCondition()) {
486 return;
487 }
488
489 HCondition* block_cond = input->AsCondition();
490 HCondition* dominator_cond = dominator_input->AsCondition();
491
492 if (block_cond->GetLeft() != dominator_cond->GetLeft() ||
493 block_cond->GetRight() != dominator_cond->GetRight() ||
494 block_cond->GetOppositeCondition() != dominator_cond->GetCondition()) {
495 return;
496 }
497 }
498
499 if (kIsDebugBuild) {
500 // `block`'s successors should have only one predecessor. Otherwise, we have a critical edge in
501 // the graph.
502 for (HBasicBlock* succ : block->GetSuccessors()) {
503 DCHECK_EQ(succ->GetNumberOfPredecessors(), 1u);
504 }
505 }
506
507 const size_t pred_size = block->GetNumberOfPredecessors();
508 HPhi* new_phi = new (graph_->GetAllocator())
509 HPhi(graph_->GetAllocator(), kNoRegNumber, pred_size, DataType::Type::kInt32);
510
511 for (size_t index = 0; index < pred_size; index++) {
512 HBasicBlock* pred = block->GetPredecessors()[index];
513 const bool dominated_by_true =
514 dominator->GetLastInstruction()->AsIf()->IfTrueSuccessor()->Dominates(pred);
515 const bool dominated_by_false =
516 dominator->GetLastInstruction()->AsIf()->IfFalseSuccessor()->Dominates(pred);
517 if (dominated_by_true == dominated_by_false) {
518 // In this case, we can't know if we are coming from the true branch, or the false branch. It
519 // happens in cases like:
520 // 1 (outer if)
521 // / \
522 // 2 3 (inner if)
523 // | / \
524 // | 4 5
525 // \/ |
526 // 6 |
527 // \ |
528 // 7 (has the same if(cond) as 1)
529 // |
530 // 8
531 // `7` (which would be `block` in this example), and `6` will come from both the true path and
532 // the false path of `1`. We bumped into something similar in SelectGenerator. See
533 // HSelectGenerator::TryFixupDoubleDiamondPattern.
534 // TODO(solanes): Figure out if we can fix up the graph into a double diamond in a generic way
535 // so that DeadCodeElimination and SelectGenerator can take advantage of it.
536
537 if (!same_input) {
538 // `1` and `7` having the opposite condition is a case we are missing. We could potentially
539 // add a BooleanNot instruction to be able to add the Phi, but it seems like overkill since
540 // this case is not that common.
541 return;
542 }
543
544 // The Phi will have `0`, `1`, and `cond` as inputs. If SimplifyIf redirects 0s and 1s, we
545 // will end up with Phi(cond,...,cond) which will be replaced by `cond`. Effectively, we will
546 // redirect edges that we are able to redirect and the rest will remain as before (i.e. we
547 // won't have an extra Phi).
548 new_phi->SetRawInputAt(index, input);
549 } else {
550 // Redirect to either the true branch (1), or the false branch (0).
551 // Given that `dominated_by_true` is the exact opposite of `dominated_by_false`,
552 // `(same_input && dominated_by_true) || (!same_input && dominated_by_false)` is equivalent to
553 // `same_input == dominated_by_true`.
554 new_phi->SetRawInputAt(
555 index,
556 same_input == dominated_by_true ? graph_->GetIntConstant(1) : graph_->GetIntConstant(0));
557 }
558 }
559
560 block->AddPhi(new_phi);
561 if_instruction->ReplaceInput(new_phi, 0);
562
563 // Remove the old input now, if possible. This allows the branch redirection in SimplifyIf to
564 // work without waiting for another pass of DCE.
565 if (input->IsDeadAndRemovable()) {
566 DCHECK(!same_input)
567 << " if both blocks have the same condition, it shouldn't be dead and removable since the "
568 << "dominator block's If instruction would be using that condition.";
569 input->GetBlock()->RemoveInstruction(input);
570 }
571 MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyIfAddedPhi);
572}
573
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100574void HDeadCodeElimination::ConnectSuccessiveBlocks() {
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100575 // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
576 for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
577 HBasicBlock* block = graph_->GetReversePostOrder()[i];
578 DCHECK(!block->IsEntryBlock());
579 while (block->GetLastInstruction()->IsGoto()) {
580 HBasicBlock* successor = block->GetSingleSuccessor();
581 if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
582 break;
583 }
584 DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
585 block->MergeWith(successor);
586 --size;
587 DCHECK_EQ(size, graph_->GetReversePostOrder().size());
588 DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
589 // Reiterate on this block in case it can be merged with its new successor.
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100590 }
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100591 }
592}
593
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000594struct HDeadCodeElimination::TryBelongingInformation {
Santiago Aboy Solanes44ba4232022-11-23 12:27:57 +0000595 explicit TryBelongingInformation(ScopedArenaAllocator* allocator)
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000596 : blocks_in_try(allocator->Adapter(kArenaAllocDCE)),
597 coalesced_try_entries(allocator->Adapter(kArenaAllocDCE)) {}
598
599 // Which blocks belong in the try.
600 ScopedArenaSet<HBasicBlock*> blocks_in_try;
601 // Which other try entries are referencing this same try.
602 ScopedArenaSet<HBasicBlock*> coalesced_try_entries;
603};
604
605bool HDeadCodeElimination::CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info) {
606 for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
607 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
608 if (it.Current()->CanThrow()) {
609 return false;
610 }
611 }
612 }
613 return true;
614}
615
616void HDeadCodeElimination::DisconnectHandlersAndUpdateTryBoundary(
617 HBasicBlock* block,
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000618 /* out */ bool* any_block_in_loop) {
619 if (block->IsInLoop()) {
620 *any_block_in_loop = true;
621 }
622
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000623 // Disconnect the handlers.
624 while (block->GetSuccessors().size() > 1) {
625 HBasicBlock* handler = block->GetSuccessors()[1];
626 DCHECK(handler->IsCatchBlock());
627 block->RemoveSuccessor(handler);
628 handler->RemovePredecessor(block);
629 if (handler->IsInLoop()) {
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000630 *any_block_in_loop = true;
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000631 }
632 }
633
634 // Change TryBoundary to Goto.
635 DCHECK(block->EndsWithTryBoundary());
636 HInstruction* last = block->GetLastInstruction();
637 block->RemoveInstruction(last);
638 block->AddInstruction(new (graph_->GetAllocator()) HGoto(last->GetDexPc()));
639 DCHECK_EQ(block->GetSuccessors().size(), 1u);
640}
641
642void HDeadCodeElimination::RemoveTry(HBasicBlock* try_entry,
643 const TryBelongingInformation& try_belonging_info,
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000644 /* out */ bool* any_block_in_loop) {
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000645 // Update all try entries.
646 DCHECK(try_entry->EndsWithTryBoundary());
647 DCHECK(try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000648 DisconnectHandlersAndUpdateTryBoundary(try_entry, any_block_in_loop);
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000649
650 for (HBasicBlock* other_try_entry : try_belonging_info.coalesced_try_entries) {
651 DCHECK(other_try_entry->EndsWithTryBoundary());
652 DCHECK(other_try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000653 DisconnectHandlersAndUpdateTryBoundary(other_try_entry, any_block_in_loop);
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000654 }
655
656 // Update the blocks in the try.
657 for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
658 // Update the try catch information since now the try doesn't exist.
659 block->SetTryCatchInformation(nullptr);
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000660 if (block->IsInLoop()) {
661 *any_block_in_loop = true;
662 }
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000663
664 if (block->EndsWithTryBoundary()) {
665 // Try exits.
666 DCHECK(!block->GetLastInstruction()->AsTryBoundary()->IsEntry());
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000667 DisconnectHandlersAndUpdateTryBoundary(block, any_block_in_loop);
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000668
669 if (block->GetSingleSuccessor()->IsExitBlock()) {
Santiago Aboy Solanese22e84b2022-11-28 11:49:16 +0000670 // `block` used to be a single exit TryBoundary that got turned into a Goto. It
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000671 // is now pointing to the exit which we don't allow. To fix it, we disconnect
Santiago Aboy Solanese22e84b2022-11-28 11:49:16 +0000672 // `block` from its predecessor and RemoveDeadBlocks will remove it from the
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000673 // graph.
674 DCHECK(block->IsSingleGoto());
675 HBasicBlock* predecessor = block->GetSinglePredecessor();
676 predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());
Santiago Aboy Solanese22e84b2022-11-28 11:49:16 +0000677
678 if (!block->GetDominatedBlocks().empty()) {
679 // Update domination tree if `block` dominates a block to keep the graph consistent.
680 DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
681 DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
682 predecessor->AddDominatedBlock(graph_->GetExitBlock());
683 graph_->GetExitBlock()->SetDominator(predecessor);
684 block->RemoveDominatedBlock(graph_->GetExitBlock());
685 }
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000686 }
687 }
688 }
689}
690
691bool HDeadCodeElimination::RemoveUnneededTries() {
692 if (!graph_->HasTryCatch()) {
693 return false;
694 }
695
696 // Use local allocator for allocating memory.
697 ScopedArenaAllocator allocator(graph_->GetArenaStack());
698
699 // Collect which blocks are part of which try.
700 std::unordered_map<HBasicBlock*, TryBelongingInformation> tries;
701 for (HBasicBlock* block : graph_->GetReversePostOrderSkipEntryBlock()) {
702 if (block->IsTryBlock()) {
703 HBasicBlock* key = block->GetTryCatchInformation()->GetTryEntry().GetBlock();
704 auto it = tries.find(key);
705 if (it == tries.end()) {
706 it = tries.insert({key, TryBelongingInformation(&allocator)}).first;
707 }
708 it->second.blocks_in_try.insert(block);
709 }
710 }
711
712 // Deduplicate the tries which have different try entries but they are really the same try.
713 for (auto it = tries.begin(); it != tries.end(); it++) {
714 DCHECK(it->first->EndsWithTryBoundary());
715 HTryBoundary* try_boundary = it->first->GetLastInstruction()->AsTryBoundary();
716 for (auto other_it = next(it); other_it != tries.end(); /*other_it++ in the loop*/) {
717 DCHECK(other_it->first->EndsWithTryBoundary());
718 HTryBoundary* other_try_boundary = other_it->first->GetLastInstruction()->AsTryBoundary();
719 if (try_boundary->HasSameExceptionHandlersAs(*other_try_boundary)) {
720 // Merge the entries as they are really the same one.
721 // Block merging.
722 it->second.blocks_in_try.insert(other_it->second.blocks_in_try.begin(),
723 other_it->second.blocks_in_try.end());
724
725 // Add the coalesced try entry to update it too.
726 it->second.coalesced_try_entries.insert(other_it->first);
727
728 // Erase the other entry.
729 other_it = tries.erase(other_it);
730 } else {
731 other_it++;
732 }
733 }
734 }
735
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000736 size_t removed_tries = 0;
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000737 bool any_block_in_loop = false;
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000738
739 // Check which tries contain throwing instructions.
740 for (const auto& entry : tries) {
741 if (CanPerformTryRemoval(entry.second)) {
742 ++removed_tries;
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000743 RemoveTry(entry.first, entry.second, &any_block_in_loop);
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000744 }
745 }
746
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000747 if (removed_tries != 0) {
748 // We want to:
749 // 1) Update the dominance information
750 // 2) Remove catch block subtrees, if they are now unreachable.
751 // If we run the dominance recomputation without removing the code, those catch blocks will
752 // not be part of the post order and won't be removed. If we don't run the dominance
753 // recomputation, we risk RemoveDeadBlocks not running it and leaving the graph in an
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000754 // inconsistent state. So, what we can do is run RemoveDeadBlocks and force a recomputation.
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000755 // Note that we are not guaranteed to remove a catch block if we have nested try blocks:
756 //
757 // try {
758 // ... nothing can throw. TryBoundary A ...
759 // try {
760 // ... can throw. TryBoundary B...
761 // } catch (Error e) {}
762 // } catch (Exception e) {}
763 //
764 // In the example above, we can remove the TryBoundary A but the Exception catch cannot be
765 // removed as the TryBoundary B might still throw into that catch. TryBoundary A and B don't get
766 // coalesced since they have different catch handlers.
767
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000768 RemoveDeadBlocks(/* force_recomputation= */ true, any_block_in_loop);
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000769 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedTry, removed_tries);
770 return true;
771 } else {
772 return false;
773 }
774}
775
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000776bool HDeadCodeElimination::RemoveDeadBlocks(bool force_recomputation,
777 bool force_loop_recomputation) {
778 DCHECK_IMPLIES(force_loop_recomputation, force_recomputation);
779
Vladimir Marko009d1662017-10-10 13:21:15 +0100780 // Use local allocator for allocating memory.
781 ScopedArenaAllocator allocator(graph_->GetArenaStack());
782
David Brazdil2d7352b2015-04-20 14:52:42 +0100783 // Classify blocks as reachable/unreachable.
Vladimir Marko009d1662017-10-10 13:21:15 +0100784 ArenaBitVector live_blocks(&allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE);
785 live_blocks.ClearAllBits();
David Brazdila4b8c212015-05-07 09:59:30 +0100786
Vladimir Marko211c2112015-09-24 16:52:33 +0100787 MarkReachableBlocks(graph_, &live_blocks);
Nicolas Geoffray1f82ecc2015-06-24 12:20:24 +0100788 bool removed_one_or_more_blocks = false;
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000789 bool rerun_dominance_and_loop_analysis = false;
David Brazdil2d7352b2015-04-20 14:52:42 +0100790
David Brazdila4b8c212015-05-07 09:59:30 +0100791 // Remove all dead blocks. Iterate in post order because removal needs the
792 // block's chain of dominators and nested loops need to be updated from the
793 // inside out.
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100794 for (HBasicBlock* block : graph_->GetPostOrder()) {
David Brazdila4b8c212015-05-07 09:59:30 +0100795 int id = block->GetBlockId();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000796 if (!live_blocks.IsBitSet(id)) {
David Brazdil69a28042015-04-29 17:16:07 +0100797 MaybeRecordDeadBlock(block);
798 block->DisconnectAndDelete();
Nicolas Geoffray1f82ecc2015-06-24 12:20:24 +0100799 removed_one_or_more_blocks = true;
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000800 if (block->IsInLoop()) {
801 rerun_dominance_and_loop_analysis = true;
802 }
David Brazdil2d7352b2015-04-20 14:52:42 +0100803 }
David Brazdil2d7352b2015-04-20 14:52:42 +0100804 }
805
Nicolas Geoffray1f82ecc2015-06-24 12:20:24 +0100806 // If we removed at least one block, we need to recompute the full
David Brazdil8a7c0fe2015-11-02 20:24:55 +0000807 // dominator tree and try block membership.
Santiago Aboy Solanesfa55aa02022-11-29 11:42:20 +0000808 if (removed_one_or_more_blocks || force_recomputation) {
809 if (rerun_dominance_and_loop_analysis || force_loop_recomputation) {
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000810 graph_->ClearLoopInformation();
811 graph_->ClearDominanceInformation();
812 graph_->BuildDominatorTree();
813 } else {
814 graph_->ClearDominanceInformation();
815 graph_->ComputeDominanceInformation();
816 graph_->ComputeTryBlockInformation();
817 }
Nicolas Geoffray1f82ecc2015-06-24 12:20:24 +0100818 }
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100819 return removed_one_or_more_blocks;
David Brazdil2d7352b2015-04-20 14:52:42 +0100820}
821
822void HDeadCodeElimination::RemoveDeadInstructions() {
Roland Levillain72bceff2014-09-15 18:29:00 +0100823 // Process basic blocks in post-order in the dominator tree, so that
David Brazdil2d7352b2015-04-20 14:52:42 +0100824 // a dead instruction depending on another dead instruction is removed.
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100825 for (HBasicBlock* block : graph_->GetPostOrder()) {
Roland Levillain72bceff2014-09-15 18:29:00 +0100826 // Traverse this block's instructions in backward order and remove
827 // the unused ones.
828 HBackwardInstructionIterator i(block->GetInstructions());
829 // Skip the first iteration, as the last instruction of a block is
830 // a branching instruction.
831 DCHECK(i.Current()->IsControlFlow());
832 for (i.Advance(); !i.Done(); i.Advance()) {
833 HInstruction* inst = i.Current();
834 DCHECK(!inst->IsControlFlow());
Aart Bik482095d2016-10-10 15:39:10 -0700835 if (inst->IsDeadAndRemovable()) {
Roland Levillain72bceff2014-09-15 18:29:00 +0100836 block->RemoveInstruction(inst);
Igor Murashkin1e065a52017-08-09 13:20:34 -0700837 MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadInstruction);
Roland Levillain72bceff2014-09-15 18:29:00 +0100838 }
839 }
840 }
841}
842
Santiago Aboy Solanes74da6682022-12-16 19:28:47 +0000843void HDeadCodeElimination::UpdateGraphFlags() {
844 bool has_monitor_operations = false;
845 bool has_simd = false;
846 bool has_bounds_checks = false;
847 bool has_always_throwing_invokes = false;
848
849 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
850 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
851 HInstruction* instruction = it.Current();
852 if (instruction->IsMonitorOperation()) {
853 has_monitor_operations = true;
854 } else if (instruction->IsVecOperation()) {
855 has_simd = true;
856 } else if (instruction->IsBoundsCheck()) {
857 has_bounds_checks = true;
858 } else if (instruction->IsInvoke() && instruction->AsInvoke()->AlwaysThrows()) {
859 has_always_throwing_invokes = true;
860 }
861 }
862 }
863
864 graph_->SetHasMonitorOperations(has_monitor_operations);
865 graph_->SetHasSIMD(has_simd);
866 graph_->SetHasBoundsChecks(has_bounds_checks);
867 graph_->SetHasAlwaysThrowingInvokes(has_always_throwing_invokes);
868}
869
Aart Bik24773202018-04-26 10:28:51 -0700870bool HDeadCodeElimination::Run() {
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100871 // Do not eliminate dead blocks if the graph has irreducible loops. We could
872 // support it, but that would require changes in our loop representation to handle
873 // multiple entry points. We decided it was not worth the complexity.
874 if (!graph_->HasIrreducibleLoops()) {
875 // Simplify graph to generate more dead block patterns.
876 ConnectSuccessiveBlocks();
877 bool did_any_simplification = false;
Aart Bika8b8e9b2018-01-09 11:01:02 -0800878 did_any_simplification |= SimplifyAlwaysThrows();
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100879 did_any_simplification |= SimplifyIfs();
880 did_any_simplification |= RemoveDeadBlocks();
Santiago Aboy Solanesb2d364d2022-11-10 10:26:31 +0000881 // We call RemoveDeadBlocks before RemoveUnneededTries to remove the dead blocks from the
882 // previous optimizations. Otherwise, we might detect that a try has throwing instructions but
883 // they are actually dead code. RemoveUnneededTryBoundary will call RemoveDeadBlocks again if
884 // needed.
885 did_any_simplification |= RemoveUnneededTries();
Nicolas Geoffraydac9b192016-07-15 10:46:17 +0100886 if (did_any_simplification) {
887 // Connect successive blocks created by dead branches.
888 ConnectSuccessiveBlocks();
889 }
890 }
David Brazdil84daae52015-05-18 12:06:52 +0100891 SsaRedundantPhiElimination(graph_).Run();
David Brazdil2d7352b2015-04-20 14:52:42 +0100892 RemoveDeadInstructions();
Santiago Aboy Solanes74da6682022-12-16 19:28:47 +0000893 UpdateGraphFlags();
Aart Bik24773202018-04-26 10:28:51 -0700894 return true;
David Brazdil2d7352b2015-04-20 14:52:42 +0100895}
896
Roland Levillain72bceff2014-09-15 18:29:00 +0100897} // namespace art