ssa_phi_elimination.cc revision 2aaa4b5532d30c4e65d8892b556400bb61f9dc8c
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
19namespace art {
20
21void SsaDeadPhiElimination::Run() {
22  MarkDeadPhis();
23  EliminateDeadPhis();
24}
25
26void SsaDeadPhiElimination::MarkDeadPhis() {
27  // Add to the worklist phis referenced by non-phi instructions.
28  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
29    HBasicBlock* block = it.Current();
30    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
31      HPhi* phi = inst_it.Current()->AsPhi();
32      // Set dead ahead of running through uses. The phi may have no use.
33      phi->SetDead();
34      for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
35        HUseListNode<HInstruction*>* current = use_it.Current();
36        HInstruction* user = current->GetUser();
37        if (!user->IsPhi()) {
38          worklist_.push_back(phi);
39          phi->SetLive();
40          break;
41        }
42      }
43    }
44  }
45
46  // Process the worklist by propagating liveness to phi inputs.
47  while (!worklist_.empty()) {
48    HPhi* phi = worklist_.back();
49    worklist_.pop_back();
50    for (HInputIterator it(phi); !it.Done(); it.Advance()) {
51      HInstruction* input = it.Current();
52      if (input->IsPhi() && input->AsPhi()->IsDead()) {
53        worklist_.push_back(input->AsPhi());
54        input->AsPhi()->SetLive();
55      }
56    }
57  }
58}
59
60void SsaDeadPhiElimination::EliminateDeadPhis() {
61  // Remove phis that are not live. Visit in post order so that phis
62  // that are not inputs of loop phis can be removed when they have
63  // no users left (dead phis might use dead phis).
64  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
65    HBasicBlock* block = it.Current();
66    HInstruction* current = block->GetFirstPhi();
67    HInstruction* next = nullptr;
68    HPhi* phi;
69    while (current != nullptr) {
70      phi = current->AsPhi();
71      next = current->GetNext();
72      if (phi->IsDead()) {
73        // Make sure the phi is only used by other dead phis.
74        if (kIsDebugBuild) {
75          for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
76               use_it.Advance()) {
77            HInstruction* user = use_it.Current()->GetUser();
78            DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
79            DCHECK(user->AsPhi()->IsDead()) << user->GetId();
80          }
81        }
82        // Remove the phi from use lists of its inputs.
83        for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
84          phi->RemoveAsUserOfInput(i);
85        }
86        // Remove the phi from environments that use it.
87        for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done();
88             use_it.Advance()) {
89          HUseListNode<HEnvironment*>* user_node = use_it.Current();
90          HEnvironment* user = user_node->GetUser();
91          user->SetRawEnvAt(user_node->GetIndex(), nullptr);
92        }
93        // Delete it from the instruction list.
94        block->RemovePhi(phi, /*ensure_safety=*/ false);
95      }
96      current = next;
97    }
98  }
99}
100
101void SsaRedundantPhiElimination::Run() {
102  // Add all phis in the worklist. Order does not matter for correctness, and
103  // neither will necessarily converge faster.
104  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
105    HBasicBlock* block = it.Current();
106    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
107      worklist_.push_back(inst_it.Current()->AsPhi());
108    }
109  }
110
111  while (!worklist_.empty()) {
112    HPhi* phi = worklist_.back();
113    worklist_.pop_back();
114
115    // If the phi has already been processed, continue.
116    if (!phi->IsInBlock()) {
117      continue;
118    }
119
120    if (phi->InputCount() == 0) {
121      DCHECK(phi->IsCatchPhi());
122      DCHECK(phi->IsDead());
123      continue;
124    }
125
126    // Find if the inputs of the phi are the same instruction.
127    HInstruction* candidate = phi->InputAt(0);
128    // A loop phi cannot have itself as the first phi. Note that this
129    // check relies on our simplification pass ensuring the pre-header
130    // block is first in the list of predecessors of the loop header.
131    DCHECK(!phi->IsLoopHeaderPhi() || phi->GetBlock()->IsLoopPreHeaderFirstPredecessor());
132    DCHECK_NE(phi, candidate);
133
134    for (size_t i = 1; i < phi->InputCount(); ++i) {
135      HInstruction* input = phi->InputAt(i);
136      // For a loop phi, if the input is the phi, the phi is still candidate for
137      // elimination.
138      if (input != candidate && input != phi) {
139        candidate = nullptr;
140        break;
141      }
142    }
143
144    // If the inputs are not the same, continue.
145    if (candidate == nullptr) {
146      continue;
147    }
148
149    // The candidate may not dominate a phi in a catch block.
150    if (phi->IsCatchPhi() && !candidate->StrictlyDominates(phi)) {
151      continue;
152    }
153
154    // Because we're updating the users of this phi, we may have new candidates
155    // for elimination. Add phis that use this phi to the worklist.
156    for (HUseIterator<HInstruction*> it(phi->GetUses()); !it.Done(); it.Advance()) {
157      HUseListNode<HInstruction*>* current = it.Current();
158      HInstruction* user = current->GetUser();
159      if (user->IsPhi()) {
160        worklist_.push_back(user->AsPhi());
161      }
162    }
163
164    phi->ReplaceWith(candidate);
165    phi->GetBlock()->RemovePhi(phi);
166  }
167}
168
169}  // namespace art
170