summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/gvn.cc84
1 files changed, 51 insertions, 33 deletions
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index d088e02132..a82d6c4325 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -362,9 +362,17 @@ class GlobalValueNumberer : public ValueObject {
allocator_(graph->GetArenaStack()),
side_effects_(side_effects),
sets_(graph->GetBlocks().size(), nullptr, allocator_.Adapter(kArenaAllocGvn)),
+ dominated_to_visit_(graph->GetBlocks().size(), allocator_.Adapter(kArenaAllocGvn)),
+ successors_to_visit_(graph->GetBlocks().size(), allocator_.Adapter(kArenaAllocGvn)),
+ free_sets_(&allocator_, graph->GetBlocks().size(), /* expandable= */ false, kArenaAllocGvn),
visited_blocks_(
&allocator_, graph->GetBlocks().size(), /* expandable= */ false, kArenaAllocGvn),
- did_optimization_(false) {}
+ did_optimization_(false) {
+ for (HBasicBlock* block : graph->GetReversePostOrder()) {
+ dominated_to_visit_[block->GetBlockId()] = block->GetDominatedBlocks().size();
+ successors_to_visit_[block->GetBlockId()] = block->GetSuccessors().size();
+ }
+ }
bool Run();
@@ -387,23 +395,31 @@ class GlobalValueNumberer : public ValueObject {
DCHECK(sets_[block->GetBlockId()] != nullptr)
<< "Block B" << block->GetBlockId() << " expected to have a set";
sets_[block->GetBlockId()] = nullptr;
+ free_sets_.ClearBit(block->GetBlockId());
}
// Returns false if the GlobalValueNumberer has already visited all blocks
// which may reference `block`.
- bool WillBeReferencedAgain(HBasicBlock* block) const;
+ bool WillBeReferencedAgain(uint32_t block_id) const;
// Iterates over visited blocks and finds one which has a ValueSet such that:
// (a) it will not be referenced in the future, and
// (b) it can hold a copy of `reference_set` with a reasonable load factor.
- HBasicBlock* FindVisitedBlockWithRecyclableSet(HBasicBlock* block,
- const ValueSet& reference_set) const;
+ HBasicBlock* FindVisitedBlockWithRecyclableSet(const ValueSet& reference_set) const;
// ValueSet for blocks. Initially null, but for an individual block they
// are allocated and populated by the dominator, and updated by all blocks
// in the path from the dominator to the block.
ScopedArenaVector<ValueSet*> sets_;
+ // Extra bookkeeping to speed up GVN, indexed by block id.
+ // Number of dominated blocks left to visit.
+ ScopedArenaVector<uint32_t> dominated_to_visit_;
+ // Number of successor blocks left to visit.
+ ScopedArenaVector<uint32_t> successors_to_visit_;
+ // True iff the block's ValueSet is free to be reused by another block.
+ ArenaBitVector free_sets_;
+
// BitVector which serves as a fast-access map from block id to
// visited/unvisited Boolean.
ArenaBitVector visited_blocks_;
@@ -449,7 +465,7 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
// Try to find a basic block which will never be referenced again and whose
// ValueSet can therefore be recycled. We will need to copy `dominator_set`
// into the recycled set, so we pass `dominator_set` as a reference for size.
- HBasicBlock* recyclable = FindVisitedBlockWithRecyclableSet(block, *dominator_set);
+ HBasicBlock* recyclable = FindVisitedBlockWithRecyclableSet(*dominator_set);
if (recyclable == nullptr) {
// No block with a suitable ValueSet found. Allocate a new one and
// copy `dominator_set` into it.
@@ -528,54 +544,56 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
}
visited_blocks_.SetBit(block->GetBlockId());
-}
-
-bool GlobalValueNumberer::WillBeReferencedAgain(HBasicBlock* block) const {
- DCHECK(visited_blocks_.IsBitSet(block->GetBlockId()));
- for (const HBasicBlock* dominated_block : block->GetDominatedBlocks()) {
- if (!visited_blocks_.IsBitSet(dominated_block->GetBlockId())) {
- return true;
+ // Bookkeeping to mark ValueSets to be reused. We mark them as free if the dominator /
+ // predecessor will never be referenced again, and if it hasn't been reused already by this
+ // method (See the `dominator->GetSuccessors().size() == 1` case).
+ if (block->GetDominator() != nullptr) {
+ const uint32_t id = block->GetDominator()->GetBlockId();
+ dominated_to_visit_[id]--;
+ if (!WillBeReferencedAgain(id) && sets_[id] != nullptr) {
+ free_sets_.SetBit(id);
}
}
-
- for (const HBasicBlock* successor : block->GetSuccessors()) {
- if (!visited_blocks_.IsBitSet(successor->GetBlockId())) {
- return true;
+ for (HBasicBlock* pred : predecessors) {
+ const uint32_t id = pred->GetBlockId();
+ successors_to_visit_[id]--;
+ if (!WillBeReferencedAgain(id) && sets_[id] != nullptr) {
+ free_sets_.SetBit(id);
}
}
+}
- return false;
+bool GlobalValueNumberer::WillBeReferencedAgain(uint32_t block_id) const {
+ // Block itself hasn't been visited, or
+ // a dominated block has yet to be visited, or
+ // a successor is yet to be visited.
+ return !visited_blocks_.IsBitSet(block_id) ||
+ dominated_to_visit_[block_id] != 0 ||
+ successors_to_visit_[block_id] != 0;
}
HBasicBlock* GlobalValueNumberer::FindVisitedBlockWithRecyclableSet(
- HBasicBlock* block, const ValueSet& reference_set) const {
+ const ValueSet& reference_set) const {
HBasicBlock* secondary_match = nullptr;
- for (size_t block_id : visited_blocks_.Indexes()) {
+ // TODO(solanes): If we keep `free_sets_` sorted by size we could do a binary search instead of a
+ // linear one. Note that while a HashMap<size, free_sets> is better for knowing if there's an
+ // exact match, that data structure is worse for the exact_match=false case.
+ for (size_t block_id : free_sets_.Indexes()) {
+ DCHECK(!WillBeReferencedAgain(block_id));
ValueSet* current_set = sets_[block_id];
- if (current_set == nullptr) {
- // Set was already recycled.
- continue;
- }
-
- HBasicBlock* current_block = block->GetGraph()->GetBlocks()[block_id];
+ DCHECK_NE(current_set, nullptr);
// We test if `current_set` has enough buckets to store a copy of
// `reference_set` with a reasonable load factor. If we find a set whose
// number of buckets matches perfectly, we return right away. If we find one
// that is larger, we return it if no perfectly-matching set is found.
- // Note that we defer testing WillBeReferencedAgain until all other criteria
- // have been satisfied because it might be expensive.
if (current_set->CanHoldCopyOf(reference_set, /* exact_match= */ true)) {
- if (!WillBeReferencedAgain(current_block)) {
- return current_block;
- }
+ return graph_->GetBlocks()[block_id];
} else if (secondary_match == nullptr &&
current_set->CanHoldCopyOf(reference_set, /* exact_match= */ false)) {
- if (!WillBeReferencedAgain(current_block)) {
- secondary_match = current_block;
- }
+ secondary_match = graph_->GetBlocks()[block_id];
}
}