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