Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  dead_code_elimination.cc

  Sprache: C
 

/*
 * 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 "dead_code_elimination.h"

#include "android-base/logging.h"
#include "base/array_ref.h"
#include "base/bit_vector-inl.h"
#include "base/logging.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
#include "optimizing/nodes.h"
#include "optimizing/nodes_vector.h"
#include "ssa_phi_elimination.h"

namespace art HIDDEN {

static void MarkReachableBlocks(HGraph* graph, BitVectorView<size_t> visited) {
  // Use local allocator for allocating memory.
  ScopedArenaAllocator allocator(graph->GetArenaStack());

  ScopedArenaVector<HBasicBlock*> worklist(allocator.Adapter(kArenaAllocDCE));
  constexpr size_t kDefaultWorlistSize = 8;
  worklist.reserve(kDefaultWorlistSize);
  visited.SetBit(graph->GetEntryBlock()->GetBlockId());
  worklist.push_back(graph->GetEntryBlock());

  while (!worklist.empty()) {
    HBasicBlock* block = worklist.back();
    worklist.pop_back();
    int block_id = block->GetBlockId();
    DCHECK(visited.IsBitSet(block_id));

    ArrayRef<HBasicBlock* const> live_successors(block->GetSuccessors());
    HInstruction* last_instruction = block->GetLastInstruction();
    if (last_instruction->IsIf()) {
      HIf* if_instruction = last_instruction->AsIf();
      HInstruction* condition = if_instruction->InputAt(0);
      if (condition->IsIntConstant()) {
        if (condition->AsIntConstant()->IsTrue()) {
          live_successors = live_successors.SubArray(0u, 1u);
          DCHECK_EQ(live_successors[0], if_instruction->IfTrueSuccessor());
        } else {
          DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue();
          live_successors = live_successors.SubArray(1u, 1u);
          DCHECK_EQ(live_successors[0], if_instruction->IfFalseSuccessor());
        }
      }
    } else if (last_instruction->IsPackedSwitch()) {
      HPackedSwitch* switch_instruction = last_instruction->AsPackedSwitch();
      HInstruction* switch_input = switch_instruction->InputAt(0);
      if (switch_input->IsIntConstant()) {
        int32_t switch_value = switch_input->AsIntConstant()->GetValue();
        int32_t start_value = switch_instruction->GetStartValue();
        // Note: Though the spec forbids packed-switch values to wrap around, we leave
        // that task to the verifier and use unsigned arithmetic with it's "modulo 2^32"
        // semantics to check if the value is in range, wrapped or not.
        uint32_t switch_index =
            static_cast<uint32_t>(switch_value) - static_cast<uint32_t>(start_value);
        if (switch_index < switch_instruction->GetNumEntries()) {
          live_successors = live_successors.SubArray(switch_index, 1u);
          DCHECK_EQ(live_successors[0], block->GetSuccessors()[switch_index]);
        } else {
          live_successors = live_successors.SubArray(switch_instruction->GetNumEntries(), 1u);
          DCHECK_EQ(live_successors[0], switch_instruction->GetDefaultBlock());
        }
      }
    }

    for (HBasicBlock* successor : live_successors) {
      // Add only those successors that have not been visited yet.
      if (!visited.IsBitSet(successor->GetBlockId())) {
        visited.SetBit(successor->GetBlockId());
        worklist.push_back(successor);
      }
    }
  }
}

void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
  if (stats_ != nullptr) {
    stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
                       block->GetPhis().CountSize() + block->GetInstructions().CountSize());
  }
}

void HDeadCodeElimination::MaybeRecordSimplifyIf() {
  if (stats_ != nullptr) {
    stats_->RecordStat(MethodCompilationStat::kSimplifyIf);
  }
}

static bool HasInput(HCondition* instruction, HInstruction* input) {
  return (instruction->InputAt(0) == input) ||
         (instruction->InputAt(1) == input);
}

static bool HasEquality(IfCondition condition) {
  switch (condition) {
    case kCondEQ:
    case kCondLE:
    case kCondGE:
    case kCondBE:
    case kCondAE:
      return true;
    case kCondNE:
    case kCondLT:
    case kCondGT:
    case kCondB:
    case kCondA:
      return false;
  }
}

static HConstant* Evaluate(HCondition* condition, HInstruction* left, HInstruction* right) {
  if (left == right && !DataType::IsFloatingPointType(left->GetType())) {
    return condition->GetBlock()->GetGraph()->GetIntConstant(
        HasEquality(condition->GetCondition()) ? 1 : 0);
  }

  if (!left->IsConstant() || !right->IsConstant()) {
    return nullptr;
  }

  if (left->IsIntConstant()) {
    return condition->Evaluate(left->AsIntConstant(), right->AsIntConstant());
  } else if (left->IsNullConstant()) {
    return condition->Evaluate(left->AsNullConstant(), right->AsNullConstant());
  } else if (left->IsLongConstant()) {
    return condition->Evaluate(left->AsLongConstant(), right->AsLongConstant());
  } else if (left->IsFloatConstant()) {
    return condition->Evaluate(left->AsFloatConstant(), right->AsFloatConstant());
  } else {
    DCHECK(left->IsDoubleConstant());
    return condition->Evaluate(left->AsDoubleConstant(), right->AsDoubleConstant());
  }
}

static bool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) {
  // Test for an if as last statement.
  if (!block->EndsWithIf()) {
    return false;
  }
  HIf* ifs = block->GetLastInstruction()->AsIf();
  // Find either:
  //   if obj == null
  //     throws
  //   else
  //     not_throws
  // or:
  //   if obj != null
  //     not_throws
  //   else
  //     throws
  HInstruction* cond = ifs->InputAt(0);
  HBasicBlock* not_throws = nullptr;
  if (throws == ifs->IfTrueSuccessor() && cond->IsEqual()) {
    not_throws = ifs->IfFalseSuccessor();
  } else if (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
    not_throws = ifs->IfTrueSuccessor();
  } else {
    return false;
  }
  DCHECK(cond->IsEqual() || cond->IsNotEqual());
  HInstruction* obj = cond->InputAt(1);
  if (obj->IsNullConstant()) {
    obj = cond->InputAt(0);
  } else if (!cond->InputAt(0)->IsNullConstant()) {
    return false;
  }

  // We can't create a BoundType for an object with an invalid RTI.
  const ReferenceTypeInfo ti = obj->GetReferenceTypeInfo();
  if (!ti.IsValid()) {
    return false;
  }

  // Scan all uses of obj and find null check under control dependence.
  HBoundType* bound = nullptr;
  const HUseList<HInstruction*>& uses = obj->GetUses();
  for (auto it = uses.begin(), end = uses.end(); it != end;) {
    HInstruction* user = it->GetUser();
    ++it;  // increment before possibly replacing
    if (user->IsNullCheck()) {
      HBasicBlock* user_block = user->GetBlock();
      if (user_block != block &&
          user_block != throws &&
          block->Dominates(user_block)) {
        if (bound == nullptr) {
          bound = new (block->GetGraph()->GetAllocator()) HBoundType(obj);
          bound->SetUpperBound(ti, /*can_be_null*/ false);
          bound->SetReferenceTypeInfo(ti);
          bound->SetCanBeNull(false);
          not_throws->InsertInstructionBefore(bound, not_throws->GetFirstInstruction());
        }
        user->ReplaceWith(bound);
        user_block->RemoveInstruction(user);
      }
    }
  }
  return bound != nullptr;
}

