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 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->AsPhi();
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   ArenaBitVector visited_phis_in_cycle(&allocator,
143                                        graph_->GetCurrentInstructionId(),
144                                        /* expandable= */ false,
145                                        kArenaAllocSsaPhiElimination);
146   visited_phis_in_cycle.ClearAllBits();
147   ScopedArenaVector<HPhi*> cycle_worklist(allocator.Adapter(kArenaAllocSsaPhiElimination));
148 
149   while (!worklist.empty()) {
150     HPhi* phi = worklist.back();
151     worklist.pop_back();
152 
153     // If the phi has already been processed, continue.
154     if (!phi->IsInBlock()) {
155       continue;
156     }
157 
158     // If the phi is dead, we know we won't revive it and it will be removed,
159     // so don't process it.
160     if (phi->IsDead()) {
161       continue;
162     }
163 
164     HInstruction* candidate = nullptr;
165     visited_phis_in_cycle.ClearAllBits();
166     cycle_worklist.clear();
167 
168     cycle_worklist.push_back(phi);
169     visited_phis_in_cycle.SetBit(phi->GetId());
170     bool catch_phi_in_cycle = phi->IsCatchPhi();
171     bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi();
172 
173     // First do a simple loop over inputs and check if they are all the same.
174     for (HInstruction* input : phi->GetInputs()) {
175       if (input == phi) {
176         continue;
177       } else if (candidate == nullptr) {
178         candidate = input;
179       } else if (candidate != input) {
180         candidate = nullptr;
181         break;
182       }
183     }
184 
185     // If we haven't found a candidate, check for a phi cycle. Note that we need to detect
186     // such cycles to avoid having reference and non-reference equivalents. We check this
187     // invariant in the graph checker.
188     if (candidate == nullptr) {
189       // We iterate over the array as long as it grows.
190       for (size_t i = 0; i < cycle_worklist.size(); ++i) {
191         HPhi* current = cycle_worklist[i];
192         DCHECK(!current->IsLoopHeaderPhi() ||
193                current->GetBlock()->IsLoopPreHeaderFirstPredecessor());
194 
195         for (HInstruction* input : current->GetInputs()) {
196           if (input == current) {
197             continue;
198           } else if (input->IsPhi()) {
199             if (!visited_phis_in_cycle.IsBitSet(input->GetId())) {
200               cycle_worklist.push_back(input->AsPhi());
201               visited_phis_in_cycle.SetBit(input->GetId());
202               catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi();
203               irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi();
204             } else {
205               // Already visited, nothing to do.
206             }
207           } else if (candidate == nullptr) {
208             candidate = input;
209           } else if (candidate != input) {
210             candidate = nullptr;
211             // Clear the cycle worklist to break out of the outer loop.
212             cycle_worklist.clear();
213             break;
214           }
215         }
216       }
217     }
218 
219     if (candidate == nullptr) {
220       continue;
221     }
222 
223     if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) {
224       // For irreducible loops, we need to keep the phis to satisfy our linear scan
225       // algorithm.
226       // There is one exception for constants, as the type propagation requires redundant
227       // cyclic phis of a constant to be removed. This is ok for the linear scan as it
228       // has to deal with constants anyway, and they can trivially be rematerialized.
229       continue;
230     }
231 
232     for (HPhi* current : cycle_worklist) {
233       // The candidate may not dominate a phi in a catch block: there may be non-throwing
234       // instructions at the beginning of a try range, that may be the first input of
235       // catch phis.
236       // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions
237       // before the try entry.
238       if (catch_phi_in_cycle) {
239         if (!candidate->StrictlyDominates(current)) {
240           continue;
241         }
242       } else {
243         DCHECK(candidate->StrictlyDominates(current));
244       }
245 
246       // Because we're updating the users of this phi, we may have new candidates
247       // for elimination. Add phis that use this phi to the worklist.
248       for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
249         HInstruction* user = use.GetUser();
250         if (user->IsPhi() && !visited_phis_in_cycle.IsBitSet(user->GetId())) {
251           worklist.push_back(user->AsPhi());
252         }
253       }
254       DCHECK(candidate->StrictlyDominates(current));
255       current->ReplaceWith(candidate);
256       current->GetBlock()->RemovePhi(current);
257     }
258   }
259   return true;
260 }
261 
262 }  // namespace art
263