ssa_phi_elimination.cc revision d6138ef1ea13d07ae555542f8898b30d89e9ac9a
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_.Add(phi);
39          phi->SetLive();
40          break;
41        }
42      }
43    }
44  }
45
46  // Process the worklist by propagating liveness to phi inputs.
47  while (!worklist_.IsEmpty()) {
48    HPhi* phi = worklist_.Pop();
49    for (HInputIterator it(phi); !it.Done(); it.Advance()) {
50      HInstruction* input = it.Current();
51      if (input->IsPhi() && input->AsPhi()->IsDead()) {
52        worklist_.Add(input->AsPhi());
53        input->AsPhi()->SetLive();
54      }
55    }
56  }
57}
58
59void SsaDeadPhiElimination::EliminateDeadPhis() {
60  // Remove phis that are not live. Visit in post order so that phis
61  // that are not inputs of loop phis can be removed when they have
62  // no users left (dead phis might use dead phis).
63  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
64    HBasicBlock* block = it.Current();
65    HInstruction* current = block->GetFirstPhi();
66    HInstruction* next = nullptr;
67    while (current != nullptr) {
68      next = current->GetNext();
69      if (current->AsPhi()->IsDead()) {
70        if (current->HasUses()) {
71          for (HUseIterator<HInstruction*> use_it(current->GetUses()); !use_it.Done();
72               use_it.Advance()) {
73            HUseListNode<HInstruction*>* user_node = use_it.Current();
74            HInstruction* user = user_node->GetUser();
75            DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
76            DCHECK(user->AsPhi()->IsDead()) << user->GetId();
77            // Just put itself as an input. The phi will be removed in this loop anyway.
78            user->SetRawInputAt(user_node->GetIndex(), user);
79            current->RemoveUser(user, user_node->GetIndex());
80          }
81        }
82        if (current->HasEnvironmentUses()) {
83          for (HUseIterator<HEnvironment*> use_it(current->GetEnvUses()); !use_it.Done();
84               use_it.Advance()) {
85            HUseListNode<HEnvironment*>* user_node = use_it.Current();
86            HEnvironment* user = user_node->GetUser();
87            user->SetRawEnvAt(user_node->GetIndex(), nullptr);
88            current->RemoveEnvironmentUser(user_node);
89          }
90        }
91        block->RemovePhi(current->AsPhi());
92      }
93      current = next;
94    }
95  }
96}
97
98void SsaRedundantPhiElimination::Run() {
99  // Add all phis in the worklist.
100  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
101    HBasicBlock* block = it.Current();
102    for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
103      worklist_.Add(inst_it.Current()->AsPhi());
104    }
105  }
106
107  while (!worklist_.IsEmpty()) {
108    HPhi* phi = worklist_.Pop();
109
110    // If the phi has already been processed, continue.
111    if (!phi->IsInBlock()) {
112      continue;
113    }
114
115    // Find if the inputs of the phi are the same instruction.
116    HInstruction* candidate = phi->InputAt(0);
117    // A loop phi cannot have itself as the first phi. Note that this
118    // check relies on our simplification pass ensuring the pre-header
119    // block is first in the list of predecessors of the loop header.
120    DCHECK(!phi->IsLoopHeaderPhi() || phi->GetBlock()->IsLoopPreHeaderFirstPredecessor());
121    DCHECK_NE(phi, candidate);
122
123    for (size_t i = 1; i < phi->InputCount(); ++i) {
124      HInstruction* input = phi->InputAt(i);
125      // For a loop phi, if the input is the phi, the phi is still candidate for
126      // elimination.
127      if (input != candidate && input != phi) {
128        candidate = nullptr;
129        break;
130      }
131    }
132
133    // If the inputs are not the same, continue.
134    if (candidate == nullptr) {
135      continue;
136    }
137
138    if (phi->IsInLoop()) {
139      // Because we're updating the users of this phi, we may have new
140      // phis candidate for elimination if this phi is in a loop. Add phis that
141      // used this phi to the worklist.
142      for (HUseIterator<HInstruction*> it(phi->GetUses()); !it.Done(); it.Advance()) {
143        HUseListNode<HInstruction*>* current = it.Current();
144        HInstruction* user = current->GetUser();
145        if (user->IsPhi()) {
146          worklist_.Add(user->AsPhi());
147        }
148      }
149    }
150    phi->ReplaceWith(candidate);
151    phi->GetBlock()->RemovePhi(phi);
152  }
153}
154
155}  // namespace art
156