// Simplify the pattern:
//
//           B1
//          /  \
//          |   instr_1
//          |   ...
//          |   instr_n
//          |   foo()  // always throws
//          |   instr_n+2
//          |   ...
//          |   instr_n+m
//          \   goto B2
//           \ /
//            B2
//
// Into:
//
//           B1
//          /  \
//          |  instr_1
//          |  ...
//          |  instr_n
//          |  foo()
//          |  goto Exit
//          |   |
//         B2  Exit
//
// Rationale:
// Removal of the never taken edge to B2 may expose other optimization opportunities, such as code
// sinking.
//
// Note: The example above is a simple one that uses a `goto` but we could end the block with an If,
// for example.
bool HDeadCodeElimination::SimplifyAlwaysThrows() {
  HBasicBlock* exit = graph_->GetExitBlock();
  if (!graph_->HasAlwaysThrowingInvokes() || exit == nullptr) {
    return false;
  }

  bool rerun_dominance_and_loop_analysis = false;

  // Order does not matter, just pick one.
  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
    if (block->IsTryBlock()) {
      // We don't want to perform the simplify always throws optimizations for throws inside of
      // tries since those throws might not go to the exit block.
      continue;
    }

    // We iterate to find the first instruction that always throws. If two instructions always
    // throw, the first one will throw and the second one will never be reached.
    HInstruction* throwing_invoke = nullptr;
    for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
      if (it.Current()->IsInvoke() && it.Current()->AsInvoke()->AlwaysThrows()) {
        throwing_invoke = it.Current();
        break;
      }
    }

    if (throwing_invoke == nullptr) {
      // No always-throwing instruction found. Continue with the rest of the blocks.
      continue;
    }

    // If we are already pointing at the exit block we could still remove the instructions
    // between the always throwing instruction, and the exit block. If we have no other
    // instructions, just continue since there's nothing to do.
    if (block->GetSuccessors().size() == 1 &&
        block->GetSingleSuccessor() == exit &&
        block->GetLastInstruction()->GetPrevious() == throwing_invoke) {
      continue;
    }

    // We split the block at the throwing instruction, and the instructions after the throwing
    // instructions will be disconnected from the graph after `block` points to the exit.
    // `RemoveDeadBlocks` will take care of removing this new block and its instructions.
    HBasicBlock* new_block = block->SplitBefore(throwing_invoke->GetNext());
    DCHECK_EQ(block->GetSingleSuccessor(), new_block);
    block->ReplaceSuccessor(new_block, exit);

    rerun_dominance_and_loop_analysis = true;
    MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
    // Perform a quick follow up optimization on object != null control dependences
    // that is much cheaper to perform now than in a later phase.
    // If there are multiple predecessors, none may end with a HIf as required in
    // RemoveNonNullControlDependences because we split critical edges.
    if (block->GetPredecessors().size() == 1u &&
        RemoveNonNullControlDependences(block->GetSinglePredecessor(), block)) {
      MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
    }
  }

  // We need to re-analyze the graph in order to run DCE afterwards.
  if (rerun_dominance_and_loop_analysis) {
    graph_->RecomputeDominatorTree();
    return true;
  }
  return false;
}

