blob: b1ec99ab8e14ec4f0a28e168f5e3f7147ac4f56b [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"
Vladimir Markoc9ef1682016-05-10 13:31:23 +010020#include "base/arena_bit_vector.h"
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +000021#include "base/bit_vector-inl.h"
David Brazdil809d70f2015-11-19 10:29:39 +000022
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010023namespace art {
24
25void SsaDeadPhiElimination::Run() {
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000026 MarkDeadPhis();
27 EliminateDeadPhis();
28}
29
30void SsaDeadPhiElimination::MarkDeadPhis() {
David Brazdil809d70f2015-11-19 10:29:39 +000031 // Phis are constructed live and should not be revived if previously marked
32 // dead. This algorithm temporarily breaks that invariant but we DCHECK that
33 // only phis which were initially live are revived.
Vladimir Marko3ea5a972016-05-09 20:23:34 +010034 ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter(kArenaAllocSsaPhiElimination));
David Brazdil809d70f2015-11-19 10:29:39 +000035
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010036 // Add to the worklist phis referenced by non-phi instructions.
37 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
38 HBasicBlock* block = it.Current();
Andreas Gampe277ccbd2014-11-03 21:36:10 -080039 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
40 HPhi* phi = inst_it.Current()->AsPhi();
David Brazdil809d70f2015-11-19 10:29:39 +000041 if (phi->IsDead()) {
42 continue;
43 }
44
David Brazdild9510df2015-11-04 23:30:22 +000045 bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
46 if (!keep_alive) {
Vladimir Marko46817b82016-03-29 12:21:58 +010047 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
48 if (!use.GetUser()->IsPhi()) {
David Brazdild9510df2015-11-04 23:30:22 +000049 keep_alive = true;
50 break;
51 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010052 }
53 }
David Brazdil809d70f2015-11-19 10:29:39 +000054
David Brazdild9510df2015-11-04 23:30:22 +000055 if (keep_alive) {
David Brazdil809d70f2015-11-19 10:29:39 +000056 worklist_.push_back(phi);
57 } else {
58 phi->SetDead();
59 if (kIsDebugBuild) {
60 initially_live.insert(phi);
61 }
62 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010063 }
64 }
65
66 // Process the worklist by propagating liveness to phi inputs.
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010067 while (!worklist_.empty()) {
68 HPhi* phi = worklist_.back();
69 worklist_.pop_back();
Vladimir Marko372f10e2016-05-17 16:30:10 +010070 for (HInstruction* raw_input : phi->GetInputs()) {
71 HPhi* input = raw_input->AsPhi();
David Brazdil809d70f2015-11-19 10:29:39 +000072 if (input != nullptr && input->IsDead()) {
73 // Input is a dead phi. Revive it and add to the worklist. We make sure
74 // that the phi was not dead initially (see definition of `initially_live`).
75 DCHECK(ContainsElement(initially_live, input));
76 input->SetLive();
77 worklist_.push_back(input);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010078 }
79 }
80 }
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000081}
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010082
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +000083void SsaDeadPhiElimination::EliminateDeadPhis() {
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +010084 // Remove phis that are not live. Visit in post order so that phis
85 // that are not inputs of loop phis can be removed when they have
86 // no users left (dead phis might use dead phis).
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010087 for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
88 HBasicBlock* block = it.Current();
89 HInstruction* current = block->GetFirstPhi();
90 HInstruction* next = nullptr;
David Brazdil1abb4192015-02-17 18:33:36 +000091 HPhi* phi;
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010092 while (current != nullptr) {
David Brazdil1abb4192015-02-17 18:33:36 +000093 phi = current->AsPhi();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +010094 next = current->GetNext();
David Brazdil1abb4192015-02-17 18:33:36 +000095 if (phi->IsDead()) {
96 // Make sure the phi is only used by other dead phis.
97 if (kIsDebugBuild) {
Vladimir Marko46817b82016-03-29 12:21:58 +010098 for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
99 HInstruction* user = use.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.
Vladimir Marko372f10e2016-05-17 16:30:10 +0100105 phi->RemoveAsUserOfAllInputs();
David Brazdil1abb4192015-02-17 18:33:36 +0000106 // Remove the phi from environments that use it.
Vladimir Marko46817b82016-03-29 12:21:58 +0100107 for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
108 HEnvironment* user = use.GetUser();
109 user->SetRawEnvAt(use.GetIndex(), nullptr);
David Brazdil1abb4192015-02-17 18:33:36 +0000110 }
111 // Delete it from the instruction list.
112 block->RemovePhi(phi, /*ensure_safety=*/ false);
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100113 }
114 current = next;
115 }
116 }
117}
118
119void SsaRedundantPhiElimination::Run() {
David Brazdil77b022d2015-08-19 14:17:31 +0100120 // Add all phis in the worklist. Order does not matter for correctness, and
121 // neither will necessarily converge faster.
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100122 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
123 HBasicBlock* block = it.Current();
Andreas Gampe277ccbd2014-11-03 21:36:10 -0800124 for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100125 worklist_.push_back(inst_it.Current()->AsPhi());
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100126 }
127 }
128
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100129 ArenaBitVector visited_phis_in_cycle(graph_->GetArena(),
130 graph_->GetCurrentInstructionId(),
131 /* expandable */ false,
132 kArenaAllocSsaPhiElimination);
Vladimir Marko3ea5a972016-05-09 20:23:34 +0100133 ArenaVector<HPhi*> cycle_worklist(graph_->GetArena()->Adapter(kArenaAllocSsaPhiElimination));
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000134
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100135 while (!worklist_.empty()) {
136 HPhi* phi = worklist_.back();
137 worklist_.pop_back();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100138
139 // If the phi has already been processed, continue.
140 if (!phi->IsInBlock()) {
141 continue;
142 }
143
Nicolas Geoffray05b3fa02016-05-04 12:05:56 +0100144 // If the phi is dead, we know we won't revive it and it will be removed,
145 // so don't process it.
146 if (phi->IsDead()) {
David Brazdilffee3d32015-07-06 11:48:53 +0100147 continue;
148 }
149
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000150 HInstruction* candidate = nullptr;
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100151 visited_phis_in_cycle.ClearAllBits();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000152 cycle_worklist.clear();
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100153
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000154 cycle_worklist.push_back(phi);
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100155 visited_phis_in_cycle.SetBit(phi->GetId());
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000156 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.
Vladimir Marko372f10e2016-05-17 16:30:10 +0100160 for (HInstruction* input : phi->GetInputs()) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000161 if (input == phi) {
162 continue;
163 } else if (candidate == nullptr) {
164 candidate = input;
165 } else if (candidate != input) {
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100166 candidate = nullptr;
167 break;
168 }
169 }
170
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000171 // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
172 // such cycles to avoid having reference and non-reference equivalents. We check this
173 // invariant in the graph checker.
174 if (candidate == nullptr) {
175 // We iterate over the array as long as it grows.
176 for (size_t i = 0; i < cycle_worklist.size(); ++i) {
177 HPhi* current = cycle_worklist[i];
178 DCHECK(!current->IsLoopHeaderPhi() ||
179 current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
180
Vladimir Marko372f10e2016-05-17 16:30:10 +0100181 for (HInstruction* input : current->GetInputs()) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000182 if (input == current) {
183 continue;
184 } else if (input->IsPhi()) {
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100185 if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000186 cycle_worklist.push_back(input->AsPhi());
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100187 visited_phis_in_cycle.SetBit(input->GetId());
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000188 catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000189 irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000190 } else {
191 // Already visited, nothing to do.
192 }
193 } else if (candidate == nullptr) {
194 candidate = input;
195 } else if (candidate != input) {
196 candidate = nullptr;
197 // Clear the cycle worklist to break out of the outer loop.
198 cycle_worklist.clear();
199 break;
200 }
201 }
202 }
203 }
204
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100205 if (candidate == nullptr) {
206 continue;
207 }
208
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000209 if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
210 // For irreducible loops, we need to keep the phis to satisfy our linear scan
211 // algorithm.
212 // There is one exception for constants, as the type propagation requires redundant
213 // cyclic phis of a constant to be removed. This is ok for the linear scan as it
214 // has to deal with constants anyway, and they can trivially be rematerialized.
215 continue;
216 }
217
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000218 for (HPhi* current : cycle_worklist) {
219 // The candidate may not dominate a phi in a catch block: there may be non-throwing
220 // instructions at the beginning of a try range, that may be the first input of
221 // catch phis.
222 // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
223 // before the try entry.
224 if (catch_phi_in_cycle) {
225 if (!candidate->StrictlyDominates(current)) {
226 continue;
227 }
228 } else {
229 DCHECK(candidate->StrictlyDominates(current));
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100230 }
David Brazdil77b022d2015-08-19 14:17:31 +0100231
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000232 // Because we're updating the users of this phi, we may have new candidates
233 // for elimination. Add phis that use this phi to the worklist.
Vladimir Marko46817b82016-03-29 12:21:58 +0100234 for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
235 HInstruction* user = use.GetUser();
Vladimir Markoc9ef1682016-05-10 13:31:23 +0100236 if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
Nicolas Geoffrayf5f64ef2015-12-15 14:11:59 +0000237 worklist_.push_back(user->AsPhi());
238 }
239 }
240 DCHECK(candidate->StrictlyDominates(current));
241 current->ReplaceWith(candidate);
242 current->GetBlock()->RemovePhi(current);
243 }
Nicolas Geoffray7dc206a2014-07-11 09:49:49 +0100244 }
245}
246
247} // namespace art