blob: 6816b6a0289f6942d6f13cbbe42ec136ae78001e [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
David Brazdil809d70f2015-11-19 10:29:39 +000019#include "base/arena_containers.h"
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +000020#include "base/bit_vector-inl.h"
David Brazdil809d70f2015-11-19 10:29:39 +000021
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010022namespace art {
23
24void SsaDeadPhiElimination::Run() {
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000025 MarkDeadPhis();
26 EliminateDeadPhis();
27}
28
29void SsaDeadPhiElimination::MarkDeadPhis() {
David Brazdil809d70f2015-11-19 10:29:39 +000030 // Phis are constructed live and should not be revived if previously marked
31 // dead. This algorithm temporarily breaks that invariant but we DCHECK that
32 // only phis which were initially live are revived.
33 ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter());
34
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010035 // Add to the worklist phis referenced by non-phi instructions.
36 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
37 HBasicBlock* block = it.Current();
Andreas Gampe277ccbd2014-11-03 21:36:10 -080038 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
39 HPhi* phi = inst_it.Current()->AsPhi();
David Brazdil809d70f2015-11-19 10:29:39 +000040 if (phi->IsDead()) {
41 continue;
42 }
43
David Brazdild9510df2015-11-04 23:30:22 +000044 bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
45 if (!keep_alive) {
46 for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
47 if (!use_it.Current()->GetUser()->IsPhi()) {
48 keep_alive = true;
49 break;
50 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010051 }
52 }
David Brazdil809d70f2015-11-19 10:29:39 +000053
David Brazdild9510df2015-11-04 23:30:22 +000054 if (keep_alive) {
David Brazdil809d70f2015-11-19 10:29:39 +000055 worklist_.push_back(phi);
56 } else {
57 phi->SetDead();
58 if (kIsDebugBuild) {
59 initially_live.insert(phi);
60 }
61 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010062 }
63 }
64
65 // Process the worklist by propagating liveness to phi inputs.
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010066 while (!worklist_.empty()) {
67 HPhi* phi = worklist_.back();
68 worklist_.pop_back();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010069 for (HInputIterator it(phi); !it.Done(); it.Advance()) {
David Brazdil809d70f2015-11-19 10:29:39 +000070 HPhi* input = it.Current()->AsPhi();
71 if (input != nullptr && input->IsDead()) {
72 // Input is a dead phi. Revive it and add to the worklist. We make sure
73 // that the phi was not dead initially (see definition of `initially_live`).
74 DCHECK(ContainsElement(initially_live, input));
75 input->SetLive();
76 worklist_.push_back(input);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010077 }
78 }
79 }
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000080}
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010081
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000082void SsaDeadPhiElimination::EliminateDeadPhis() {
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +010083 // Remove phis that are not live. Visit in post order so that phis
84 // that are not inputs of loop phis can be removed when they have
85 // no users left (dead phis might use dead phis).
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010086 for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
87 HBasicBlock* block = it.Current();
88 HInstruction* current = block->GetFirstPhi();
89 HInstruction* next = nullptr;
David Brazdil1abb4192015-02-17 18:33:36 +000090 HPhi* phi;
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010091 while (current != nullptr) {
David Brazdil1abb4192015-02-17 18:33:36 +000092 phi = current->AsPhi();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010093 next = current->GetNext();
David Brazdil1abb4192015-02-17 18:33:36 +000094 if (phi->IsDead()) {
95 // Make sure the phi is only used by other dead phis.
96 if (kIsDebugBuild) {
97 for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
Andreas Gampe277ccbd2014-11-03 21:36:10 -080098 use_it.Advance()) {
David Brazdil1abb4192015-02-17 18:33:36 +000099 HInstruction* user = use_it.Current()->GetUser();
David Brazdild9510df2015-11-04 23:30:22 +0000100 DCHECK(user->IsLoopHeaderPhi());
101 DCHECK(user->AsPhi()->IsDead());
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100102 }
103 }
David Brazdil1abb4192015-02-17 18:33:36 +0000104 // Remove the phi from use lists of its inputs.
105 for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
106 phi->RemoveAsUserOfInput(i);
Nicolas Geoffray102cbed2014-10-15 18:31:05 +0100107 }
David Brazdil1abb4192015-02-17 18:33:36 +0000108 // Remove the phi from environments that use it.
109 for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done();
110 use_it.Advance()) {
111 HUseListNode<HEnvironment*>* user_node = use_it.Current();
112 HEnvironment* user = user_node->GetUser();
113 user->SetRawEnvAt(user_node->GetIndex(), nullptr);
114 }
115 // Delete it from the instruction list.
116 block->RemovePhi(phi, /*ensure_safety=*/ false);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100117 }
118 current = next;
119 }
120 }
121}
122
123void SsaRedundantPhiElimination::Run() {
David Brazdil77b022d2015-08-19 14:17:31 +0100124 // Add all phis in the worklist. Order does not matter for correctness, and
125 // neither will necessarily converge faster.
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100126 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
127 HBasicBlock* block = it.Current();
Andreas Gampe277ccbd2014-11-03 21:36:10 -0800128 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100129 worklist_.push_back(inst_it.Current()->AsPhi());
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100130 }
131 }
132
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000133 ArenaSet<uint32_t> visited_phis_in_cycle(graph_->GetArena()->Adapter());
134 ArenaVector<HPhi*> cycle_worklist(graph_->GetArena()->Adapter());
135
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100136 while (!worklist_.empty()) {
137 HPhi* phi = worklist_.back();
138 worklist_.pop_back();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100139
140 // If the phi has already been processed, continue.
141 if (!phi->IsInBlock()) {
142 continue;
143 }
144
David Brazdilffee3d32015-07-06 11:48:53 +0100145 if (phi->InputCount() == 0) {
David Brazdilffee3d32015-07-06 11:48:53 +0100146 DCHECK(phi->IsDead());
147 continue;
148 }
149
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000150 HInstruction* candidate = nullptr;
151 visited_phis_in_cycle.clear();
152 cycle_worklist.clear();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100153
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000154 cycle_worklist.push_back(phi);
155 visited_phis_in_cycle.insert(phi->GetId());
156 bool catch_phi_in_cycle = phi->IsCatchPhi();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000157 bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000158
159 // First do a simple loop over inputs and check if they are all the same.
160 for (size_t j = 0; j < phi->InputCount(); ++j) {
161 HInstruction* input = phi->InputAt(j);
162 if (input == phi) {
163 continue;
164 } else if (candidate == nullptr) {
165 candidate = input;
166 } else if (candidate != input) {
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100167 candidate = nullptr;
168 break;
169 }
170 }
171
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000172 // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
173 // such cycles to avoid having reference and non-reference equivalents. We check this
174 // invariant in the graph checker.
175 if (candidate == nullptr) {
176 // We iterate over the array as long as it grows.
177 for (size_t i = 0; i < cycle_worklist.size(); ++i) {
178 HPhi* current = cycle_worklist[i];
179 DCHECK(!current->IsLoopHeaderPhi() ||
180 current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
181
182 for (size_t j = 0; j < current->InputCount(); ++j) {
183 HInstruction* input = current->InputAt(j);
184 if (input == current) {
185 continue;
186 } else if (input->IsPhi()) {
187 if (!ContainsElement(visited_phis_in_cycle, input->GetId())) {
188 cycle_worklist.push_back(input->AsPhi());
189 visited_phis_in_cycle.insert(input->GetId());
190 catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000191 irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000192 } else {
193 // Already visited, nothing to do.
194 }
195 } else if (candidate == nullptr) {
196 candidate = input;
197 } else if (candidate != input) {
198 candidate = nullptr;
199 // Clear the cycle worklist to break out of the outer loop.
200 cycle_worklist.clear();
201 break;
202 }
203 }
204 }
205 }
206
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100207 if (candidate == nullptr) {
208 continue;
209 }
210
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000211 if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
212 // For irreducible loops, we need to keep the phis to satisfy our linear scan
213 // algorithm.
214 // There is one exception for constants, as the type propagation requires redundant
215 // cyclic phis of a constant to be removed. This is ok for the linear scan as it
216 // has to deal with constants anyway, and they can trivially be rematerialized.
217 continue;
218 }
219
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000220 for (HPhi* current : cycle_worklist) {
221 // The candidate may not dominate a phi in a catch block: there may be non-throwing
222 // instructions at the beginning of a try range, that may be the first input of
223 // catch phis.
224 // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
225 // before the try entry.
226 if (catch_phi_in_cycle) {
227 if (!candidate->StrictlyDominates(current)) {
228 continue;
229 }
230 } else {
231 DCHECK(candidate->StrictlyDominates(current));
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100232 }
David Brazdil77b022d2015-08-19 14:17:31 +0100233
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000234 // Because we're updating the users of this phi, we may have new candidates
235 // for elimination. Add phis that use this phi to the worklist.
236 for (HUseIterator<HInstruction*> it(current->GetUses()); !it.Done(); it.Advance()) {
237 HUseListNode<HInstruction*>* use = it.Current();
238 HInstruction* user = use->GetUser();
239 if (user->IsPhi() && !ContainsElement(visited_phis_in_cycle, user->GetId())) {
240 worklist_.push_back(user->AsPhi());
241 }
242 }
243 DCHECK(candidate->StrictlyDominates(current));
244 current->ReplaceWith(candidate);
245 current->GetBlock()->RemovePhi(current);
246 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100247 }
248}
249
250} // namespace art