| /* | 
 |  * Copyright (C) 2014 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "ssa_phi_elimination.h" | 
 |  | 
 | #include "base/arena_containers.h" | 
 | #include "base/bit_vector-inl.h" | 
 |  | 
 | namespace art { | 
 |  | 
 | void SsaDeadPhiElimination::Run() { | 
 |   MarkDeadPhis(); | 
 |   EliminateDeadPhis(); | 
 | } | 
 |  | 
 | void SsaDeadPhiElimination::MarkDeadPhis() { | 
 |   // Phis are constructed live and should not be revived if previously marked | 
 |   // dead. This algorithm temporarily breaks that invariant but we DCHECK that | 
 |   // only phis which were initially live are revived. | 
 |   ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter()); | 
 |  | 
 |   // Add to the worklist phis referenced by non-phi instructions. | 
 |   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { | 
 |     HBasicBlock* block = it.Current(); | 
 |     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { | 
 |       HPhi* phi = inst_it.Current()->AsPhi(); | 
 |       if (phi->IsDead()) { | 
 |         continue; | 
 |       } | 
 |  | 
 |       bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses()); | 
 |       if (!keep_alive) { | 
 |         for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) { | 
 |           if (!use_it.Current()->GetUser()->IsPhi()) { | 
 |             keep_alive = true; | 
 |             break; | 
 |           } | 
 |         } | 
 |       } | 
 |  | 
 |       if (keep_alive) { | 
 |         worklist_.push_back(phi); | 
 |       } else { | 
 |         phi->SetDead(); | 
 |         if (kIsDebugBuild) { | 
 |           initially_live.insert(phi); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Process the worklist by propagating liveness to phi inputs. | 
 |   while (!worklist_.empty()) { | 
 |     HPhi* phi = worklist_.back(); | 
 |     worklist_.pop_back(); | 
 |     for (HInputIterator it(phi); !it.Done(); it.Advance()) { | 
 |       HPhi* input = it.Current()->AsPhi(); | 
 |       if (input != nullptr && input->IsDead()) { | 
 |         // Input is a dead phi. Revive it and add to the worklist. We make sure | 
 |         // that the phi was not dead initially (see definition of `initially_live`). | 
 |         DCHECK(ContainsElement(initially_live, input)); | 
 |         input->SetLive(); | 
 |         worklist_.push_back(input); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SsaDeadPhiElimination::EliminateDeadPhis() { | 
 |   // Remove phis that are not live. Visit in post order so that phis | 
 |   // that are not inputs of loop phis can be removed when they have | 
 |   // no users left (dead phis might use dead phis). | 
 |   for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { | 
 |     HBasicBlock* block = it.Current(); | 
 |     HInstruction* current = block->GetFirstPhi(); | 
 |     HInstruction* next = nullptr; | 
 |     HPhi* phi; | 
 |     while (current != nullptr) { | 
 |       phi = current->AsPhi(); | 
 |       next = current->GetNext(); | 
 |       if (phi->IsDead()) { | 
 |         // Make sure the phi is only used by other dead phis. | 
 |         if (kIsDebugBuild) { | 
 |           for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); | 
 |                use_it.Advance()) { | 
 |             HInstruction* user = use_it.Current()->GetUser(); | 
 |             DCHECK(user->IsLoopHeaderPhi()); | 
 |             DCHECK(user->AsPhi()->IsDead()); | 
 |           } | 
 |         } | 
 |         // Remove the phi from use lists of its inputs. | 
 |         for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { | 
 |           phi->RemoveAsUserOfInput(i); | 
 |         } | 
 |         // Remove the phi from environments that use it. | 
 |         for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done(); | 
 |              use_it.Advance()) { | 
 |           HUseListNode<HEnvironment*>* user_node = use_it.Current(); | 
 |           HEnvironment* user = user_node->GetUser(); | 
 |           user->SetRawEnvAt(user_node->GetIndex(), nullptr); | 
 |         } | 
 |         // Delete it from the instruction list. | 
 |         block->RemovePhi(phi, /*ensure_safety=*/ false); | 
 |       } | 
 |       current = next; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void SsaRedundantPhiElimination::Run() { | 
 |   // Add all phis in the worklist. Order does not matter for correctness, and | 
 |   // neither will necessarily converge faster. | 
 |   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { | 
 |     HBasicBlock* block = it.Current(); | 
 |     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { | 
 |       worklist_.push_back(inst_it.Current()->AsPhi()); | 
 |     } | 
 |   } | 
 |  | 
 |   ArenaSet<uint32_t> visited_phis_in_cycle(graph_->GetArena()->Adapter()); | 
 |   ArenaVector<HPhi*> cycle_worklist(graph_->GetArena()->Adapter()); | 
 |  | 
 |   while (!worklist_.empty()) { | 
 |     HPhi* phi = worklist_.back(); | 
 |     worklist_.pop_back(); | 
 |  | 
 |     // If the phi has already been processed, continue. | 
 |     if (!phi->IsInBlock()) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (phi->InputCount() == 0) { | 
 |       DCHECK(phi->IsDead()); | 
 |       continue; | 
 |     } | 
 |  | 
 |     HInstruction* candidate = nullptr; | 
 |     visited_phis_in_cycle.clear(); | 
 |     cycle_worklist.clear(); | 
 |  | 
 |     cycle_worklist.push_back(phi); | 
 |     visited_phis_in_cycle.insert(phi->GetId()); | 
 |     bool catch_phi_in_cycle = phi->IsCatchPhi(); | 
 |     bool irreducible_loop_phi_in_cycle = phi->IsIrreducibleLoopHeaderPhi(); | 
 |  | 
 |     // First do a simple loop over inputs and check if they are all the same. | 
 |     for (size_t j = 0; j < phi->InputCount(); ++j) { | 
 |       HInstruction* input = phi->InputAt(j); | 
 |       if (input == phi) { | 
 |         continue; | 
 |       } else if (candidate == nullptr) { | 
 |         candidate = input; | 
 |       } else if (candidate != input) { | 
 |         candidate = nullptr; | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     // If we haven't found a candidate, check for a phi cycle. Note that we need to detect | 
 |     // such cycles to avoid having reference and non-reference equivalents. We check this | 
 |     // invariant in the graph checker. | 
 |     if (candidate == nullptr) { | 
 |       // We iterate over the array as long as it grows. | 
 |       for (size_t i = 0; i < cycle_worklist.size(); ++i) { | 
 |         HPhi* current = cycle_worklist[i]; | 
 |         DCHECK(!current->IsLoopHeaderPhi() || | 
 |                current->GetBlock()->IsLoopPreHeaderFirstPredecessor()); | 
 |  | 
 |         for (size_t j = 0; j < current->InputCount(); ++j) { | 
 |           HInstruction* input = current->InputAt(j); | 
 |           if (input == current) { | 
 |             continue; | 
 |           } else if (input->IsPhi()) { | 
 |             if (!ContainsElement(visited_phis_in_cycle, input->GetId())) { | 
 |               cycle_worklist.push_back(input->AsPhi()); | 
 |               visited_phis_in_cycle.insert(input->GetId()); | 
 |               catch_phi_in_cycle |= input->AsPhi()->IsCatchPhi(); | 
 |               irreducible_loop_phi_in_cycle |= input->IsIrreducibleLoopHeaderPhi(); | 
 |             } else { | 
 |               // Already visited, nothing to do. | 
 |             } | 
 |           } else if (candidate == nullptr) { | 
 |             candidate = input; | 
 |           } else if (candidate != input) { | 
 |             candidate = nullptr; | 
 |             // Clear the cycle worklist to break out of the outer loop. | 
 |             cycle_worklist.clear(); | 
 |             break; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     if (candidate == nullptr) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (irreducible_loop_phi_in_cycle && !candidate->IsConstant()) { | 
 |       // For irreducible loops, we need to keep the phis to satisfy our linear scan | 
 |       // algorithm. | 
 |       // There is one exception for constants, as the type propagation requires redundant | 
 |       // cyclic phis of a constant to be removed. This is ok for the linear scan as it | 
 |       // has to deal with constants anyway, and they can trivially be rematerialized. | 
 |       continue; | 
 |     } | 
 |  | 
 |     for (HPhi* current : cycle_worklist) { | 
 |       // The candidate may not dominate a phi in a catch block: there may be non-throwing | 
 |       // instructions at the beginning of a try range, that may be the first input of | 
 |       // catch phis. | 
 |       // TODO(dbrazdil): Remove this situation by moving those non-throwing instructions | 
 |       // before the try entry. | 
 |       if (catch_phi_in_cycle) { | 
 |         if (!candidate->StrictlyDominates(current)) { | 
 |           continue; | 
 |         } | 
 |       } else { | 
 |         DCHECK(candidate->StrictlyDominates(current)); | 
 |       } | 
 |  | 
 |       // Because we're updating the users of this phi, we may have new candidates | 
 |       // for elimination. Add phis that use this phi to the worklist. | 
 |       for (HUseIterator<HInstruction*> it(current->GetUses()); !it.Done(); it.Advance()) { | 
 |         HUseListNode<HInstruction*>* use = it.Current(); | 
 |         HInstruction* user = use->GetUser(); | 
 |         if (user->IsPhi() && !ContainsElement(visited_phis_in_cycle, user->GetId())) { | 
 |           worklist_.push_back(user->AsPhi()); | 
 |         } | 
 |       } | 
 |       DCHECK(candidate->StrictlyDominates(current)); | 
 |       current->ReplaceWith(candidate); | 
 |       current->GetBlock()->RemovePhi(current); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace art |