1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2014 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include "ssa_phi_elimination.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include "base/arena_bit_vector.h"
20*795d594fSAndroid Build Coastguard Worker #include "base/scoped_arena_allocator.h"
21*795d594fSAndroid Build Coastguard Worker #include "base/scoped_arena_containers.h"
22*795d594fSAndroid Build Coastguard Worker #include "base/bit_vector-inl.h"
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
25*795d594fSAndroid Build Coastguard Worker
Run()26*795d594fSAndroid Build Coastguard Worker bool SsaDeadPhiElimination::Run() {
27*795d594fSAndroid Build Coastguard Worker MarkDeadPhis();
28*795d594fSAndroid Build Coastguard Worker EliminateDeadPhis();
29*795d594fSAndroid Build Coastguard Worker return true;
30*795d594fSAndroid Build Coastguard Worker }
31*795d594fSAndroid Build Coastguard Worker
MarkDeadPhis()32*795d594fSAndroid Build Coastguard Worker void SsaDeadPhiElimination::MarkDeadPhis() {
33*795d594fSAndroid Build Coastguard Worker // Use local allocator for allocating memory used by this optimization.
34*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator allocator(graph_->GetArenaStack());
35*795d594fSAndroid Build Coastguard Worker
36*795d594fSAndroid Build Coastguard Worker static constexpr size_t kDefaultWorklistSize = 8;
37*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<HPhi*> worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
38*795d594fSAndroid Build Coastguard Worker worklist.reserve(kDefaultWorklistSize);
39*795d594fSAndroid Build Coastguard Worker
40*795d594fSAndroid Build Coastguard Worker // Phis are constructed live and should not be revived if previously marked
41*795d594fSAndroid Build Coastguard Worker // dead. This algorithm temporarily breaks that invariant but we DCHECK that
42*795d594fSAndroid Build Coastguard Worker // only phis which were initially live are revived.
43*795d594fSAndroid Build Coastguard Worker ScopedArenaSet<HPhi*> initially_live(allocator.Adapter(kArenaAllocSsaPhiElimination));
44*795d594fSAndroid Build Coastguard Worker
45*795d594fSAndroid Build Coastguard Worker // Add to the worklist phis referenced by non-phi instructions.
46*795d594fSAndroid Build Coastguard Worker for (HBasicBlock* block : graph_->GetReversePostOrder()) {
47*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
48*795d594fSAndroid Build Coastguard Worker HPhi* phi = inst_it.Current()->AsPhi();
49*795d594fSAndroid Build Coastguard Worker if (phi->IsDead()) {
50*795d594fSAndroid Build Coastguard Worker continue;
51*795d594fSAndroid Build Coastguard Worker }
52*795d594fSAndroid Build Coastguard Worker
53*795d594fSAndroid Build Coastguard Worker bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
54*795d594fSAndroid Build Coastguard Worker if (!keep_alive) {
55*795d594fSAndroid Build Coastguard Worker for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
56*795d594fSAndroid Build Coastguard Worker if (!use.GetUser()->IsPhi()) {
57*795d594fSAndroid Build Coastguard Worker keep_alive = true;
58*795d594fSAndroid Build Coastguard Worker break;
59*795d594fSAndroid Build Coastguard Worker }
60*795d594fSAndroid Build Coastguard Worker }
61*795d594fSAndroid Build Coastguard Worker }
62*795d594fSAndroid Build Coastguard Worker
63*795d594fSAndroid Build Coastguard Worker if (keep_alive) {
64*795d594fSAndroid Build Coastguard Worker worklist.push_back(phi);
65*795d594fSAndroid Build Coastguard Worker } else {
66*795d594fSAndroid Build Coastguard Worker phi->SetDead();
67*795d594fSAndroid Build Coastguard Worker if (kIsDebugBuild) {
68*795d594fSAndroid Build Coastguard Worker initially_live.insert(phi);
69*795d594fSAndroid Build Coastguard Worker }
70*795d594fSAndroid Build Coastguard Worker }
71*795d594fSAndroid Build Coastguard Worker }
72*795d594fSAndroid Build Coastguard Worker }
73*795d594fSAndroid Build Coastguard Worker
74*795d594fSAndroid Build Coastguard Worker // Process the worklist by propagating liveness to phi inputs.
75*795d594fSAndroid Build Coastguard Worker while (!worklist.empty()) {
76*795d594fSAndroid Build Coastguard Worker HPhi* phi = worklist.back();
77*795d594fSAndroid Build Coastguard Worker worklist.pop_back();
78*795d594fSAndroid Build Coastguard Worker for (HInstruction* raw_input : phi->GetInputs()) {
79*795d594fSAndroid Build Coastguard Worker HPhi* input = raw_input->AsPhiOrNull();
80*795d594fSAndroid Build Coastguard Worker if (input != nullptr && input->IsDead()) {
81*795d594fSAndroid Build Coastguard Worker // Input is a dead phi. Revive it and add to the worklist. We make sure
82*795d594fSAndroid Build Coastguard Worker // that the phi was not dead initially (see definition of `initially_live`).
83*795d594fSAndroid Build Coastguard Worker DCHECK(ContainsElement(initially_live, input));
84*795d594fSAndroid Build Coastguard Worker input->SetLive();
85*795d594fSAndroid Build Coastguard Worker worklist.push_back(input);
86*795d594fSAndroid Build Coastguard Worker }
87*795d594fSAndroid Build Coastguard Worker }
88*795d594fSAndroid Build Coastguard Worker }
89*795d594fSAndroid Build Coastguard Worker }
90*795d594fSAndroid Build Coastguard Worker
EliminateDeadPhis()91*795d594fSAndroid Build Coastguard Worker void SsaDeadPhiElimination::EliminateDeadPhis() {
92*795d594fSAndroid Build Coastguard Worker // Remove phis that are not live. Visit in post order so that phis
93*795d594fSAndroid Build Coastguard Worker // that are not inputs of loop phis can be removed when they have
94*795d594fSAndroid Build Coastguard Worker // no users left (dead phis might use dead phis).
95*795d594fSAndroid Build Coastguard Worker for (HBasicBlock* block : graph_->GetPostOrder()) {
96*795d594fSAndroid Build Coastguard Worker HInstruction* current = block->GetFirstPhi();
97*795d594fSAndroid Build Coastguard Worker HInstruction* next = nullptr;
98*795d594fSAndroid Build Coastguard Worker HPhi* phi;
99*795d594fSAndroid Build Coastguard Worker while (current != nullptr) {
100*795d594fSAndroid Build Coastguard Worker phi = current->AsPhi();
101*795d594fSAndroid Build Coastguard Worker next = current->GetNext();
102*795d594fSAndroid Build Coastguard Worker if (phi->IsDead()) {
103*795d594fSAndroid Build Coastguard Worker // Make sure the phi is only used by other dead phis.
104*795d594fSAndroid Build Coastguard Worker if (kIsDebugBuild) {
105*795d594fSAndroid Build Coastguard Worker for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
106*795d594fSAndroid Build Coastguard Worker HInstruction* user = use.GetUser();
107*795d594fSAndroid Build Coastguard Worker DCHECK(user->IsLoopHeaderPhi());
108*795d594fSAndroid Build Coastguard Worker DCHECK(user->AsPhi()->IsDead());
109*795d594fSAndroid Build Coastguard Worker }
110*795d594fSAndroid Build Coastguard Worker }
111*795d594fSAndroid Build Coastguard Worker // Remove the phi from use lists of its inputs.
112*795d594fSAndroid Build Coastguard Worker phi->RemoveAsUserOfAllInputs();
113*795d594fSAndroid Build Coastguard Worker // Remove the phi from environments that use it.
114*795d594fSAndroid Build Coastguard Worker for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
115*795d594fSAndroid Build Coastguard Worker HEnvironment* user = use.GetUser();
116*795d594fSAndroid Build Coastguard Worker user->SetRawEnvAt(use.GetIndex(), nullptr);
117*795d594fSAndroid Build Coastguard Worker }
118*795d594fSAndroid Build Coastguard Worker // Delete it from the instruction list.
119*795d594fSAndroid Build Coastguard Worker block->RemovePhi(phi, /*ensure_safety=*/ false);
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker current = next;
122*795d594fSAndroid Build Coastguard Worker }
123*795d594fSAndroid Build Coastguard Worker }
124*795d594fSAndroid Build Coastguard Worker }
125*795d594fSAndroid Build Coastguard Worker
Run()126*795d594fSAndroid Build Coastguard Worker bool SsaRedundantPhiElimination::Run() {
127*795d594fSAndroid Build Coastguard Worker // Use local allocator for allocating memory used by this optimization.
128*795d594fSAndroid Build Coastguard Worker ScopedArenaAllocator allocator(graph_->GetArenaStack());
129*795d594fSAndroid Build Coastguard Worker
130*795d594fSAndroid Build Coastguard Worker static constexpr size_t kDefaultWorklistSize = 8;
131*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<HPhi*> worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
132*795d594fSAndroid Build Coastguard Worker worklist.reserve(kDefaultWorklistSize);
133*795d594fSAndroid Build Coastguard Worker
134*795d594fSAndroid Build Coastguard Worker // Add all phis in the worklist. Order does not matter for correctness, and
135*795d594fSAndroid Build Coastguard Worker // neither will necessarily converge faster.
136*795d594fSAndroid Build Coastguard Worker for (HBasicBlock* block : graph_->GetReversePostOrder()) {
137*795d594fSAndroid Build Coastguard Worker for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
138*795d594fSAndroid Build Coastguard Worker worklist.push_back(inst_it.Current()->AsPhi());
139*795d594fSAndroid Build Coastguard Worker }
140*795d594fSAndroid Build Coastguard Worker }
141*795d594fSAndroid Build Coastguard Worker
142*795d594fSAndroid Build Coastguard Worker ArenaBitVector visited_phis_in_cycle(&allocator,
143*795d594fSAndroid Build Coastguard Worker graph_->GetCurrentInstructionId(),
144*795d594fSAndroid Build Coastguard Worker /* expandable= */ false,
145*795d594fSAndroid Build Coastguard Worker kArenaAllocSsaPhiElimination);
146*795d594fSAndroid Build Coastguard Worker ScopedArenaVector<HPhi*> cycle_worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
147*795d594fSAndroid Build Coastguard Worker
148*795d594fSAndroid Build Coastguard Worker while (!worklist.empty()) {
149*795d594fSAndroid Build Coastguard Worker HPhi* phi = worklist.back();
150*795d594fSAndroid Build Coastguard Worker worklist.pop_back();
151*795d594fSAndroid Build Coastguard Worker
152*795d594fSAndroid Build Coastguard Worker // If the phi has already been processed, continue.
153*795d594fSAndroid Build Coastguard Worker if (!phi->IsInBlock()) {
154*795d594fSAndroid Build Coastguard Worker continue;
155*795d594fSAndroid Build Coastguard Worker }
156*795d594fSAndroid Build Coastguard Worker
157*795d594fSAndroid Build Coastguard Worker // If the phi is dead, we know we won't revive it and it will be removed,
158*795d594fSAndroid Build Coastguard Worker // so don't process it.
159*795d594fSAndroid Build Coastguard Worker if (phi->IsDead()) {
160*795d594fSAndroid Build Coastguard Worker continue;
161*795d594fSAndroid Build Coastguard Worker }
162*795d594fSAndroid Build Coastguard Worker
163*795d594fSAndroid Build Coastguard Worker HInstruction* candidate = nullptr;
164*795d594fSAndroid Build Coastguard Worker visited_phis_in_cycle.ClearAllBits();
165*795d594fSAndroid Build Coastguard Worker cycle_worklist.clear();
166*795d594fSAndroid Build Coastguard Worker
167*795d594fSAndroid Build Coastguard Worker cycle_worklist.push_back(phi);
168*795d594fSAndroid Build Coastguard Worker visited_phis_in_cycle.SetBit(phi->GetId());
169*795d594fSAndroid Build Coastguard Worker bool catch_phi_in_cycle = phi->IsCatchPhi();
170*795d594fSAndroid Build Coastguard Worker bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
171*795d594fSAndroid Build Coastguard Worker
172*795d594fSAndroid Build Coastguard Worker // First do a simple loop over inputs and check if they are all the same.
173*795d594fSAndroid Build Coastguard Worker for (HInstruction* input : phi->GetInputs()) {
174*795d594fSAndroid Build Coastguard Worker if (input == phi) {
175*795d594fSAndroid Build Coastguard Worker continue;
176*795d594fSAndroid Build Coastguard Worker } else if (candidate == nullptr) {
177*795d594fSAndroid Build Coastguard Worker candidate = input;
178*795d594fSAndroid Build Coastguard Worker } else if (candidate != input) {
179*795d594fSAndroid Build Coastguard Worker candidate = nullptr;
180*795d594fSAndroid Build Coastguard Worker break;
181*795d594fSAndroid Build Coastguard Worker }
182*795d594fSAndroid Build Coastguard Worker }
183*795d594fSAndroid Build Coastguard Worker
184*795d594fSAndroid Build Coastguard Worker // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
185*795d594fSAndroid Build Coastguard Worker // such cycles to avoid having reference and non-reference equivalents. We check this
186*795d594fSAndroid Build Coastguard Worker // invariant in the graph checker.
187*795d594fSAndroid Build Coastguard Worker if (candidate == nullptr) {
188*795d594fSAndroid Build Coastguard Worker // We iterate over the array as long as it grows.
189*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < cycle_worklist.size(); ++i) {
190*795d594fSAndroid Build Coastguard Worker HPhi* current = cycle_worklist[i];
191*795d594fSAndroid Build Coastguard Worker DCHECK_IMPLIES(current->IsLoopHeaderPhi(),
192*795d594fSAndroid Build Coastguard Worker current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
193*795d594fSAndroid Build Coastguard Worker
194*795d594fSAndroid Build Coastguard Worker for (HInstruction* input : current->GetInputs()) {
195*795d594fSAndroid Build Coastguard Worker if (input == current) {
196*795d594fSAndroid Build Coastguard Worker continue;
197*795d594fSAndroid Build Coastguard Worker } else if (input->IsPhi()) {
198*795d594fSAndroid Build Coastguard Worker if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
199*795d594fSAndroid Build Coastguard Worker cycle_worklist.push_back(input->AsPhi());
200*795d594fSAndroid Build Coastguard Worker visited_phis_in_cycle.SetBit(input->GetId());
201*795d594fSAndroid Build Coastguard Worker catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
202*795d594fSAndroid Build Coastguard Worker irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
203*795d594fSAndroid Build Coastguard Worker } else {
204*795d594fSAndroid Build Coastguard Worker // Already visited, nothing to do.
205*795d594fSAndroid Build Coastguard Worker }
206*795d594fSAndroid Build Coastguard Worker } else if (candidate == nullptr) {
207*795d594fSAndroid Build Coastguard Worker candidate = input;
208*795d594fSAndroid Build Coastguard Worker } else if (candidate != input) {
209*795d594fSAndroid Build Coastguard Worker candidate = nullptr;
210*795d594fSAndroid Build Coastguard Worker // Clear the cycle worklist to break out of the outer loop.
211*795d594fSAndroid Build Coastguard Worker cycle_worklist.clear();
212*795d594fSAndroid Build Coastguard Worker break;
213*795d594fSAndroid Build Coastguard Worker }
214*795d594fSAndroid Build Coastguard Worker }
215*795d594fSAndroid Build Coastguard Worker }
216*795d594fSAndroid Build Coastguard Worker }
217*795d594fSAndroid Build Coastguard Worker
218*795d594fSAndroid Build Coastguard Worker if (candidate == nullptr) {
219*795d594fSAndroid Build Coastguard Worker continue;
220*795d594fSAndroid Build Coastguard Worker }
221*795d594fSAndroid Build Coastguard Worker
222*795d594fSAndroid Build Coastguard Worker if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
223*795d594fSAndroid Build Coastguard Worker // For irreducible loops, we need to keep the phis to satisfy our linear scan
224*795d594fSAndroid Build Coastguard Worker // algorithm.
225*795d594fSAndroid Build Coastguard Worker // There is one exception for constants, as the type propagation requires redundant
226*795d594fSAndroid Build Coastguard Worker // cyclic phis of a constant to be removed. This is ok for the linear scan as it
227*795d594fSAndroid Build Coastguard Worker // has to deal with constants anyway, and they can trivially be rematerialized.
228*795d594fSAndroid Build Coastguard Worker continue;
229*795d594fSAndroid Build Coastguard Worker }
230*795d594fSAndroid Build Coastguard Worker
231*795d594fSAndroid Build Coastguard Worker for (HPhi* current : cycle_worklist) {
232*795d594fSAndroid Build Coastguard Worker // The candidate may not dominate a phi in a catch block: there may be non-throwing
233*795d594fSAndroid Build Coastguard Worker // instructions at the beginning of a try range, that may be the first input of
234*795d594fSAndroid Build Coastguard Worker // catch phis.
235*795d594fSAndroid Build Coastguard Worker // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
236*795d594fSAndroid Build Coastguard Worker // before the try entry.
237*795d594fSAndroid Build Coastguard Worker if (catch_phi_in_cycle) {
238*795d594fSAndroid Build Coastguard Worker if (!candidate->StrictlyDominates(current)) {
239*795d594fSAndroid Build Coastguard Worker continue;
240*795d594fSAndroid Build Coastguard Worker }
241*795d594fSAndroid Build Coastguard Worker } else {
242*795d594fSAndroid Build Coastguard Worker DCHECK(candidate->StrictlyDominates(current));
243*795d594fSAndroid Build Coastguard Worker }
244*795d594fSAndroid Build Coastguard Worker
245*795d594fSAndroid Build Coastguard Worker // Because we're updating the users of this phi, we may have new candidates
246*795d594fSAndroid Build Coastguard Worker // for elimination. Add phis that use this phi to the worklist.
247*795d594fSAndroid Build Coastguard Worker for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
248*795d594fSAndroid Build Coastguard Worker HInstruction* user = use.GetUser();
249*795d594fSAndroid Build Coastguard Worker if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
250*795d594fSAndroid Build Coastguard Worker worklist.push_back(user->AsPhi());
251*795d594fSAndroid Build Coastguard Worker }
252*795d594fSAndroid Build Coastguard Worker }
253*795d594fSAndroid Build Coastguard Worker DCHECK(candidate->StrictlyDominates(current));
254*795d594fSAndroid Build Coastguard Worker current->ReplaceWith(candidate);
255*795d594fSAndroid Build Coastguard Worker current->GetBlock()->RemovePhi(current);
256*795d594fSAndroid Build Coastguard Worker }
257*795d594fSAndroid Build Coastguard Worker }
258*795d594fSAndroid Build Coastguard Worker return true;
259*795d594fSAndroid Build Coastguard Worker }
260*795d594fSAndroid Build Coastguard Worker
261*795d594fSAndroid Build Coastguard Worker } // namespace art
262