• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
19 #include "base/arena_bit_vector.h"
20 #include "base/scoped_arena_allocator.h"
21 #include "base/scoped_arena_containers.h"
22 #include "base/bit_vector-inl.h"
23 
24 namespace art HIDDEN {
25 
Run()26 bool SsaDeadPhiElimination::Run() {
27   MarkDeadPhis();
28   EliminateDeadPhis();
29   return true;
30 }
31 
MarkDeadPhis()32 void SsaDeadPhiElimination::MarkDeadPhis() {
33   // 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 
40   // 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.
43   ScopedArenaSet<HPhi*> initially_live(allocator.Adapter(kArenaAllocSsaPhiElimination));
44 
45   // Add to the worklist phis referenced by non-phi instructions.
46   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
47     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
48       HPhi* phi = inst_it.Current()->AsPhi();
49       if (phi->IsDead()) {
50         continue;
51       }
52 
53       bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
54       if (!keep_alive) {
55         for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
56           if (!use.GetUser()->IsPhi()) {
57             keep_alive = true;
58             break;
59           }
60         }
61       }
62 
63       if (keep_alive) {
64         worklist.push_back(phi);
65       } else {
66         phi->SetDead();
67         if (kIsDebugBuild) {
68           initially_live.insert(phi);
69         }
70       }
71     }
72   }
73 
74   // Process the worklist by propagating liveness to phi inputs.
75   while (!worklist.empty()) {
76     HPhi* phi = worklist.back();
77     worklist.pop_back();
78     for (HInstruction* raw_input : phi->GetInputs()) {
79       HPhi* input = raw_input->AsPhiOrNull();
80       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();
85         worklist.push_back(input);
86       }
87     }
88   }
89 }
90 
EliminateDeadPhis()91 void SsaDeadPhiElimination::EliminateDeadPhis() {
92   // 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).
95   for (HBasicBlock* block : graph_->GetPostOrder()) {
96     HInstruction* current = block->GetFirstPhi();
97     HInstruction* next = nullptr;
98     HPhi* phi;
99     while (current != nullptr) {
100       phi = current->AsPhi();
101       next = current->GetNext();
102       if (phi->IsDead()) {
103         // Make sure the phi is only used by other dead phis.
104         if (kIsDebugBuild) {
105           for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
106             HInstruction* user = use.GetUser();
107             DCHECK(user->IsLoopHeaderPhi());
108             DCHECK(user->AsPhi()->IsDead());
109           }
110         }
111         // Remove the phi from use lists of its inputs.
112         phi->RemoveAsUserOfAllInputs();
113         // Remove the phi from environments that use it.
114         for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
115           HEnvironment* user = use.GetUser();
116           user->SetRawEnvAt(use.GetIndex(), nullptr);
117         }
118         // Delete it from the instruction list.
119         block->RemovePhi(phi, /*ensure_safety=*/ false);
120       }
121       current = next;
122     }
123   }
124 }
125 
Run()126 bool SsaRedundantPhiElimination::Run() {
127   // 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 
134   // Add all phis in the worklist. Order does not matter for correctness, and
135   // neither will necessarily converge faster.
136   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
137     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
138       worklist.push_back(inst_it.Current()->AsPhi());
139     }
140   }
141 
142   BitVectorView<size_t> visited_phis_in_cycle = ArenaBitVector::CreateFixedSize(
143       &allocator, graph_->GetCurrentInstructionId(), kArenaAllocSsaPhiElimination);
144   ScopedArenaVector<HPhi*> cycle_worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
145 
146   while (!worklist.empty()) {
147     HPhi* phi = worklist.back();
148     worklist.pop_back();
149 
150     // If the phi has already been processed, continue.
151     if (!phi->IsInBlock()) {
152       continue;
153     }
154 
155     // If the phi is dead, we know we won't revive it and it will be removed,
156     // so don't process it.
157     if (phi->IsDead()) {
158       continue;
159     }
160 
161     HInstruction* candidate = nullptr;
162     visited_phis_in_cycle.ClearAllBits();
163     cycle_worklist.clear();
164 
165     cycle_worklist.push_back(phi);
166     visited_phis_in_cycle.SetBit(phi->GetId());
167     bool catch_phi_in_cycle = phi->IsCatchPhi();
168     bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
169 
170     // First do a simple loop over inputs and check if they are all the same.
171     for (HInstruction* input : phi->GetInputs()) {
172       if (input == phi) {
173         continue;
174       } else if (candidate == nullptr) {
175         candidate = input;
176       } else if (candidate != input) {
177         candidate = nullptr;
178         break;
179       }
180     }
181 
182     // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
183     // such cycles to avoid having reference and non-reference equivalents. We check this
184     // invariant in the graph checker.
185     if (candidate == nullptr) {
186       // We iterate over the array as long as it grows.
187       for (size_t i = 0; i < cycle_worklist.size(); ++i) {
188         HPhi* current = cycle_worklist[i];
189         DCHECK_IMPLIES(current->IsLoopHeaderPhi(),
190                        current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
191 
192         for (HInstruction* input : current->GetInputs()) {
193           if (input == current) {
194             continue;
195           } else if (input->IsPhi()) {
196             if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
197               cycle_worklist.push_back(input->AsPhi());
198               visited_phis_in_cycle.SetBit(input->GetId());
199               catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
200               irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
201             } else {
202               // Already visited, nothing to do.
203             }
204           } else if (candidate == nullptr) {
205             candidate = input;
206           } else if (candidate != input) {
207             candidate = nullptr;
208             // Clear the cycle worklist to break out of the outer loop.
209             cycle_worklist.clear();
210             break;
211           }
212         }
213       }
214     }
215 
216     if (candidate == nullptr) {
217       continue;
218     }
219 
220     if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
221       // For irreducible loops, we need to keep the phis to satisfy our linear scan
222       // algorithm.
223       // There is one exception for constants, as the type propagation requires redundant
224       // cyclic phis of a constant to be removed. This is ok for the linear scan as it
225       // has to deal with constants anyway, and they can trivially be rematerialized.
226       continue;
227     }
228 
229     for (HPhi* current : cycle_worklist) {
230       // The candidate may not dominate a phi in a catch block: there may be non-throwing
231       // instructions at the beginning of a try range, that may be the first input of
232       // catch phis.
233       // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
234       // before the try entry.
235       if (catch_phi_in_cycle) {
236         if (!candidate->StrictlyDominates(current)) {
237           continue;
238         }
239       } else {
240         DCHECK(candidate->StrictlyDominates(current));
241       }
242 
243       // Because we're updating the users of this phi, we may have new candidates
244       // for elimination. Add phis that use this phi to the worklist.
245       for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
246         HInstruction* user = use.GetUser();
247         if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
248           worklist.push_back(user->AsPhi());
249         }
250       }
251       DCHECK(candidate->StrictlyDominates(current));
252       current->ReplaceWith(candidate);
253       current->GetBlock()->RemovePhi(current);
254     }
255   }
256   return true;
257 }
258 
259 }  // namespace art
260