blob: 89bba2d9f656e0722a8a4ca18e15424aee945e40 [file] [log] [blame]
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +01001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "gvn.h"
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000018#include "side_effects_analysis.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010019
20namespace art {
21
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000022/**
23 * A node in the collision list of a ValueSet. Encodes the instruction,
24 * the hash code, and the next node in the collision list.
25 */
26class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
27 public:
28 ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
29 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010030
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000031 size_t GetHashCode() const { return hash_code_; }
32 HInstruction* GetInstruction() const { return instruction_; }
33 ValueSetNode* GetNext() const { return next_; }
34 void SetNext(ValueSetNode* node) { next_ = node; }
35
36 private:
37 HInstruction* const instruction_;
38 const size_t hash_code_;
39 ValueSetNode* next_;
40
41 DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
42};
43
44/**
45 * A ValueSet holds instructions that can replace other instructions. It is updated
46 * through the `Add` method, and the `Kill` method. The `Kill` method removes
47 * instructions that are affected by the given side effect.
48 *
49 * The `Lookup` method returns an equivalent instruction to the given instruction
50 * if there is one in the set. In GVN, we would say those instructions have the
51 * same "number".
52 */
53class ValueSet : public ArenaObject<kArenaAllocMisc> {
54 public:
55 explicit ValueSet(ArenaAllocator* allocator)
56 : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
57 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
58 table_[i] = nullptr;
59 }
60 }
61
62 // Adds an instruction in the set.
63 void Add(HInstruction* instruction) {
64 DCHECK(Lookup(instruction) == nullptr);
65 size_t hash_code = instruction->ComputeHashCode();
66 size_t index = hash_code % kDefaultNumberOfEntries;
67 if (table_[index] == nullptr) {
68 table_[index] = instruction;
69 } else {
70 collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
71 }
72 ++number_of_entries_;
73 }
74
75 // If in the set, returns an equivalent instruction to the given instruction. Returns
76 // null otherwise.
77 HInstruction* Lookup(HInstruction* instruction) const {
78 size_t hash_code = instruction->ComputeHashCode();
79 size_t index = hash_code % kDefaultNumberOfEntries;
80 HInstruction* existing = table_[index];
81 if (existing != nullptr && existing->Equals(instruction)) {
82 return existing;
83 }
84
85 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
86 if (node->GetHashCode() == hash_code) {
87 existing = node->GetInstruction();
88 if (existing->Equals(instruction)) {
89 return existing;
90 }
91 }
92 }
93 return nullptr;
94 }
95
96 // Returns whether `instruction` is in the set.
97 HInstruction* IdentityLookup(HInstruction* instruction) const {
98 size_t hash_code = instruction->ComputeHashCode();
99 size_t index = hash_code % kDefaultNumberOfEntries;
100 HInstruction* existing = table_[index];
101 if (existing != nullptr && existing == instruction) {
102 return existing;
103 }
104
105 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
106 if (node->GetHashCode() == hash_code) {
107 existing = node->GetInstruction();
108 if (existing == instruction) {
109 return existing;
110 }
111 }
112 }
113 return nullptr;
114 }
115
116 // Removes all instructions in the set that are affected by the given side effects.
117 void Kill(SideEffects side_effects) {
118 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
119 HInstruction* instruction = table_[i];
120 if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
121 table_[i] = nullptr;
122 --number_of_entries_;
123 }
124 }
125
126 for (ValueSetNode* current = collisions_, *previous = nullptr;
127 current != nullptr;
128 current = current->GetNext()) {
129 HInstruction* instruction = current->GetInstruction();
130 if (instruction->GetSideEffects().DependsOn(side_effects)) {
131 if (previous == nullptr) {
132 collisions_ = current->GetNext();
133 } else {
134 previous->SetNext(current->GetNext());
135 }
136 --number_of_entries_;
137 } else {
138 previous = current;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100139 }
140 }
141 }
142
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000143 // Returns a copy of this set.
144 ValueSet* Copy() const {
145 ValueSet* copy = new (allocator_) ValueSet(allocator_);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100146
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000147 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
148 copy->table_[i] = table_[i];
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100149 }
150
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000151 // Note that the order will be inverted in the copy. This is fine, as the order is not
152 // relevant for a ValueSet.
153 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
154 copy->collisions_ = new (allocator_) ValueSetNode(
155 node->GetInstruction(), node->GetHashCode(), copy->collisions_);
156 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100157
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000158 copy->number_of_entries_ = number_of_entries_;
159 return copy;
160 }
161
162 void Clear() {
163 number_of_entries_ = 0;
164 collisions_ = nullptr;
165 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
166 table_[i] = nullptr;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100167 }
168 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100169
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000170 // Update this `ValueSet` by intersecting with instructions in `other`.
171 void IntersectionWith(ValueSet* other) {
172 if (IsEmpty()) {
173 return;
174 } else if (other->IsEmpty()) {
175 Clear();
176 } else {
177 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
178 if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
179 --number_of_entries_;
180 table_[i] = nullptr;
181 }
182 }
183 for (ValueSetNode* current = collisions_, *previous = nullptr;
184 current != nullptr;
185 current = current->GetNext()) {
186 if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
187 if (previous == nullptr) {
188 collisions_ = current->GetNext();
189 } else {
190 previous->SetNext(current->GetNext());
191 }
192 --number_of_entries_;
193 } else {
194 previous = current;
195 }
196 }
197 }
198 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100199
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000200 bool IsEmpty() const { return number_of_entries_ == 0; }
201 size_t GetNumberOfEntries() const { return number_of_entries_; }
202
203 private:
204 static constexpr size_t kDefaultNumberOfEntries = 8;
205
206 ArenaAllocator* const allocator_;
207
208 // The number of entries in the set.
209 size_t number_of_entries_;
210
211 // The internal implementation of the set. It uses a combination of a hash code based
212 // fixed-size list, and a linked list to handle hash code collisions.
213 // TODO: Tune the fixed size list original size, and support growing it.
214 ValueSetNode* collisions_;
215 HInstruction* table_[kDefaultNumberOfEntries];
216
217 DISALLOW_COPY_AND_ASSIGN(ValueSet);
218};
219
220/**
221 * Optimization phase that removes redundant instruction.
222 */
223class GlobalValueNumberer : public ValueObject {
224 public:
225 GlobalValueNumberer(ArenaAllocator* allocator,
226 HGraph* graph,
227 const SideEffectsAnalysis& side_effects)
228 : graph_(graph),
229 allocator_(allocator),
230 side_effects_(side_effects),
231 sets_(allocator, graph->GetBlocks().Size(), nullptr) {}
232
233 void Run();
234
235 private:
236 // Per-block GVN. Will also update the ValueSet of the dominated and
237 // successor blocks.
238 void VisitBasicBlock(HBasicBlock* block);
239
240 HGraph* graph_;
241 ArenaAllocator* const allocator_;
242 const SideEffectsAnalysis& side_effects_;
243
244 // ValueSet for blocks. Initially null, but for an individual block they
245 // are allocated and populated by the dominator, and updated by all blocks
246 // in the path from the dominator to the block.
247 GrowableArray<ValueSet*> sets_;
248
249 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
250};
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100251
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000252void GlobalValueNumberer::Run() {
253 DCHECK(side_effects_.HasRun());
254 sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
255
256 // Use the reverse post order to ensure the non back-edge predecessors of a block are
257 // visited before the block itself.
258 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
259 VisitBasicBlock(it.Current());
260 }
261}
262
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100263void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000264 ValueSet* set = nullptr;
265 const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
266 if (predecessors.Size() == 0 || predecessors.Get(0)->IsEntryBlock()) {
267 // The entry block should only accumulate constant instructions, and
268 // the builder puts constants only in the entry block.
269 // Therefore, there is no need to propagate the value set to the next block.
270 set = new (allocator_) ValueSet(allocator_);
271 } else {
272 HBasicBlock* dominator = block->GetDominator();
273 set = sets_.Get(dominator->GetBlockId())->Copy();
274 if (dominator->GetSuccessors().Size() != 1 || dominator->GetSuccessors().Get(0) != block) {
275 // We have to copy if the dominator has other successors, or `block` is not a successor
276 // of the dominator.
277 set = set->Copy();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100278 }
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000279 if (!set->IsEmpty()) {
280 if (block->IsLoopHeader()) {
281 DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000282 set->Kill(side_effects_.GetLoopEffects(block));
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000283 } else if (predecessors.Size() > 1) {
284 for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
285 set->IntersectionWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
286 if (set->IsEmpty()) {
287 break;
288 }
289 }
290 }
291 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100292 }
293
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000294 sets_.Put(block->GetBlockId(), set);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100295
296 HInstruction* current = block->GetFirstInstruction();
297 while (current != nullptr) {
298 set->Kill(current->GetSideEffects());
299 // Save the next instruction in case `current` is removed from the graph.
300 HInstruction* next = current->GetNext();
301 if (current->CanBeMoved()) {
302 HInstruction* existing = set->Lookup(current);
303 if (existing != nullptr) {
304 current->ReplaceWith(existing);
305 current->GetBlock()->RemoveInstruction(current);
306 } else {
307 set->Add(current);
308 }
309 }
310 current = next;
311 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100312}
313
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000314void GVNOptimization::Run() {
315 GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
316 gvn.Run();
317}
318
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100319} // namespace art