bool HDeadCodeElimination::SimplifyIfs() {
  bool simplified_one_or_more_ifs = false;
  bool rerun_dominance_and_loop_analysis = false;

  // Iterating in PostOrder it's better for MaybeAddPhi as it can add a Phi for multiple If
  // instructions in a chain without updating the dominator chain. The branch redirection itself can
  // work in PostOrder or ReversePostOrder without issues.
  for (HBasicBlock* block : graph_->GetPostOrder()) {
    if (block->IsCatchBlock()) {
      // This simplification cannot be applied to catch blocks, because exception handler edges do
      // not represent normal control flow. Though in theory this could still apply to normal
      // control flow going directly to a catch block, we cannot support it at the moment because
      // the catch Phi's inputs do not correspond to the catch block's predecessors, so we cannot
      // identify which predecessor corresponds to a given statically evaluated input.
      continue;
    }

    HInstruction* last = block->GetLastInstruction();
    if (!last->IsIf()) {
      continue;
    }

    if (block->IsLoopHeader()) {
      // We do not apply this optimization to loop headers as this could create irreducible loops.
      continue;
    }

    // We will add a Phi which allows the simplification to take place in cases where it wouldn't.
    MaybeAddPhi(block);

    // TODO(solanes): Investigate support for multiple phis in `block`. We can potentially "push
    // downwards" existing Phis into the true/false branches. For example, let's say we have another
    // Phi: Phi(x1,x2,x3,x4,x5,x6). This could turn into Phi(x1,x2) in the true branch, Phi(x3,x4)
    // in the false branch, and remain as Phi(x5,x6) in `block` (for edges that we couldn't
    // redirect). We might even be able to remove some phis altogether as they will have only one
    // value.
    if (block->HasSinglePhi() &&
        block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
      HInstruction* first = block->GetFirstInstruction();
      bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
      bool has_only_phi_condition_and_if =
          !has_only_phi_and_if &&
          first->IsCondition() &&
          HasInput(first->AsCondition(), block->GetFirstPhi()) &&
          (first->GetNext() == last) &&
          (last->InputAt(0) == first) &&
          first->HasOnlyOneNonEnvironmentUse();

      if (has_only_phi_and_if || has_only_phi_condition_and_if) {
        HPhi* phi = block->GetFirstPhi()->AsPhi();
        bool phi_input_is_left = (first->InputAt(0) == phi);

        // Walk over all inputs of the phis and update the control flow of
        // predecessors feeding constants to the phi.
        // Note that phi->InputCount() may change inside the loop.
        for (size_t i = 0; i < phi->InputCount();) {
          HInstruction* input = phi->InputAt(i);
          HInstruction* value_to_check = nullptr;
          if (has_only_phi_and_if) {
            if (input->IsIntConstant()) {
              value_to_check = input;
            }
          } else {
            DCHECK(has_only_phi_condition_and_if);
            if (phi_input_is_left) {
              value_to_check = Evaluate(first->AsCondition(), input, first->InputAt(1));
            } else {
              value_to_check = Evaluate(first->AsCondition(), first->InputAt(0), input);
            }
          }
          if (value_to_check == nullptr) {
            // Could not evaluate to a constant, continue iterating over the inputs.
            ++i;
          } else {
            HBasicBlock* predecessor_to_update = block->GetPredecessors()[i];
            HBasicBlock* successor_to_update = nullptr;
            if (value_to_check->AsIntConstant()->IsTrue()) {
              successor_to_update = last->AsIf()->IfTrueSuccessor();
            } else {
              DCHECK(value_to_check->AsIntConstant()->IsFalse())
                  << value_to_check->AsIntConstant()->GetValue();
              successor_to_update = last->AsIf()->IfFalseSuccessor();
            }
            predecessor_to_update->ReplaceSuccessor(block, successor_to_update);
            phi->RemoveInputAt(i);
            simplified_one_or_more_ifs = true;
            if (block->IsInLoop()) {
              rerun_dominance_and_loop_analysis = true;
            }
            // For simplicity, don't create a dead block, let the dead code elimination
            // pass deal with it.
            if (phi->InputCount() == 1) {
              break;
            }
          }
        }
        if (block->GetPredecessors().size() == 1) {
          phi->ReplaceWith(phi->InputAt(0));
          block->RemovePhi(phi);
          if (has_only_phi_condition_and_if) {
            // Evaluate here (and not wait for a constant folding pass) to open
            // more opportunities for DCE.
            HInstruction* result = first->AsCondition()->TryStaticEvaluation();
            if (result != nullptr) {
              first->ReplaceWith(result);
              block->RemoveInstruction(first);
            }
          }
        }
        if (simplified_one_or_more_ifs) {
          MaybeRecordSimplifyIf();
        }
      }
    }
  }
  // We need to re-analyze the graph in order to run DCE afterwards.
  if (simplified_one_or_more_ifs) {
    if (rerun_dominance_and_loop_analysis) {
      graph_->RecomputeDominatorTree();
    } else {
      graph_->ClearDominanceInformation();
      // We have introduced critical edges, remove them.
      graph_->SimplifyCFG();
      graph_->ComputeDominanceInformation();
      graph_->ComputeTryBlockInformation();
    }
  }

  return simplified_one_or_more_ifs;
}

