blob: 74848d5d968423026f1b98ef357159aeefac9fb7 [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"
David Brazdil6d340c42015-03-03 11:54:54 +000019#include "utils.h"
20
21#include "utils/arena_bit_vector.h"
22#include "base/bit_vector-inl.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010023
24namespace art {
25
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000026/**
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000027 * A ValueSet holds instructions that can replace other instructions. It is updated
28 * through the `Add` method, and the `Kill` method. The `Kill` method removes
29 * instructions that are affected by the given side effect.
30 *
31 * The `Lookup` method returns an equivalent instruction to the given instruction
32 * if there is one in the set. In GVN, we would say those instructions have the
33 * same "number".
34 */
35class ValueSet : public ArenaObject<kArenaAllocMisc> {
36 public:
David Brazdil6d340c42015-03-03 11:54:54 +000037 // Constructs an empty ValueSet which owns all its buckets.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000038 explicit ValueSet(ArenaAllocator* allocator)
David Brazdil6d340c42015-03-03 11:54:54 +000039 : allocator_(allocator),
40 num_buckets_(kMinimumNumberOfBuckets),
41 buckets_(allocator->AllocArray<Node*>(num_buckets_)),
42 buckets_owned_(allocator, num_buckets_, false),
43 num_entries_(0) {
44 // ArenaAllocator returns zeroed memory, so no need to set buckets to null.
45 DCHECK(IsPowerOfTwo(num_buckets_));
46 buckets_owned_.SetInitialBits(num_buckets_);
47 }
48
49 // Copy constructor. Depending on the load factor, it will either make a deep
50 // copy (all buckets owned) or a shallow one (buckets pointing to the parent).
51 ValueSet(ArenaAllocator* allocator, const ValueSet& to_copy)
52 : allocator_(allocator),
53 num_buckets_(to_copy.IdealBucketCount()),
54 buckets_(allocator->AllocArray<Node*>(num_buckets_)),
55 buckets_owned_(allocator, num_buckets_, false),
56 num_entries_(to_copy.num_entries_) {
57 // ArenaAllocator returns zeroed memory, so entries of buckets_ and
58 // buckets_owned_ are initialized to nullptr and false, respectively.
59 DCHECK(IsPowerOfTwo(num_buckets_));
60 if (num_buckets_ == to_copy.num_buckets_) {
61 // Hash table remains the same size. We copy the bucket pointers and leave
62 // all buckets_owned_ bits false.
63 memcpy(buckets_, to_copy.buckets_, num_buckets_ * sizeof(Node*));
64 } else {
65 // Hash table size changes. We copy and rehash all entries, and set all
66 // buckets_owned_ bits to true.
67 for (size_t i = 0; i < to_copy.num_buckets_; ++i) {
68 for (Node* node = to_copy.buckets_[i]; node != nullptr; node = node->GetNext()) {
69 size_t new_index = BucketIndex(node->GetHashCode());
70 buckets_[new_index] = node->Dup(allocator_, buckets_[new_index]);
71 }
72 }
73 buckets_owned_.SetInitialBits(num_buckets_);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000074 }
75 }
76
77 // Adds an instruction in the set.
78 void Add(HInstruction* instruction) {
79 DCHECK(Lookup(instruction) == nullptr);
David Brazdil6d340c42015-03-03 11:54:54 +000080 size_t hash_code = HashCode(instruction);
81 size_t index = BucketIndex(hash_code);
82
83 if (!buckets_owned_.IsBitSet(index)) {
84 CloneBucket(index);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000085 }
David Brazdil6d340c42015-03-03 11:54:54 +000086 buckets_[index] = new (allocator_) Node(instruction, hash_code, buckets_[index]);
87 ++num_entries_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000088 }
89
David Brazdil6d340c42015-03-03 11:54:54 +000090 // If in the set, returns an equivalent instruction to the given instruction.
91 // Returns null otherwise.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000092 HInstruction* Lookup(HInstruction* instruction) const {
David Brazdil6d340c42015-03-03 11:54:54 +000093 size_t hash_code = HashCode(instruction);
94 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000095
David Brazdil6d340c42015-03-03 11:54:54 +000096 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000097 if (node->GetHashCode() == hash_code) {
David Brazdil6d340c42015-03-03 11:54:54 +000098 HInstruction* existing = node->GetInstruction();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000099 if (existing->Equals(instruction)) {
100 return existing;
101 }
102 }
103 }
104 return nullptr;
105 }
106
David Brazdil6d340c42015-03-03 11:54:54 +0000107 // Returns whether instruction is in the set.
108 bool Contains(HInstruction* instruction) const {
109 size_t hash_code = HashCode(instruction);
110 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000111
David Brazdil6d340c42015-03-03 11:54:54 +0000112 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
113 if (node->GetInstruction() == instruction) {
114 return true;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000115 }
116 }
David Brazdil6d340c42015-03-03 11:54:54 +0000117 return false;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000118 }
119
David Brazdil6d340c42015-03-03 11:54:54 +0000120 // Removes all instructions in the set affected by the given side effects.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000121 void Kill(SideEffects side_effects) {
David Brazdil6d340c42015-03-03 11:54:54 +0000122 DeleteAllImpureWhich([side_effects](Node* node) {
123 return node->GetInstruction()->GetSideEffects().DependsOn(side_effects);
124 });
125 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000126
David Brazdil6d340c42015-03-03 11:54:54 +0000127 // Updates this set by intersecting with instructions in a predecessor's set.
128 void IntersectWith(ValueSet* predecessor) {
129 if (IsEmpty()) {
130 return;
131 } else if (predecessor->IsEmpty()) {
132 Clear();
133 } else {
134 // Pure instructions do not need to be tested because only impure
135 // instructions can be killed.
136 DeleteAllImpureWhich([predecessor](Node* node) {
137 return !predecessor->Contains(node->GetInstruction());
138 });
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100139 }
140 }
141
David Brazdil6d340c42015-03-03 11:54:54 +0000142 bool IsEmpty() const { return num_entries_ == 0; }
143 size_t GetNumberOfEntries() const { return num_entries_; }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100144
David Brazdil6d340c42015-03-03 11:54:54 +0000145 private:
146 class Node : public ArenaObject<kArenaAllocMisc> {
147 public:
148 Node(HInstruction* instruction, size_t hash_code, Node* next)
149 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
150
151 size_t GetHashCode() const { return hash_code_; }
152 HInstruction* GetInstruction() const { return instruction_; }
153 Node* GetNext() const { return next_; }
154 void SetNext(Node* node) { next_ = node; }
155
156 Node* Dup(ArenaAllocator* allocator, Node* new_next = nullptr) {
157 return new (allocator) Node(instruction_, hash_code_, new_next);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100158 }
159
David Brazdil6d340c42015-03-03 11:54:54 +0000160 private:
161 HInstruction* const instruction_;
162 const size_t hash_code_;
163 Node* next_;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100164
David Brazdil6d340c42015-03-03 11:54:54 +0000165 DISALLOW_COPY_AND_ASSIGN(Node);
166 };
167
168 // Creates our own copy of a bucket that is currently pointing to a parent.
169 // This algorithm can be called while iterating over the bucket because it
170 // preserves the order of entries in the bucket and will return the clone of
171 // the given 'iterator'.
172 Node* CloneBucket(size_t index, Node* iterator = nullptr) {
173 DCHECK(!buckets_owned_.IsBitSet(index));
174 Node* clone_current = nullptr;
175 Node* clone_previous = nullptr;
176 Node* clone_iterator = nullptr;
177 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
178 clone_current = node->Dup(allocator_, nullptr);
179 if (node == iterator) {
180 clone_iterator = clone_current;
181 }
182 if (clone_previous == nullptr) {
183 buckets_[index] = clone_current;
184 } else {
185 clone_previous->SetNext(clone_current);
186 }
187 clone_previous = clone_current;
188 }
189 buckets_owned_.SetBit(index);
190 return clone_iterator;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000191 }
192
193 void Clear() {
David Brazdil6d340c42015-03-03 11:54:54 +0000194 num_entries_ = 0;
195 for (size_t i = 0; i < num_buckets_; ++i) {
196 buckets_[i] = nullptr;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100197 }
David Brazdil6d340c42015-03-03 11:54:54 +0000198 buckets_owned_.SetInitialBits(num_buckets_);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100199 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100200
David Brazdil6d340c42015-03-03 11:54:54 +0000201 // Iterates over buckets with impure instructions (even indices) and deletes
202 // the ones on which 'cond' returns true.
203 template<typename Functor>
204 void DeleteAllImpureWhich(Functor cond) {
205 for (size_t i = 0; i < num_buckets_; i += 2) {
206 Node* node = buckets_[i];
207 Node* previous = nullptr;
208
209 if (node == nullptr) {
210 continue;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000211 }
David Brazdil6d340c42015-03-03 11:54:54 +0000212
213 if (!buckets_owned_.IsBitSet(i)) {
214 // Bucket is not owned but maybe we won't need to change it at all.
215 // Iterate as long as the entries don't satisfy 'cond'.
216 while (node != nullptr) {
217 if (cond(node)) {
218 // We do need to delete an entry but we do not own the bucket.
219 // Clone the bucket, make sure 'previous' and 'node' point to
220 // the cloned entries and break.
221 previous = CloneBucket(i, previous);
222 node = (previous == nullptr) ? buckets_[i] : previous->GetNext();
223 break;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000224 }
David Brazdil6d340c42015-03-03 11:54:54 +0000225 previous = node;
226 node = node->GetNext();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000227 }
228 }
David Brazdil6d340c42015-03-03 11:54:54 +0000229
230 // By this point we either own the bucket and can start deleting entries,
231 // or we do not own it but no entries matched 'cond'.
232 DCHECK(buckets_owned_.IsBitSet(i) || node == nullptr);
233
234 // We iterate over the remainder of entries and delete those that match
235 // the given condition.
236 while (node != nullptr) {
237 Node* next = node->GetNext();
238 if (cond(node)) {
239 if (previous == nullptr) {
240 buckets_[i] = next;
241 } else {
242 previous->SetNext(next);
243 }
244 } else {
245 previous = node;
246 }
247 node = next;
248 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000249 }
250 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100251
David Brazdil6d340c42015-03-03 11:54:54 +0000252 // Computes a bucket count such that the load factor is reasonable.
253 // This is estimated as (num_entries_ * 1.5) and rounded up to nearest pow2.
254 size_t IdealBucketCount() const {
255 size_t bucket_count = RoundUpToPowerOfTwo(num_entries_ + (num_entries_ >> 1));
256 if (bucket_count > kMinimumNumberOfBuckets) {
257 return bucket_count;
258 } else {
259 return kMinimumNumberOfBuckets;
260 }
261 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000262
David Brazdil6d340c42015-03-03 11:54:54 +0000263 // Generates a hash code for an instruction. Pure instructions are put into
264 // odd buckets to speed up deletion.
265 size_t HashCode(HInstruction* instruction) const {
266 size_t hash_code = instruction->ComputeHashCode();
267 if (instruction->GetSideEffects().HasDependencies()) {
268 return (hash_code << 1) | 0;
269 } else {
270 return (hash_code << 1) | 1;
271 }
272 }
273
274 // Converts a hash code to a bucket index.
275 size_t BucketIndex(size_t hash_code) const {
276 return hash_code & (num_buckets_ - 1);
277 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000278
279 ArenaAllocator* const allocator_;
280
David Brazdil6d340c42015-03-03 11:54:54 +0000281 // The internal bucket implementation of the set.
282 size_t const num_buckets_;
283 Node** const buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000284
David Brazdil6d340c42015-03-03 11:54:54 +0000285 // Flags specifying which buckets were copied into the set from its parent.
286 // If a flag is not set, the corresponding bucket points to entries in the
287 // parent and must be cloned prior to making changes.
288 ArenaBitVector buckets_owned_;
289
290 // The number of entries in the set.
291 size_t num_entries_;
292
293 static constexpr size_t kMinimumNumberOfBuckets = 8;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000294
295 DISALLOW_COPY_AND_ASSIGN(ValueSet);
296};
297
298/**
299 * Optimization phase that removes redundant instruction.
300 */
301class GlobalValueNumberer : public ValueObject {
302 public:
303 GlobalValueNumberer(ArenaAllocator* allocator,
304 HGraph* graph,
305 const SideEffectsAnalysis& side_effects)
306 : graph_(graph),
307 allocator_(allocator),
308 side_effects_(side_effects),
309 sets_(allocator, graph->GetBlocks().Size(), nullptr) {}
310
311 void Run();
312
313 private:
314 // Per-block GVN. Will also update the ValueSet of the dominated and
315 // successor blocks.
316 void VisitBasicBlock(HBasicBlock* block);
317
318 HGraph* graph_;
319 ArenaAllocator* const allocator_;
320 const SideEffectsAnalysis& side_effects_;
321
322 // ValueSet for blocks. Initially null, but for an individual block they
323 // are allocated and populated by the dominator, and updated by all blocks
324 // in the path from the dominator to the block.
325 GrowableArray<ValueSet*> sets_;
326
327 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
328};
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100329
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000330void GlobalValueNumberer::Run() {
331 DCHECK(side_effects_.HasRun());
332 sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
333
334 // Use the reverse post order to ensure the non back-edge predecessors of a block are
335 // visited before the block itself.
336 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
337 VisitBasicBlock(it.Current());
338 }
339}
340
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100341void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000342 ValueSet* set = nullptr;
343 const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
344 if (predecessors.Size() == 0 || predecessors.Get(0)->IsEntryBlock()) {
345 // The entry block should only accumulate constant instructions, and
346 // the builder puts constants only in the entry block.
347 // Therefore, there is no need to propagate the value set to the next block.
348 set = new (allocator_) ValueSet(allocator_);
349 } else {
350 HBasicBlock* dominator = block->GetDominator();
David Brazdil6d340c42015-03-03 11:54:54 +0000351 ValueSet* dominator_set = sets_.Get(dominator->GetBlockId());
352 if (dominator->GetSuccessors().Size() == 1) {
353 DCHECK_EQ(dominator->GetSuccessors().Get(0), block);
354 set = dominator_set;
355 } else {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000356 // We have to copy if the dominator has other successors, or `block` is not a successor
357 // of the dominator.
David Brazdil6d340c42015-03-03 11:54:54 +0000358 set = new (allocator_) ValueSet(allocator_, *dominator_set);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100359 }
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000360 if (!set->IsEmpty()) {
361 if (block->IsLoopHeader()) {
362 DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000363 set->Kill(side_effects_.GetLoopEffects(block));
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000364 } else if (predecessors.Size() > 1) {
365 for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
David Brazdil6d340c42015-03-03 11:54:54 +0000366 set->IntersectWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000367 if (set->IsEmpty()) {
368 break;
369 }
370 }
371 }
372 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100373 }
374
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000375 sets_.Put(block->GetBlockId(), set);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100376
377 HInstruction* current = block->GetFirstInstruction();
378 while (current != nullptr) {
379 set->Kill(current->GetSideEffects());
380 // Save the next instruction in case `current` is removed from the graph.
381 HInstruction* next = current->GetNext();
382 if (current->CanBeMoved()) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800383 if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
384 // For commutative ops, (x op y) will be treated the same as (y op x)
385 // after fixed ordering.
386 current->AsBinaryOperation()->OrderInputs();
387 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100388 HInstruction* existing = set->Lookup(current);
389 if (existing != nullptr) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800390 // This replacement doesn't make more OrderInputs() necessary since
391 // current is either used by an instruction that it dominates,
392 // which hasn't been visited yet due to the order we visit instructions.
393 // Or current is used by a phi, and we don't do OrderInputs() on a phi anyway.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100394 current->ReplaceWith(existing);
395 current->GetBlock()->RemoveInstruction(current);
396 } else {
397 set->Add(current);
398 }
399 }
400 current = next;
401 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100402}
403
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000404void GVNOptimization::Run() {
405 GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
406 gvn.Run();
407}
408
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100409} // namespace art