xref: /aosp_15_r20/art/compiler/optimizing/ssa_phi_elimination.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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