}
}
+static void MarkLoopHeadersContaining(const HBasicBlock& block, ArenaBitVector* set) {
+ for (HLoopInformationOutwardIterator it(block); !it.Done(); it.Advance()) {
+ set->SetBit(it.Current()->GetHeader()->GetBlockId());
+ }
+}
+
void HDeadCodeElimination::MaybeRecordDeadBlock(HBasicBlock* block) {
if (stats_ != nullptr) {
stats_->RecordStat(MethodCompilationStat::kRemovedDeadInstruction,
// Classify blocks as reachable/unreachable.
ArenaAllocator* allocator = graph_->GetArena();
ArenaBitVector live_blocks(allocator, graph_->GetBlocks().Size(), false);
+ ArenaBitVector affected_loops(allocator, graph_->GetBlocks().Size(), false);
+
MarkReachableBlocks(graph_->GetEntryBlock(), &live_blocks);
- // Remove all dead blocks. Process blocks in post-order, because removal needs
- // the block's chain of dominators.
+ // 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 (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
- if (live_blocks.IsBitSet(block->GetBlockId())) {
- // If this block is part of a loop that is being dismantled, we need to
- // update its loop information.
- block->UpdateLoopInformation();
+ int id = block->GetBlockId();
+ if (live_blocks.IsBitSet(id)) {
+ if (affected_loops.IsBitSet(id)) {
+ DCHECK(block->IsLoopHeader());
+ block->GetLoopInformation()->Update();
+ }
} else {
MaybeRecordDeadBlock(block);
+ MarkLoopHeadersContaining(*block, &affected_loops);
block->DisconnectAndDelete();
}
}
#include "nodes.h"
#include "ssa_builder.h"
+#include "base/bit_vector-inl.h"
#include "utils/growable_array.h"
#include "scoped_thread_state_change.h"
}
bool HLoopInformation::Populate() {
+ DCHECK_EQ(blocks_.NumSetBits(), 0u) << "Loop information has already been populated";
for (size_t i = 0, e = GetBackEdges().Size(); i < e; ++i) {
HBasicBlock* back_edge = GetBackEdges().Get(i);
DCHECK(back_edge->GetDominator() != nullptr);
return true;
}
+void HLoopInformation::Update() {
+ HGraph* graph = header_->GetGraph();
+ for (uint32_t id : blocks_.Indexes()) {
+ HBasicBlock* block = graph->GetBlocks().Get(id);
+ // Reset loop information of non-header blocks inside the loop, except
+ // members of inner nested loops because those should already have been
+ // updated by their own LoopInformation.
+ if (block->GetLoopInformation() == this && block != header_) {
+ block->SetLoopInformation(nullptr);
+ }
+ }
+ blocks_.ClearAllBits();
+
+ if (back_edges_.IsEmpty()) {
+ // The loop has been dismantled, delete its suspend check and remove info
+ // from the header.
+ DCHECK(HasSuspendCheck());
+ header_->RemoveInstruction(suspend_check_);
+ header_->SetLoopInformation(nullptr);
+ header_ = nullptr;
+ suspend_check_ = nullptr;
+ } else {
+ if (kIsDebugBuild) {
+ for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) {
+ DCHECK(header_->Dominates(back_edges_.Get(i)));
+ }
+ }
+ // This loop still has reachable back edges. Repopulate the list of blocks.
+ bool populate_successful = Populate();
+ DCHECK(populate_successful);
+ }
+}
+
HBasicBlock* HLoopInformation::GetPreHeader() const {
return header_->GetDominator();
}
SetGraph(nullptr);
}
-void HBasicBlock::UpdateLoopInformation() {
- // Check if loop information points to a dismantled loop. If so, replace with
- // the loop information of a larger loop which contains this block, or nullptr
- // otherwise. We iterate in case the larger loop has been destroyed too.
- while (IsInLoop() && loop_information_->GetBackEdges().IsEmpty()) {
- if (IsLoopHeader()) {
- HSuspendCheck* suspend_check = loop_information_->GetSuspendCheck();
- DCHECK_EQ(suspend_check->GetBlock(), this);
- RemoveInstruction(suspend_check);
- }
- loop_information_ = loop_information_->GetPreHeader()->GetLoopInformation();
- }
-}
-
void HBasicBlock::MergeWith(HBasicBlock* other) {
DCHECK_EQ(GetGraph(), other->GetGraph());
DCHECK(GetDominatedBlocks().Contains(other));
--- /dev/null
+# Copyright (C) 2015 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.method public static $inline$True()Z
+ .registers 1
+ const/4 v0, 1
+ return v0
+.end method
+
+
+# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (before)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
+# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (after)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[AddX:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[AddX]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+.method public static testSingleExit(IZ)I
+ .registers 3
+
+ # p0 = int X
+ # p1 = boolean Y
+ # v0 = true
+
+ invoke-static {}, LTestCase;->$inline$True()Z
+ move-result v0
+
+ :loop_start
+ if-eqz p1, :loop_body # cannot be determined statically
+ if-nez v0, :loop_end # will always exit
+
+ # Dead block
+ add-int/lit8 p0, p0, 5
+ goto :loop_start
+
+ # Live block
+ :loop_body
+ add-int/lit8 p0, p0, 7
+ goto :loop_start
+
+ :loop_end
+ return p0
+.end method
+
+
+# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (before)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
+# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgZ]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (after)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgZ]] ] loop_header:null
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+.method public static testMultipleExits(IZZ)I
+ .registers 4
+
+ # p0 = int X
+ # p1 = boolean Y
+ # p2 = boolean Z
+ # v0 = true
+
+ invoke-static {}, LTestCase;->$inline$True()Z
+ move-result v0
+
+ :loop_start
+ if-eqz p1, :loop_body # cannot be determined statically
+ if-nez p2, :loop_end # may exit
+ if-nez v0, :loop_end # will always exit
+
+ # Dead block
+ add-int/lit8 p0, p0, 5
+ goto :loop_start
+
+ # Live block
+ :loop_body
+ add-int/lit8 p0, p0, 7
+ goto :loop_start
+
+ :loop_end
+ return p0
+.end method
+
+
+# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (before)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
+# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[Cst9:i\d+]] IntConstant 9
+# CHECK-DAG: [[PhiX1:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgZ]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Mul9:i\d+]] Mul [ [[PhiX1]] [[Cst9]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[PhiX2:i\d+]] Phi [ [[Mul9]] [[PhiX1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add5]] Add [ [[PhiX2]] [[Cst5]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX1]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: Return [ [[PhiX2]] ] loop_header:null
+
+# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: [[Cst9:i\d+]] IntConstant 9
+# CHECK-DAG: [[PhiX1:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX1]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgZ]] ] loop_header:null
+# CHECK-DAG: [[Mul9:i\d+]] Mul [ [[PhiX1]] [[Cst9]] ] loop_header:null
+# CHECK-DAG: [[PhiX2:i\d+]] Phi [ [[Mul9]] [[PhiX1]] ] loop_header:null
+# CHECK-DAG: Return [ [[PhiX2]] ] loop_header:null
+
+.method public static testExitPredecessors(IZZ)I
+ .registers 4
+
+ # p0 = int X
+ # p1 = boolean Y
+ # p2 = boolean Z
+ # v0 = true
+
+ invoke-static {}, LTestCase;->$inline$True()Z
+ move-result v0
+
+ :loop_start
+ if-eqz p1, :loop_body # cannot be determined statically
+
+ # Additional logic which will end up outside the loop
+ if-eqz p2, :skip_if
+ mul-int/lit8 p0, p0, 9
+ :skip_if
+
+ if-nez v0, :loop_end # will always take the branch
+
+ # Dead block
+ add-int/lit8 p0, p0, 5
+ goto :loop_start
+
+ # Live block
+ :loop_body
+ add-int/lit8 p0, p0, 7
+ goto :loop_start
+
+ :loop_end
+ return p0
+.end method
+
+
+# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (before)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst0:i\d+]] IntConstant 0
+# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
+# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+#
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: [[PhiZ1:i\d+]] Phi [ [[ArgZ]] [[XorZ:i\d+]] [[PhiZ1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+#
+# ### Inner loop ###
+# CHECK-DAG: [[PhiZ2:i\d+]] Phi [ [[PhiZ1]] [[XorZ]] ] loop_header:[[HeaderZ:B\d+]]
+# CHECK-DAG: [[XorZ]] Xor [ [[PhiZ2]] [[Cst1]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: [[CondZ:z\d+]] Equal [ [[XorZ]] [[Cst0]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: If [ [[CondZ]] ] loop_header:[[HeaderZ]]
+#
+# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (after)
+# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
+# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
+# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
+# CHECK-DAG: [[Cst0:i\d+]] IntConstant 0
+# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
+# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+#
+# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
+# CHECK-DAG: [[PhiZ1:i\d+]] Phi [ [[ArgZ]] [[PhiZ1]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+#
+# ### Inner loop ###
+# CHECK-DAG: [[PhiZ2:i\d+]] Phi [ [[PhiZ1]] [[XorZ:i\d+]] ] loop_header:[[HeaderZ:B\d+]]
+# CHECK-DAG: [[XorZ]] Xor [ [[PhiZ2]] [[Cst1]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: [[CondZ:z\d+]] Equal [ [[XorZ]] [[Cst0]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: If [ [[CondZ]] ] loop_header:[[HeaderZ]]
+#
+# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+
+.method public static testInnerLoop(IZZ)I
+ .registers 4
+
+ # p0 = int X
+ # p1 = boolean Y
+ # p2 = boolean Z
+ # v0 = true
+
+ invoke-static {}, LTestCase;->$inline$True()Z
+ move-result v0
+
+ :loop_start
+ if-eqz p1, :loop_body # cannot be determined statically
+
+ # Inner loop which will end up outside its parent
+ :inner_loop_start
+ xor-int/lit8 p2, p2, 1
+ if-eqz p2, :inner_loop_start
+
+ if-nez v0, :loop_end # will always take the branch
+
+ # Dead block
+ add-int/lit8 p0, p0, 5
+ goto :loop_start
+
+ # Live block
+ :loop_body
+ add-int/lit8 p0, p0, 7
+ goto :loop_start
+
+ :loop_end
+ return p0
+.end method