staticvoid MarkReachableBlocks(HGraph* graph, BitVectorView<size_t> visited) { // Use local allocator for allocating memory.
ScopedArenaAllocator allocator(graph->GetArenaStack());
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());
}
}
} elseif (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);
}
}
}
}
staticbool HasEquality(IfCondition condition) { switch (condition) { case kCondEQ: case kCondLE: case kCondGE: case kCondBE: case kCondAE: returntrue; case kCondNE: case kCondLT: case kCondGT: case kCondB: case kCondA: returnfalse;
}
}
staticbool RemoveNonNullControlDependences(HBasicBlock* block, HBasicBlock* throws) { // Test for an if as last statement. if (!block->EndsWithIf()) { returnfalse;
}
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();
} elseif (throws == ifs->IfFalseSuccessor() && cond->IsNotEqual()) {
not_throws = ifs->IfTrueSuccessor();
} else { returnfalse;
}
DCHECK(cond->IsEqual() || cond->IsNotEqual());
HInstruction* obj = cond->InputAt(1); if (obj->IsNullConstant()) {
obj = cond->InputAt(0);
} elseif (!cond->InputAt(0)->IsNullConstant()) { returnfalse;
}
// We can't create a BoundType for an object with an invalid RTI. const ReferenceTypeInfo ti = obj->GetReferenceTypeInfo(); if (!ti.IsValid()) { returnfalse;
}
// 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) { returnfalse;
}
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(); returntrue;
} returnfalse;
}
// 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();
// 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); constbool 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;
}
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);
}
}
for (size_t index = 0; index < pred_size; index++) {
HBasicBlock* pred = block->GetPredecessors()[index]; constbool dominated_by_true =
dominator->GetLastInstruction()->AsIf()->IfTrueSuccessor()->Dominates(pred); constbool 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));
}
}
// 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.
}
}
}
// 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()) { returnfalse;
}
}
} returntrue;
}
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);
}
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()) { returnfalse;
}
// Use local allocator for allocating memory.
ScopedArenaAllocator allocator(graph_->GetArenaStack());
// 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());
}
// 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.
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()));
// 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();
}
// 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);
}
}
}
}
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(); returntrue;
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-29)
¤
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.