void HDeadCodeElimination::MaybeAddPhi(HBasicBlock* block) {
  DCHECK(block->GetLastInstruction()->IsIf());
  HIf* if_instruction = block->GetLastInstruction()->AsIf();
  if (if_instruction->InputAt(0)->IsConstant()) {
    // Constant values are handled in RemoveDeadBlocks.
    return;
  }

  if (block->GetNumberOfPredecessors() < 2u) {
    // Nothing to redirect.
    return;
  }

  if (!block->GetPhis().IsEmpty()) {
    // SimplifyIf doesn't currently work with multiple phis. Adding a phi here won't help that
    // optimization.
    return;
  }

  HBasicBlock* dominator = block->GetDominator();
  if (!dominator->EndsWithIf()) {
    return;
  }

  HInstruction* input = if_instruction->InputAt(0);
  HInstruction* dominator_input = dominator->GetLastInstruction()->AsIf()->InputAt(0);
  const bool same_input = dominator_input == input;
  if (!same_input) {
    // Try to see if the dominator has the opposite input (e.g. if(cond) and if(!cond)). If that's
    // the case, we can perform the optimization with the false and true branches reversed.
    if (!dominator_input->IsCondition() || !input->IsCondition()) {
      return;
    }

    HCondition* block_cond = input->AsCondition();
    HCondition* dominator_cond = dominator_input->AsCondition();

    if (block_cond->GetLeft() != dominator_cond->GetLeft() ||
        block_cond->GetRight() != dominator_cond->GetRight() ||
        block_cond->GetOppositeCondition() != dominator_cond->GetCondition() ||
        block_cond->GetBias() != dominator_cond->GetBias()) {
      return;
    }
  }

  if (kIsDebugBuild) {
    // `block`'s successors should have only one predecessor. Otherwise, we have a critical edge in
    // the graph.
    for (HBasicBlock* succ : block->GetSuccessors()) {
      DCHECK_EQ(succ->GetNumberOfPredecessors(), 1u);
    }
  }

  const size_t pred_size = block->GetNumberOfPredecessors();
  HPhi* new_phi = new (graph_->GetAllocator())
      HPhi(graph_->GetAllocator(), kNoRegNumber, pred_size, DataType::Type::kInt32);

  for (size_t index = 0; index < pred_size; index++) {
    HBasicBlock* pred = block->GetPredecessors()[index];
    const bool dominated_by_true =
        dominator->GetLastInstruction()->AsIf()->IfTrueSuccessor()->Dominates(pred);
    const bool dominated_by_false =
        dominator->GetLastInstruction()->AsIf()->IfFalseSuccessor()->Dominates(pred);
    if (dominated_by_true == dominated_by_false) {
      // In this case, we can't know if we are coming from the true branch, or the false branch. It
      // happens in cases like:
      //      1 (outer if)
      //     / \
      //    2   3 (inner if)
      //    |  / \
      //    | 4  5
      //     \/  |
      //      6  |
      //       \ |
      //         7 (has the same if(cond) as 1)
      //         |
      //         8
      // `7` (which would be `block` in this example), and `6` will come from both the true path and
      // the false path of `1`. We bumped into something similar in `HControlFlowSimplifier`. See
      // `HControlFlowSimplifier::TryFixupDoubleDiamondPattern()`.
      // TODO(solanes): Figure out if we can fix up the graph into a double diamond in a generic way
      // so that `HDeadCodeElimination` and `HControlFlowSimplifier` can take advantage of it.

      if (!same_input) {
        // `1` and `7` having the opposite condition is a case we are missing. We could potentially
        // add a BooleanNot instruction to be able to add the Phi, but it seems like overkill since
        // this case is not that common.
        return;
      }

      // The Phi will have `0`, `1`, and `cond` as inputs. If SimplifyIf redirects 0s and 1s, we
      // will end up with Phi(cond,...,cond) which will be replaced by `cond`. Effectively, we will
      // redirect edges that we are able to redirect and the rest will remain as before (i.e. we
      // won't have an extra Phi).
      new_phi->SetRawInputAt(index, input);
    } else {
      // Redirect to either the true branch (1), or the false branch (0).
      // Given that `dominated_by_true` is the exact opposite of `dominated_by_false`,
      // `(same_input && dominated_by_true) || (!same_input && dominated_by_false)` is equivalent to
      // `same_input == dominated_by_true`.
      new_phi->SetRawInputAt(
          index,
          same_input == dominated_by_true ? graph_->GetIntConstant(1) : graph_->GetIntConstant(0));
    }
  }

  block->AddPhi(new_phi);
  if_instruction->ReplaceInput(new_phi, 0);

  // Remove the old input now, if possible. This allows the branch redirection in SimplifyIf to
  // work without waiting for another pass of DCE.
  if (input->IsDeadAndRemovable()) {
    DCHECK(!same_input)
        << " if both blocks have the same condition, it shouldn't be dead and removable since the "
        << "dominator block's If instruction would be using that condition.";
    input->GetBlock()->RemoveInstruction(input);
  }
  MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyIfAddedPhi);
}

