Home | History | Annotate | Download | only in optimizing
      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 
     23 namespace art {
     24 
     25 void SsaDeadPhiElimination::Run() {
     26   MarkDeadPhis();
     27   EliminateDeadPhis();
     28 }
     29 
     30 void 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 
     83 void 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 
    121 void 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