/* * 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. */ #ifndef ART_COMPILER_OPTIMIZING_GVN_H_ #define ART_COMPILER_OPTIMIZING_GVN_H_ #include "nodes.h" #include "optimization.h" namespace art { /** * A node in the collision list of a ValueSet. Encodes the instruction, * the hash code, and the next node in the collision list. */ class ValueSetNode : public ArenaObject { public: ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next) : instruction_(instruction), hash_code_(hash_code), next_(next) {} size_t GetHashCode() const { return hash_code_; } HInstruction* GetInstruction() const { return instruction_; } ValueSetNode* GetNext() const { return next_; } void SetNext(ValueSetNode* node) { next_ = node; } private: HInstruction* const instruction_; const size_t hash_code_; ValueSetNode* next_; DISALLOW_COPY_AND_ASSIGN(ValueSetNode); }; /** * A ValueSet holds instructions that can replace other instructions. It is updated * through the `Add` method, and the `Kill` method. The `Kill` method removes * instructions that are affected by the given side effect. * * The `Lookup` method returns an equivalent instruction to the given instruction * if there is one in the set. In GVN, we would say those instructions have the * same "number". */ class ValueSet : public ArenaObject { public: explicit ValueSet(ArenaAllocator* allocator) : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) { for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) { table_[i] = nullptr; } } // Adds an instruction in the set. void Add(HInstruction* instruction) { DCHECK(Lookup(instruction) == nullptr); size_t hash_code = instruction->ComputeHashCode(); size_t index = hash_code % kDefaultNumberOfEntries; if (table_[index] == nullptr) { table_[index] = instruction; } else { collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_); } ++number_of_entries_; } // If in the set, returns an equivalent instruction to the given instruction. Returns // null otherwise. HInstruction* Lookup(HInstruction* instruction) const { size_t hash_code = instruction->ComputeHashCode(); size_t index = hash_code % kDefaultNumberOfEntries; HInstruction* existing = table_[index]; if (existing != nullptr && existing->Equals(instruction)) { return existing; } for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) { if (node->GetHashCode() == hash_code) { existing = node->GetInstruction(); if (existing->Equals(instruction)) { return existing; } } } 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) { HInstruction* instruction = table_[i]; if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) { table_[i] = nullptr; --number_of_entries_; } } for (ValueSetNode* current = collisions_, *previous = nullptr; current != nullptr; current = current->GetNext()) { HInstruction* instruction = current->GetInstruction(); if (instruction->GetSideEffects().DependsOn(side_effects)) { if (previous == nullptr) { collisions_ = current->GetNext(); } else { previous->SetNext(current->GetNext()); } --number_of_entries_; } else { previous = current; } } } // Returns a copy of this set. ValueSet* Copy() const { ValueSet* copy = new (allocator_) ValueSet(allocator_); for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) { copy->table_[i] = table_[i]; } // Note that the order will be inverted in the copy. This is fine, as the order is not // relevant for a ValueSet. for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) { copy->collisions_ = new (allocator_) ValueSetNode( node->GetInstruction(), node->GetHashCode(), copy->collisions_); } copy->number_of_entries_ = number_of_entries_; 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_; } private: static constexpr size_t kDefaultNumberOfEntries = 8; ArenaAllocator* const allocator_; // The number of entries in the set. size_t number_of_entries_; // The internal implementation of the set. It uses a combination of a hash code based // fixed-size list, and a linked list to handle hash code collisions. // TODO: Tune the fixed size list original size, and support growing it. ValueSetNode* collisions_; HInstruction* table_[kDefaultNumberOfEntries]; DISALLOW_COPY_AND_ASSIGN(ValueSet); }; /** * Optimization phase that removes redundant instruction. */ class GlobalValueNumberer : public ValueObject { public: GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph) : graph_(graph), allocator_(allocator), block_effects_(allocator, graph->GetBlocks().Size()), loop_effects_(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); for (size_t i = 0; i < number_of_blocks; ++i) { block_effects_.Put(i, SideEffects::None()); loop_effects_.Put(i, SideEffects::None()); } } void Run(); private: // Per-block GVN. Will also update the ValueSet of the dominated and // successor blocks. void VisitBasicBlock(HBasicBlock* block); // Compute side effects of individual blocks and loops. The GVN algorithm // will use these side effects to update the ValueSet of individual blocks. void ComputeSideEffects(); void UpdateLoopEffects(HLoopInformation* info, SideEffects effects); SideEffects GetLoopEffects(HBasicBlock* block) const; SideEffects GetBlockEffects(HBasicBlock* block) const; HGraph* graph_; ArenaAllocator* const allocator_; // Side effects of individual blocks, that is the union of the side effects // of the instructions in the block. GrowableArray block_effects_; // Side effects of loops, that is the union of the side effects of the // blocks contained in that loop. GrowableArray loop_effects_; // 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. GrowableArray sets_; ART_FRIEND_TEST(GVNTest, LoopSideEffects); DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; class GVNOptimization : public HOptimization { public: explicit GVNOptimization(HGraph* graph) : HOptimization(graph, true, "GVN") {} void Run() OVERRIDE { GlobalValueNumberer gvn(graph_->GetArena(), graph_); gvn.Run(); } private: DISALLOW_COPY_AND_ASSIGN(GVNOptimization); }; } // namespace art #endif // ART_COMPILER_OPTIMIZING_GVN_H_