void HDeadCodeElimination::ConnectSuccessiveBlocks() {
  // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
  for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
    HBasicBlock* block  = graph_->GetReversePostOrder()[i];
    DCHECK(!graph_->IsEntryBlock(block));
    while (block->GetLastInstruction()->IsGoto()) {
      HBasicBlock* successor = block->GetSingleSuccessor();
      if (graph_->IsExitBlock(successor) || successor->GetPredecessors().size() != 1u) {
        break;
      }
      DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
      block->MergeWith(successor);
      --size;
      DCHECK_EQ(size, graph_->GetReversePostOrder().size());
      DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
      // Reiterate on this block in case it can be merged with its new successor.
    }
  }
}

struct HDeadCodeElimination::TryBelongingInformation {
  TryBelongingInformation(HGraph* graph, ScopedArenaAllocator* allocator)
      : blocks_in_try(ArenaBitVector::CreateFixedSize(
            allocator, graph->GetBlocks().size(), kArenaAllocDCE)),
        coalesced_try_entries(ArenaBitVector::CreateFixedSize(
            allocator, graph->GetBlocks().size(), kArenaAllocDCE)) {}

  // Which blocks belong in the try.
  BitVectorView<size_t> blocks_in_try;
  // Which other try entries are referencing this same try.
  BitVectorView<size_t> coalesced_try_entries;
};

bool HDeadCodeElimination::CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info) {
  const ArenaVector<HBasicBlock*>& blocks = graph_->GetBlocks();
  for (uint32_t i : try_belonging_info.blocks_in_try.Indexes()) {
    for (HInstructionIteratorPrefetchNext it(blocks[i]->GetInstructions()); !it.Done();
         it.Advance()) {
      if (it.Current()->CanThrow()) {
        return false;
      }
    }
  }
  return true;
}

void HDeadCodeElimination::DisconnectHandlersAndUpdateTryBoundary(
    HBasicBlock* block,
    /* out */ bool* any_block_in_loop) {
  if (block->IsInLoop()) {
    *any_block_in_loop = true;
  }

  // Disconnect the handlers.
  while (block->GetSuccessors().size() > 1) {
    HBasicBlock* handler = block->GetSuccessors()[1];
    DCHECK(handler->IsCatchBlock());
    block->RemoveSuccessor(handler);
    handler->RemovePredecessor(block);
    if (handler->IsInLoop()) {
      *any_block_in_loop = true;
    }
  }

  // Change TryBoundary to Goto.
  DCHECK(block->EndsWithTryBoundary());
  HInstruction* last = block->GetLastInstruction();
  block->RemoveInstruction(last);
  block->AddInstruction(new (graph_->GetAllocator()) HGoto(last->GetDexPc()));
  DCHECK_EQ(block->GetSuccessors().size(), 1u);
}

