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