summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2014-11-27 12:01:59 +0000
committer Nicolas Geoffray <ngeoffray@google.com> 2014-11-28 11:05:22 +0000
commitdbca6fae9d09160f45bf8d3512f15cdd9558975b (patch)
tree4e06651a63b661610f2f12654600c9146b0d3607
parent35ecc8ca8fba713728b8fc60e9e2a275da2028aa (diff)
Fix a bug in GVN.
When a predecessor block was killing instructions in a set, we were not taking into account side effects of blocks between the dominator to this predecessor. Implementation now intersects the copied set of the dominator with the predecessors to take these side effects into account. Change-Id: If297439cc4e50cee91e9fffd028216a3e49e19ef
-rw-r--r--compiler/optimizing/gvn.cc98
-rw-r--r--compiler/optimizing/gvn.h72
-rw-r--r--test/433-gvn/expected.txt1
-rw-r--r--test/433-gvn/info.txt3
-rw-r--r--test/433-gvn/src/Main.java38
5 files changed, 132 insertions, 80 deletions
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 25168b5b0c..6e5f1bd203 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -91,29 +91,38 @@ SideEffects GlobalValueNumberer::GetBlockEffects(HBasicBlock* block) const {
return block_effects_.Get(block->GetBlockId());
}
-static bool IsLoopExit(HBasicBlock* block, HBasicBlock* successor) {
- HLoopInformation* block_info = block->GetLoopInformation();
- HLoopInformation* other_info = successor->GetLoopInformation();
- return block_info != other_info && (other_info == nullptr || block_info->IsIn(*other_info));
-}
-
void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
- if (kIsDebugBuild) {
- // Check that all non back-edge processors have been visited.
- for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
- HBasicBlock* predecessor = block->GetPredecessors().Get(i);
- DCHECK(visited_.Get(predecessor->GetBlockId())
- || (block->GetLoopInformation() != nullptr
- && (block->GetLoopInformation()->GetBackEdges().Get(0) == predecessor)));
+ ValueSet* set = nullptr;
+ const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
+ if (predecessors.Size() == 0 || predecessors.Get(0)->IsEntryBlock()) {
+ // The entry block should only accumulate constant instructions, and
+ // the builder puts constants only in the entry block.
+ // Therefore, there is no need to propagate the value set to the next block.
+ set = new (allocator_) ValueSet(allocator_);
+ } else {
+ HBasicBlock* dominator = block->GetDominator();
+ set = sets_.Get(dominator->GetBlockId())->Copy();
+ if (dominator->GetSuccessors().Size() != 1 || dominator->GetSuccessors().Get(0) != block) {
+ // We have to copy if the dominator has other successors, or `block` is not a successor
+ // of the dominator.
+ set = set->Copy();
+ }
+ if (!set->IsEmpty()) {
+ if (block->IsLoopHeader()) {
+ DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
+ set->Kill(GetLoopEffects(block));
+ } else if (predecessors.Size() > 1) {
+ for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
+ set->IntersectionWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
+ if (set->IsEmpty()) {
+ break;
+ }
+ }
+ }
}
- visited_.Put(block->GetBlockId(), true);
}
- ValueSet* set = sets_.Get(block->GetBlockId());
-
- if (block->IsLoopHeader()) {
- set->Kill(GetLoopEffects(block));
- }
+ sets_.Put(block->GetBlockId(), set);
HInstruction* current = block->GetFirstInstruction();
while (current != nullptr) {
@@ -131,57 +140,6 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
}
current = next;
}
-
- if (block == graph_->GetEntryBlock()) {
- // The entry block should only accumulate constant instructions, and
- // the builder puts constants only in the entry block.
- // Therefore, there is no need to propagate the value set to the next block.
- DCHECK_EQ(block->GetDominatedBlocks().Size(), 1u);
- HBasicBlock* dominated = block->GetDominatedBlocks().Get(0);
- sets_.Put(dominated->GetBlockId(), new (allocator_) ValueSet(allocator_));
- return;
- }
-
- // Copy the value set to dominated blocks. We can re-use
- // the current set for the last dominated block because we are done visiting
- // this block.
- for (size_t i = 0, e = block->GetDominatedBlocks().Size(); i < e; ++i) {
- HBasicBlock* dominated = block->GetDominatedBlocks().Get(i);
- sets_.Put(dominated->GetBlockId(), i == e - 1 ? set : set->Copy());
- }
-
- // Kill instructions in the value set of each successor. If the successor
- // is a loop exit, then we use the side effects of the loop. If not, we use
- // the side effects of this block.
- for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
- HBasicBlock* successor = block->GetSuccessors().Get(i);
- if (successor->IsLoopHeader()
- && successor->GetLoopInformation()->GetBackEdges().Get(0) == block) {
- // In case of a back edge, we already have visited the loop header.
- // We should not update its value set, because the last dominated block
- // of the loop header uses the same value set.
- DCHECK(visited_.Get(successor->GetBlockId()));
- continue;
- }
- DCHECK(!visited_.Get(successor->GetBlockId()));
- ValueSet* successor_set = sets_.Get(successor->GetBlockId());
- // The dominator sets the set, and we are guaranteed to have visited it already.
- DCHECK(successor_set != nullptr);
-
- // If this block dominates this successor there is nothing to do.
- // Also if the set is empty, there is nothing to kill.
- if (successor->GetDominator() != block && !successor_set->IsEmpty()) {
- if (block->IsInLoop() && IsLoopExit(block, successor)) {
- // All instructions killed in the loop must be killed for a loop exit.
- SideEffects effects = GetLoopEffects(block->GetLoopInformation()->GetHeader());
- sets_.Get(successor->GetBlockId())->Kill(effects);
- } else {
- // Following block (that might be in the same loop).
- // Just kill instructions based on this block's side effects.
- sets_.Get(successor->GetBlockId())->Kill(GetBlockEffects(block));
- }
- }
- }
}
} // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index 8e739cb6d3..81f2c3fa87 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -96,6 +96,26 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> {
return nullptr;
}
+ // Returns whether `instruction` is in the set.
+ HInstruction* IdentityLookup(HInstruction* instruction) const {
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ HInstruction* existing = table_[index];
+ if (existing != nullptr && existing == instruction) {
+ return existing;
+ }
+
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ if (node->GetHashCode() == hash_code) {
+ existing = node->GetInstruction();
+ if (existing == instruction) {
+ return existing;
+ }
+ }
+ }
+ return nullptr;
+ }
+
// Removes all instructions in the set that are affected by the given side effects.
void Kill(SideEffects side_effects) {
for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
@@ -106,9 +126,9 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> {
}
}
- ValueSetNode* current = collisions_;
- ValueSetNode* previous = nullptr;
- while (current != nullptr) {
+ for (ValueSetNode* current = collisions_, *previous = nullptr;
+ current != nullptr;
+ current = current->GetNext()) {
HInstruction* instruction = current->GetInstruction();
if (instruction->GetSideEffects().DependsOn(side_effects)) {
if (previous == nullptr) {
@@ -120,7 +140,6 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> {
} else {
previous = current;
}
- current = current->GetNext();
}
}
@@ -143,6 +162,44 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> {
return copy;
}
+ void Clear() {
+ number_of_entries_ = 0;
+ collisions_ = nullptr;
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ table_[i] = nullptr;
+ }
+ }
+
+ // Update this `ValueSet` by intersecting with instructions in `other`.
+ void IntersectionWith(ValueSet* other) {
+ if (IsEmpty()) {
+ return;
+ } else if (other->IsEmpty()) {
+ Clear();
+ } else {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
+ --number_of_entries_;
+ table_[i] = nullptr;
+ }
+ }
+ for (ValueSetNode* current = collisions_, *previous = nullptr;
+ current != nullptr;
+ current = current->GetNext()) {
+ if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
+ if (previous == nullptr) {
+ collisions_ = current->GetNext();
+ } else {
+ previous->SetNext(current->GetNext());
+ }
+ --number_of_entries_;
+ } else {
+ previous = current;
+ }
+ }
+ }
+ }
+
bool IsEmpty() const { return number_of_entries_ == 0; }
size_t GetNumberOfEntries() const { return number_of_entries_; }
@@ -173,13 +230,11 @@ class GlobalValueNumberer : public ValueObject {
allocator_(allocator),
block_effects_(allocator, graph->GetBlocks().Size()),
loop_effects_(allocator, graph->GetBlocks().Size()),
- sets_(allocator, graph->GetBlocks().Size()),
- visited_(allocator, graph->GetBlocks().Size()) {
+ sets_(allocator, graph->GetBlocks().Size()) {
size_t number_of_blocks = graph->GetBlocks().Size();
block_effects_.SetSize(number_of_blocks);
loop_effects_.SetSize(number_of_blocks);
sets_.SetSize(number_of_blocks);
- visited_.SetSize(number_of_blocks);
for (size_t i = 0; i < number_of_blocks; ++i) {
block_effects_.Put(i, SideEffects::None());
@@ -219,9 +274,6 @@ class GlobalValueNumberer : public ValueObject {
// in the path from the dominator to the block.
GrowableArray<ValueSet*> sets_;
- // Mark visisted blocks. Only used for debugging.
- GrowableArray<bool> visited_;
-
ART_FRIEND_TEST(GVNTest, LoopSideEffects);
DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
};
diff --git a/test/433-gvn/expected.txt b/test/433-gvn/expected.txt
new file mode 100644
index 0000000000..d81cc0710e
--- /dev/null
+++ b/test/433-gvn/expected.txt
@@ -0,0 +1 @@
+42
diff --git a/test/433-gvn/info.txt b/test/433-gvn/info.txt
new file mode 100644
index 0000000000..bcdab152d3
--- /dev/null
+++ b/test/433-gvn/info.txt
@@ -0,0 +1,3 @@
+Regression test for the optimizing compiler's GVN, that
+used to not take into account all side effects between
+a dominator and its dominated blocks.
diff --git a/test/433-gvn/src/Main.java b/test/433-gvn/src/Main.java
new file mode 100644
index 0000000000..f9cb59434e
--- /dev/null
+++ b/test/433-gvn/src/Main.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println(foo());
+ }
+
+ public static int foo() {
+ Main m = new Main();
+ int a = m.field;
+ if (a == 0) {
+ m.field = 42;
+ if (m.test) {
+ a = 3;
+ }
+ }
+ // The compiler used to GVN this field get with the one line 24,
+ // even though the field is updated in the if.
+ return m.field;
+ }
+
+ public int field;
+ public boolean test = true;
+}