void HDeadCodeElimination::RemoveTry(HBasicBlock* try_entry,
                                     const TryBelongingInformation& try_belonging_info,
                                     /* out */ bool* any_block_in_loop) {
  // Update all try entries.
  DCHECK(try_entry->EndsWithTryBoundary());
  DCHECK(try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
  DisconnectHandlersAndUpdateTryBoundary(try_entry, any_block_in_loop);

  const ArenaVector<HBasicBlock*>& blocks = graph_->GetBlocks();
  for (uint32_t i : try_belonging_info.coalesced_try_entries.Indexes()) {
    HBasicBlock* other_try_entry = blocks[i];
    DCHECK(other_try_entry->EndsWithTryBoundary());
    DCHECK(other_try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
    DisconnectHandlersAndUpdateTryBoundary(other_try_entry, any_block_in_loop);
  }

  // Update the blocks in the try.
  for (uint32_t i : try_belonging_info.blocks_in_try.Indexes()) {
    HBasicBlock* block = blocks[i];
    // Update the try catch information since now the try doesn't exist.
    block->SetTryCatchInformation(nullptr);
    if (block->IsInLoop()) {
      *any_block_in_loop = true;
    }

    if (block->EndsWithTryBoundary()) {
      // Try exits.
      DCHECK(!block->GetLastInstruction()->AsTryBoundary()->IsEntry());
      DisconnectHandlersAndUpdateTryBoundary(block, any_block_in_loop);

      if (graph_->IsExitBlock(block->GetSingleSuccessor())) {
        // `block` used to be a single exit TryBoundary that got turned into a Goto. It
        // is now pointing to the exit which we don't allow. To fix it, we disconnect
        // `block` from its predecessor and RemoveDeadBlocks will remove it from the
        // graph.
        DCHECK(block->IsSingleGoto());
        HBasicBlock* predecessor = block->GetSinglePredecessor();
        predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());

        if (!block->GetDominatedBlocks().empty()) {
          // Update domination tree if `block` dominates a block to keep the graph consistent.
          DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
          DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
          predecessor->AddDominatedBlock(graph_->GetExitBlock());
          graph_->GetExitBlock()->SetDominator(predecessor);
          block->RemoveDominatedBlock(graph_->GetExitBlock());
        }
      }
    }
  }
}

bool HDeadCodeElimination::RemoveUnneededTries() {
  if (!graph_->HasTryCatch()) {
    return false;
  }

  // Use local allocator for allocating memory.
  ScopedArenaAllocator allocator(graph_->GetArenaStack());

  struct HBasicBlockIdComparator {
    bool operator()(const HBasicBlock* a, const HBasicBlock* b) const {
      DCHECK(a != nullptr);
      DCHECK(b != nullptr);
      return a->GetBlockId() < b->GetBlockId();
    }
  };

  // Collect which blocks are part of which try.
  ScopedArenaSafeMap<HBasicBlock*, TryBelongingInformation, HBasicBlockIdComparator> tries(
      allocator.Adapter(kArenaAllocDCE));
  for (HBasicBlock* block : graph_->GetReversePostOrderSkipEntryBlock()) {
    if (block->IsTryBlock()) {
      HBasicBlock* key = block->GetTryCatchInformation()->GetTryEntry().GetBlock();
      auto it = tries.find(key);
      if (it == tries.end()) {
        it = tries.insert({key, TryBelongingInformation(graph_, &allocator)}).first;
      }
      it->second.blocks_in_try.SetBit(block->GetBlockId());
    }
  }

  // Deduplicate the tries which have different try entries but they are really the same try.
  // We store the surviving keys of `tries` to guarantee consistency when eliminating them below.
  BitVectorView<size_t> keys =
      ArenaBitVector::CreateFixedSize(&allocator, graph_->GetBlocks().size(), kArenaAllocDCE);
  for (auto it = tries.begin(); it != tries.end(); it++) {
    HBasicBlock* block = it->first;
    DCHECK(block->EndsWithTryBoundary());
    HTryBoundary* try_boundary = block->GetLastInstruction()->AsTryBoundary();
    for (auto other_it = next(it); other_it != tries.end(); /*other_it++ in the loop*/) {
      HBasicBlock* other_block = other_it->first;
      DCHECK(other_block->EndsWithTryBoundary());
      HTryBoundary* other_try_boundary = other_block->GetLastInstruction()->AsTryBoundary();
      if (try_boundary->HasSameExceptionHandlersAs(*other_try_boundary)) {
        // Merge the entries as they are really the same one.
        // Block merging.
        it->second.blocks_in_try.Union(other_it->second.blocks_in_try);

        // Add the coalesced try entry to update it too.
        it->second.coalesced_try_entries.SetBit(other_block->GetBlockId());

        // Erase the other entry.
        other_it = tries.erase(other_it);
      } else {
        other_it++;
      }
    }
    keys.SetBit(block->GetBlockId());
  }

  size_t removed_tries = 0;
  bool any_block_in_loop = false;

  // Check which tries contain throwing instructions. Iterate in block id order to guarantee
  // consistency.
  for (size_t id : keys.Indexes()) {
    auto entry = tries.find(graph_->GetBlocks()[id]);
    DCHECK(entry != tries.end());
    if (CanPerformTryRemoval(entry->second)) {
      ++removed_tries;
      RemoveTry(entry->first, entry->second, &any_block_in_loop);
    }
  }

  if (removed_tries != 0) {
    // We want to:
    //   1) Update the dominance information
    //   2) Remove catch block subtrees, if they are now unreachable.
    // If we run the dominance recomputation without removing the code, those catch blocks will
    // not be part of the post order and won't be removed. If we don't run the dominance
    // recomputation, we risk RemoveDeadBlocks not running it and leaving the graph in an
    // inconsistent state. So, what we can do is run RemoveDeadBlocks and force a recomputation.
    // Note that we are not guaranteed to remove a catch block if we have nested try blocks:
    //
    //   try {
    //     ... nothing can throw. TryBoundary A ...
    //     try {
    //       ... can throw. TryBoundary B...
    //     } catch (Error e) {}
    //   } catch (Exception e) {}
    //
    // In the example above, we can remove the TryBoundary A but the Exception catch cannot be
    // removed as the TryBoundary B might still throw into that catch. TryBoundary A and B don't get
    // coalesced since they have different catch handlers.

    RemoveDeadBlocks(/* force_recomputation= */ true, any_block_in_loop);
    MaybeRecordStat(stats_, MethodCompilationStat::kRemovedTry, removed_tries);
    return true;
  } else {
    return false;
  }
}

bool HDeadCodeElimination::RemoveEmptyIfs() {
  bool did_opt = false;
  for (HBasicBlock* block : graph_->GetPostOrder()) {
    if (!block->EndsWithIf()) {
      continue;
    }

    HIf* if_instr = block->GetLastInstruction()->AsIf();
    HBasicBlock* true_block = if_instr->IfTrueSuccessor();
    HBasicBlock* false_block = if_instr->IfFalseSuccessor();

    // We can use `visited_blocks` to detect cases like
    //    1
    //   / \
    //  2  3
    //  \ /
    //   4  ...
    //   | /
    //   5
    // where 2, 3, and 4 are single HGoto blocks, and block 5 has Phis.
    ScopedArenaAllocator allocator(graph_->GetArenaStack());
    BitVectorView<size_t> visited_blocks =
        ArenaBitVector::CreateFixedSize(&allocator, graph_->GetBlocks().size(), kArenaAllocDCE);
    HBasicBlock* merge_true = true_block;
    visited_blocks.SetBit(merge_true->GetBlockId());
    while (merge_true->IsSingleGoto()) {
      merge_true = merge_true->GetSuccessors()[0];
      visited_blocks.SetBit(merge_true->GetBlockId());
    }

    HBasicBlock* merge_false = false_block;
    while (!visited_blocks.IsBitSet(merge_false->GetBlockId()) && merge_false->IsSingleGoto()) {
      merge_false = merge_false->GetSuccessors()[0];
    }

    if (!visited_blocks.IsBitSet(merge_false->GetBlockId()) || !merge_false->GetPhis().IsEmpty()) {
      // TODO(solanes): We could allow Phis iff both branches have the same value for all Phis. This
      // may not be covered by SsaRedundantPhiElimination in cases like `HPhi[A,A,B]` where the Phi
      // itself is not redundant for the general case but it is for a pair of branches.
      continue;
    }

    // Data structures to help remove now-dead instructions.
    ScopedArenaQueue<HInstruction*> maybe_remove(allocator.Adapter(kArenaAllocDCE));
    BitVectorView<size_t> visited = ArenaBitVector::CreateFixedSize(
        &allocator, graph_->GetCurrentInstructionId(), kArenaAllocDCE);
    maybe_remove.push(if_instr->InputAt(0));
    visited.SetBit(if_instr->GetId());

    // Swap HIf with HGoto
    block->ReplaceAndRemoveInstructionWith(
        if_instr, new (graph_->GetAllocator()) HGoto(if_instr->GetDexPc()));

    // Reconnect blocks
    block->RemoveSuccessor(true_block);
    block->RemoveSuccessor(false_block);
    true_block->RemovePredecessor(block);
    false_block->RemovePredecessor(block);
    block->AddSuccessor(merge_false);

    // Remove now dead instructions e.g. comparisons that are only used as input to the if
    // instruction. This can allow for further removal of other empty ifs.
    while (!maybe_remove.empty()) {
      HInstruction* instr = maybe_remove.front();
      maybe_remove.pop();
      if (instr->IsDeadAndRemovable()) {
        for (HInstruction* input : instr->GetInputs()) {
          if (visited.IsBitSet(input->GetId())) {
            continue;
          }
          visited.SetBit(input->GetId());
          maybe_remove.push(input);
        }
        instr->GetBlock()->RemoveInstructionOrPhi(instr);
        MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadInstruction);
      }
    }

    did_opt = true;
  }

  if (did_opt) {
    graph_->RecomputeDominatorTree();
  }

  return did_opt;
}

