blob: 5370f43b4f4e0b5b57b79634263fd700942f58ff [file] [log] [blame]
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +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 "ssa_phi_elimination.h"
18
Vladimir Markoc9ef1682016-05-10 13:31:23 +010019#include "base/arena_bit_vector.h"
Vladimir Markof3612672017-10-10 10:38:16 +010020#include "base/scoped_arena_allocator.h"
21#include "base/scoped_arena_containers.h"
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +000022#include "base/bit_vector-inl.h"
David Brazdil809d70f2015-11-19 10:29:39 +000023
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010024namespace art {
25
Aart Bik24773202018-04-26 10:28:51 -070026bool SsaDeadPhiElimination::Run() {
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000027 MarkDeadPhis();
28 EliminateDeadPhis();
Aart Bik24773202018-04-26 10:28:51 -070029 return true;
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000030}
31
32void SsaDeadPhiElimination::MarkDeadPhis() {
Vladimir Markof3612672017-10-10 10:38:16 +010033 // Use local allocator for allocating memory used by this optimization.
34 ScopedArenaAllocator allocator(graph_->GetArenaStack());
35
36 static constexpr size_t kDefaultWorklistSize = 8;
37 ScopedArenaVector<HPhi*> worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
38 worklist.reserve(kDefaultWorklistSize);
39
David Brazdil809d70f2015-11-19 10:29:39 +000040 // Phis are constructed live and should not be revived if previously marked
41 // dead. This algorithm temporarily breaks that invariant but we DCHECK that
42 // only phis which were initially live are revived.
Vladimir Markof3612672017-10-10 10:38:16 +010043 ScopedArenaSet<HPhi*> initially_live(allocator.Adapter(kArenaAllocSsaPhiElimination));
David Brazdil809d70f2015-11-19 10:29:39 +000044
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010045 // Add to the worklist phis referenced by non-phi instructions.
Vladimir Marko2c45bc92016-10-25 16:54:12 +010046 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Andreas Gampe277ccbd2014-11-03 21:36:10 -080047 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
48 HPhi* phi = inst_it.Current()->AsPhi();
David Brazdil809d70f2015-11-19 10:29:39 +000049 if (phi->IsDead()) {
50 continue;
51 }
52
David Brazdild9510df2015-11-04 23:30:22 +000053 bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
54 if (!keep_alive) {
Vladimir Marko46817b82016-03-29 12:21:58 +010055 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
56 if (!use.GetUser()->IsPhi()) {
David Brazdild9510df2015-11-04 23:30:22 +000057 keep_alive = true;
58 break;
59 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010060 }
61 }
David Brazdil809d70f2015-11-19 10:29:39 +000062
David Brazdild9510df2015-11-04 23:30:22 +000063 if (keep_alive) {
Vladimir Markof3612672017-10-10 10:38:16 +010064 worklist.push_back(phi);
David Brazdil809d70f2015-11-19 10:29:39 +000065 } else {
66 phi->SetDead();
67 if (kIsDebugBuild) {
68 initially_live.insert(phi);
69 }
70 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010071 }
72 }
73
74 // Process the worklist by propagating liveness to phi inputs.
Vladimir Markof3612672017-10-10 10:38:16 +010075 while (!worklist.empty()) {
76 HPhi* phi = worklist.back();
77 worklist.pop_back();
Vladimir Marko372f10e2016-05-17 16:30:10 +010078 for (HInstruction* raw_input : phi->GetInputs()) {
79 HPhi* input = raw_input->AsPhi();
David Brazdil809d70f2015-11-19 10:29:39 +000080 if (input != nullptr && input->IsDead()) {
81 // Input is a dead phi. Revive it and add to the worklist. We make sure
82 // that the phi was not dead initially (see definition of `initially_live`).
83 DCHECK(ContainsElement(initially_live, input));
84 input->SetLive();
Vladimir Markof3612672017-10-10 10:38:16 +010085 worklist.push_back(input);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010086 }
87 }
88 }
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000089}
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010090
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000091void SsaDeadPhiElimination::EliminateDeadPhis() {
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +010092 // Remove phis that are not live. Visit in post order so that phis
93 // that are not inputs of loop phis can be removed when they have
94 // no users left (dead phis might use dead phis).
Vladimir Marko2c45bc92016-10-25 16:54:12 +010095 for (HBasicBlock* block : graph_->GetPostOrder()) {
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010096 HInstruction* current = block->GetFirstPhi();
97 HInstruction* next = nullptr;
David Brazdil1abb4192015-02-17 18:33:36 +000098 HPhi* phi;
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010099 while (current != nullptr) {
David Brazdil1abb4192015-02-17 18:33:36 +0000100 phi = current->AsPhi();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100101 next = current->GetNext();
David Brazdil1abb4192015-02-17 18:33:36 +0000102 if (phi->IsDead()) {
103 // Make sure the phi is only used by other dead phis.
104 if (kIsDebugBuild) {
Vladimir Marko46817b82016-03-29 12:21:58 +0100105 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
106 HInstruction* user = use.GetUser();
David Brazdild9510df2015-11-04 23:30:22 +0000107 DCHECK(user->IsLoopHeaderPhi());
108 DCHECK(user->AsPhi()->IsDead());
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100109 }
110 }
David Brazdil1abb4192015-02-17 18:33:36 +0000111 // Remove the phi from use lists of its inputs.
Vladimir Marko372f10e2016-05-17 16:30:10 +0100112 phi->RemoveAsUserOfAllInputs();
David Brazdil1abb4192015-02-17 18:33:36 +0000113 // Remove the phi from environments that use it.
Vladimir Marko46817b82016-03-29 12:21:58 +0100114 for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
115 HEnvironment* user = use.GetUser();
116 user->SetRawEnvAt(use.GetIndex(), nullptr);
David Brazdil1abb4192015-02-17 18:33:36 +0000117 }
118 // Delete it from the instruction list.
119 block->RemovePhi(phi, /*ensure_safety=*/ false);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100120 }
121 current = next;
122 }
123 }
124}
125
Aart Bik24773202018-04-26 10:28:51 -0700126bool SsaRedundantPhiElimination::Run() {
Vladimir Markof3612672017-10-10 10:38:16 +0100127 // Use local allocator for allocating memory used by this optimization.
128 ScopedArenaAllocator allocator(graph_->GetArenaStack());
129
130 static constexpr size_t kDefaultWorklistSize = 8;
131 ScopedArenaVector<HPhi*> worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
132 worklist.reserve(kDefaultWorklistSize);
133
David Brazdil77b022d2015-08-19 14:17:31 +0100134 // Add all phis in the worklist. Order does not matter for correctness, and
135 // neither will necessarily converge faster.
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100136 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
Andreas Gampe277ccbd2014-11-03 21:36:10 -0800137 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
Vladimir Markof3612672017-10-10 10:38:16 +0100138 worklist.push_back(inst_it.Current()->AsPhi());
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100139 }
140 }
141
Vladimir Markof3612672017-10-10 10:38:16 +0100142 ArenaBitVector visited_phis_in_cycle(&allocator,
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100143 graph_->GetCurrentInstructionId(),
144 /* expandable */ false,
145 kArenaAllocSsaPhiElimination);
Vladimir Markof3612672017-10-10 10:38:16 +0100146 visited_phis_in_cycle.ClearAllBits();
147 ScopedArenaVector<HPhi*> cycle_worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000148
Vladimir Markof3612672017-10-10 10:38:16 +0100149 while (!worklist.empty()) {
150 HPhi* phi = worklist.back();
151 worklist.pop_back();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100152
153 // If the phi has already been processed, continue.
154 if (!phi->IsInBlock()) {
155 continue;
156 }
157
Nicolas Geoffray05b3fa02016-05-04 12:05:56 +0100158 // If the phi is dead, we know we won't revive it and it will be removed,
159 // so don't process it.
160 if (phi->IsDead()) {
David Brazdilffee3d32015-07-06 11:48:53 +0100161 continue;
162 }
163
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000164 HInstruction* candidate = nullptr;
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100165 visited_phis_in_cycle.ClearAllBits();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000166 cycle_worklist.clear();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100167
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000168 cycle_worklist.push_back(phi);
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100169 visited_phis_in_cycle.SetBit(phi->GetId());
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000170 bool catch_phi_in_cycle = phi->IsCatchPhi();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000171 bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000172
173 // First do a simple loop over inputs and check if they are all the same.
Vladimir Marko372f10e2016-05-17 16:30:10 +0100174 for (HInstruction* input : phi->GetInputs()) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000175 if (input == phi) {
176 continue;
177 } else if (candidate == nullptr) {
178 candidate = input;
179 } else if (candidate != input) {
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100180 candidate = nullptr;
181 break;
182 }
183 }
184
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000185 // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
186 // such cycles to avoid having reference and non-reference equivalents. We check this
187 // invariant in the graph checker.
188 if (candidate == nullptr) {
189 // We iterate over the array as long as it grows.
190 for (size_t i = 0; i < cycle_worklist.size(); ++i) {
191 HPhi* current = cycle_worklist[i];
192 DCHECK(!current->IsLoopHeaderPhi() ||
193 current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
194
Vladimir Marko372f10e2016-05-17 16:30:10 +0100195 for (HInstruction* input : current->GetInputs()) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000196 if (input == current) {
197 continue;
198 } else if (input->IsPhi()) {
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100199 if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000200 cycle_worklist.push_back(input->AsPhi());
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100201 visited_phis_in_cycle.SetBit(input->GetId());
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000202 catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000203 irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000204 } else {
205 // Already visited, nothing to do.
206 }
207 } else if (candidate == nullptr) {
208 candidate = input;
209 } else if (candidate != input) {
210 candidate = nullptr;
211 // Clear the cycle worklist to break out of the outer loop.
212 cycle_worklist.clear();
213 break;
214 }
215 }
216 }
217 }
218
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100219 if (candidate == nullptr) {
220 continue;
221 }
222
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000223 if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
224 // For irreducible loops, we need to keep the phis to satisfy our linear scan
225 // algorithm.
226 // There is one exception for constants, as the type propagation requires redundant
227 // cyclic phis of a constant to be removed. This is ok for the linear scan as it
228 // has to deal with constants anyway, and they can trivially be rematerialized.
229 continue;
230 }
231
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000232 for (HPhi* current : cycle_worklist) {
233 // The candidate may not dominate a phi in a catch block: there may be non-throwing
234 // instructions at the beginning of a try range, that may be the first input of
235 // catch phis.
236 // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
237 // before the try entry.
238 if (catch_phi_in_cycle) {
239 if (!candidate->StrictlyDominates(current)) {
240 continue;
241 }
242 } else {
243 DCHECK(candidate->StrictlyDominates(current));
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100244 }
David Brazdil77b022d2015-08-19 14:17:31 +0100245
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000246 // Because we're updating the users of this phi, we may have new candidates
247 // for elimination. Add phis that use this phi to the worklist.
Vladimir Marko46817b82016-03-29 12:21:58 +0100248 for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
249 HInstruction* user = use.GetUser();
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100250 if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
Vladimir Markof3612672017-10-10 10:38:16 +0100251 worklist.push_back(user->AsPhi());
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000252 }
253 }
254 DCHECK(candidate->StrictlyDominates(current));
255 current->ReplaceWith(candidate);
256 current->GetBlock()->RemovePhi(current);
257 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100258 }
Aart Bik24773202018-04-26 10:28:51 -0700259 return true;
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100260}
261
262} // namespace art