bool HDeadCodeElimination::RemoveDeadBlocks(bool force_recomputation,
                                            bool force_loop_recomputation) {
  DCHECK_IMPLIES(force_loop_recomputation, force_recomputation);

  // Use local allocator for allocating memory.
  ScopedArenaAllocator allocator(graph_->GetArenaStack());

  // Classify blocks as reachable/unreachable.
  BitVectorView<size_t> live_blocks =
      ArenaBitVector::CreateFixedSize(&allocator, graph_->GetBlocks().size(), kArenaAllocDCE);

  MarkReachableBlocks(graph_, live_blocks);
  bool removed_one_or_more_blocks = false;
  bool rerun_dominance_and_loop_analysis = false;

  // Remove all dead blocks. Iterate in post order because removal needs the
  // block's chain of dominators and nested loops need to be updated from the
  // inside out.
  for (HBasicBlock* block : graph_->GetPostOrder()) {
    int id = block->GetBlockId();
    if (!live_blocks.IsBitSet(id)) {
      MaybeRecordDeadBlock(block);
      block->DisconnectAndDelete();
      removed_one_or_more_blocks = true;
      if (block->IsInLoop()) {
        rerun_dominance_and_loop_analysis = true;
      }
    }
  }

  // If we removed at least one block, we need to recompute the full
  // dominator tree and try block membership.
  if (removed_one_or_more_blocks || force_recomputation) {
    if (rerun_dominance_and_loop_analysis || force_loop_recomputation) {
      graph_->RecomputeDominatorTree();
    } else {
      graph_->ClearDominanceInformation();
      graph_->ComputeDominanceInformation();
      graph_->ComputeTryBlockInformation();
    }
  }
  return removed_one_or_more_blocks;
}

void HDeadCodeElimination::RemoveDeadInstructions() {
  // Process basic blocks in post-order in the dominator tree, so that
  // a dead instruction depending on another dead instruction is removed.
  for (HBasicBlock* block : graph_->GetPostOrder()) {
    // Traverse this block's instructions in backward order and remove
    // the unused ones.
    HBackwardInstructionIteratorPrefetchNext i(block->GetInstructions());
    // Skip the first iteration, as the last instruction of a block is
    // a branching instruction.
    DCHECK(i.Current()->IsControlFlow());
    for (i.Advance(); !i.Done(); i.Advance()) {
      HInstruction* inst = i.Current();
      DCHECK(!inst->IsControlFlow());
      if (inst->IsDeadAndRemovable()) {
        block->RemoveInstruction(inst);
        MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadInstruction);
      }
    }

    // Same for Phis.
    for (HBackwardInstructionIteratorPrefetchNext phi_it(block->GetPhis()); !phi_it.Done();
         phi_it.Advance()) {
      DCHECK(phi_it.Current()->IsPhi());
      HPhi* phi = phi_it.Current()->AsPhi();
      if (phi->IsPhiDeadAndRemovable()) {
        block->RemovePhi(phi);
        MaybeRecordStat(stats_, MethodCompilationStat::kRemovedDeadPhi);
      }
    }
  }
}

void HDeadCodeElimination::UpdateGraphFlags() {
  bool has_monitor_operations = false;
  bool has_traditional_simd = false;
  bool has_predicated_simd = false;
  bool has_bounds_checks = false;
  bool has_always_throwing_invokes = false;

  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
    for (HInstructionIteratorPrefetchNext it(block->GetInstructions()); !it.Done(); it.Advance()) {
      HInstruction* instruction = it.Current();
      if (instruction->IsMonitorOperation()) {
        has_monitor_operations = true;
      } else if (instruction->IsVecOperation()) {
        HVecOperation* vec_instruction = instruction->AsVecOperation();
        if (vec_instruction->IsPredicated()) {
          has_predicated_simd = true;
        } else {
          has_traditional_simd = true;
        }
      } else if (instruction->IsBoundsCheck()) {
        has_bounds_checks = true;
      } else if (instruction->IsInvoke() && instruction->AsInvoke()->AlwaysThrows()) {
        has_always_throwing_invokes = true;
      }
    }
  }

  graph_->SetHasMonitorOperations(has_monitor_operations);
  graph_->SetHasTraditionalSIMD(has_traditional_simd);
  graph_->SetHasPredicatedSIMD(has_predicated_simd);
  graph_->SetHasBoundsChecks(has_bounds_checks);
  graph_->SetHasAlwaysThrowingInvokes(has_always_throwing_invokes);
}

bool HDeadCodeElimination::Run() {
  // Do not eliminate dead blocks if the graph has irreducible loops. We could
  // support it, but that would require changes in our loop representation to handle
  // multiple entry points. We decided it was not worth the complexity.
  if (!graph_->HasIrreducibleLoops()) {
    // Simplify graph to generate more dead block patterns.
    ConnectSuccessiveBlocks();
    bool did_any_simplification = false;
    did_any_simplification |= SimplifyAlwaysThrows();
    did_any_simplification |= SimplifyIfs();
    did_any_simplification |= RemoveEmptyIfs();
    did_any_simplification |= RemoveDeadBlocks();
    // We call RemoveDeadBlocks before RemoveUnneededTries to remove the dead blocks from the
    // previous optimizations. Otherwise, we might detect that a try has throwing instructions but
    // they are actually dead code. RemoveUnneededTryBoundary will call RemoveDeadBlocks again if
    // needed.
    did_any_simplification |= RemoveUnneededTries();
    if (did_any_simplification) {
      // Connect successive blocks created by dead branches.
      ConnectSuccessiveBlocks();
    }
  }
  SsaRedundantPhiElimination(graph_).Run();
  RemoveDeadInstructions();
  UpdateGraphFlags();
  return true;
}

}  // namespace art

Messung V0.5 in Prozent
C=85 H=90 G=87

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik