blob: 0db19cda8c65c437e9f2f83875f51519422cde45 [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"
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010018
Mathieu Chartiere5d80f82015-10-15 17:47:48 -070019#include "base/arena_bit_vector.h"
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010020#include "base/arena_containers.h"
21#include "base/bit_vector-inl.h"
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000022#include "side_effects_analysis.h"
David Brazdil6d340c42015-03-03 11:54:54 +000023#include "utils.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010024
25namespace art {
26
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000027/**
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000028 * A ValueSet holds instructions that can replace other instructions. It is updated
29 * through the `Add` method, and the `Kill` method. The `Kill` method removes
30 * instructions that are affected by the given side effect.
31 *
32 * The `Lookup` method returns an equivalent instruction to the given instruction
33 * if there is one in the set. In GVN, we would say those instructions have the
34 * same "number".
35 */
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010036class ValueSet : public ArenaObject<kArenaAllocGvn> {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000037 public:
David Brazdil6d340c42015-03-03 11:54:54 +000038 // Constructs an empty ValueSet which owns all its buckets.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000039 explicit ValueSet(ArenaAllocator* allocator)
David Brazdil6d340c42015-03-03 11:54:54 +000040 : allocator_(allocator),
41 num_buckets_(kMinimumNumberOfBuckets),
Vladimir Marko5233f932015-09-29 19:01:15 +010042 buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
Vladimir Markof6a35de2016-03-21 12:01:50 +000043 buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
David Brazdil4283aa92016-04-20 14:24:12 +010044 num_entries_(0u) {
David Brazdil6d340c42015-03-03 11:54:54 +000045 // ArenaAllocator returns zeroed memory, so no need to set buckets to null.
46 DCHECK(IsPowerOfTwo(num_buckets_));
47 buckets_owned_.SetInitialBits(num_buckets_);
48 }
49
50 // Copy constructor. Depending on the load factor, it will either make a deep
51 // copy (all buckets owned) or a shallow one (buckets pointing to the parent).
David Brazdil4283aa92016-04-20 14:24:12 +010052 ValueSet(ArenaAllocator* allocator, const ValueSet& other)
David Brazdil6d340c42015-03-03 11:54:54 +000053 : allocator_(allocator),
David Brazdil4283aa92016-04-20 14:24:12 +010054 num_buckets_(other.IdealBucketCount()),
Vladimir Marko5233f932015-09-29 19:01:15 +010055 buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
Vladimir Markof6a35de2016-03-21 12:01:50 +000056 buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
David Brazdil4283aa92016-04-20 14:24:12 +010057 num_entries_(0u) {
David Brazdil6d340c42015-03-03 11:54:54 +000058 // ArenaAllocator returns zeroed memory, so entries of buckets_ and
Mathieu Chartier2cebb242015-04-21 16:50:40 -070059 // buckets_owned_ are initialized to null and false, respectively.
David Brazdil6d340c42015-03-03 11:54:54 +000060 DCHECK(IsPowerOfTwo(num_buckets_));
David Brazdil4283aa92016-04-20 14:24:12 +010061 PopulateFromInternal(other, /* is_dirty */ false);
62 }
63
64 // Erases all values in this set and populates it with values from `other`.
65 void PopulateFrom(const ValueSet& other) {
66 if (this == &other) {
67 return;
68 }
69 PopulateFromInternal(other, /* is_dirty */ true);
70 }
71
72 // Returns true if `this` has enough buckets so that if `other` is copied into
73 // it, the load factor will not cross the upper threshold.
74 bool CanHoldCopyOf(const ValueSet& other, bool exact_match) {
75 if (exact_match) {
76 return other.IdealBucketCount() == num_buckets_;
David Brazdil6d340c42015-03-03 11:54:54 +000077 } else {
David Brazdil4283aa92016-04-20 14:24:12 +010078 return other.IdealBucketCount() <= num_buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000079 }
80 }
81
82 // Adds an instruction in the set.
83 void Add(HInstruction* instruction) {
84 DCHECK(Lookup(instruction) == nullptr);
David Brazdil6d340c42015-03-03 11:54:54 +000085 size_t hash_code = HashCode(instruction);
86 size_t index = BucketIndex(hash_code);
87
88 if (!buckets_owned_.IsBitSet(index)) {
89 CloneBucket(index);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000090 }
David Brazdil6d340c42015-03-03 11:54:54 +000091 buckets_[index] = new (allocator_) Node(instruction, hash_code, buckets_[index]);
92 ++num_entries_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000093 }
94
David Brazdil6d340c42015-03-03 11:54:54 +000095 // If in the set, returns an equivalent instruction to the given instruction.
96 // Returns null otherwise.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000097 HInstruction* Lookup(HInstruction* instruction) const {
David Brazdil6d340c42015-03-03 11:54:54 +000098 size_t hash_code = HashCode(instruction);
99 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000100
David Brazdil6d340c42015-03-03 11:54:54 +0000101 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000102 if (node->GetHashCode() == hash_code) {
David Brazdil6d340c42015-03-03 11:54:54 +0000103 HInstruction* existing = node->GetInstruction();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000104 if (existing->Equals(instruction)) {
105 return existing;
106 }
107 }
108 }
109 return nullptr;
110 }
111
David Brazdil6d340c42015-03-03 11:54:54 +0000112 // Returns whether instruction is in the set.
113 bool Contains(HInstruction* instruction) const {
114 size_t hash_code = HashCode(instruction);
115 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000116
David Brazdil6d340c42015-03-03 11:54:54 +0000117 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
118 if (node->GetInstruction() == instruction) {
119 return true;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000120 }
121 }
David Brazdil6d340c42015-03-03 11:54:54 +0000122 return false;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000123 }
124
David Brazdil6d340c42015-03-03 11:54:54 +0000125 // Removes all instructions in the set affected by the given side effects.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000126 void Kill(SideEffects side_effects) {
David Brazdil6d340c42015-03-03 11:54:54 +0000127 DeleteAllImpureWhich([side_effects](Node* node) {
Aart Bik854a02b2015-07-14 16:07:00 -0700128 return node->GetInstruction()->GetSideEffects().MayDependOn(side_effects);
David Brazdil6d340c42015-03-03 11:54:54 +0000129 });
130 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000131
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000132 void Clear() {
133 num_entries_ = 0;
134 for (size_t i = 0; i < num_buckets_; ++i) {
135 buckets_[i] = nullptr;
136 }
137 buckets_owned_.SetInitialBits(num_buckets_);
138 }
139
David Brazdil6d340c42015-03-03 11:54:54 +0000140 // Updates this set by intersecting with instructions in a predecessor's set.
141 void IntersectWith(ValueSet* predecessor) {
142 if (IsEmpty()) {
143 return;
144 } else if (predecessor->IsEmpty()) {
145 Clear();
146 } else {
147 // Pure instructions do not need to be tested because only impure
148 // instructions can be killed.
149 DeleteAllImpureWhich([predecessor](Node* node) {
150 return !predecessor->Contains(node->GetInstruction());
151 });
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100152 }
153 }
154
David Brazdil6d340c42015-03-03 11:54:54 +0000155 bool IsEmpty() const { return num_entries_ == 0; }
156 size_t GetNumberOfEntries() const { return num_entries_; }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100157
David Brazdil6d340c42015-03-03 11:54:54 +0000158 private:
David Brazdil4283aa92016-04-20 14:24:12 +0100159 void PopulateFromInternal(const ValueSet& other, bool is_dirty) {
160 DCHECK_NE(this, &other);
161 DCHECK_GE(num_buckets_, other.IdealBucketCount());
162
163 if (num_buckets_ == other.num_buckets_) {
164 // Hash table remains the same size. We copy the bucket pointers and leave
165 // all buckets_owned_ bits false.
166 if (is_dirty) {
167 buckets_owned_.ClearAllBits();
168 }
169 memcpy(buckets_, other.buckets_, num_buckets_ * sizeof(Node*));
170 } else {
171 // Hash table size changes. We copy and rehash all entries, and set all
172 // buckets_owned_ bits to true.
173 if (is_dirty) {
174 memset(buckets_, 0, num_buckets_ * sizeof(Node*));
175 }
176 for (size_t i = 0; i < other.num_buckets_; ++i) {
177 for (Node* node = other.buckets_[i]; node != nullptr; node = node->GetNext()) {
178 size_t new_index = BucketIndex(node->GetHashCode());
179 buckets_[new_index] = node->Dup(allocator_, buckets_[new_index]);
180 }
181 }
182 buckets_owned_.SetInitialBits(num_buckets_);
183 }
184
185 num_entries_ = other.num_entries_;
186 }
187
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100188 class Node : public ArenaObject<kArenaAllocGvn> {
David Brazdil6d340c42015-03-03 11:54:54 +0000189 public:
190 Node(HInstruction* instruction, size_t hash_code, Node* next)
191 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
192
193 size_t GetHashCode() const { return hash_code_; }
194 HInstruction* GetInstruction() const { return instruction_; }
195 Node* GetNext() const { return next_; }
196 void SetNext(Node* node) { next_ = node; }
197
198 Node* Dup(ArenaAllocator* allocator, Node* new_next = nullptr) {
199 return new (allocator) Node(instruction_, hash_code_, new_next);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100200 }
201
David Brazdil6d340c42015-03-03 11:54:54 +0000202 private:
203 HInstruction* const instruction_;
204 const size_t hash_code_;
205 Node* next_;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100206
David Brazdil6d340c42015-03-03 11:54:54 +0000207 DISALLOW_COPY_AND_ASSIGN(Node);
208 };
209
210 // Creates our own copy of a bucket that is currently pointing to a parent.
211 // This algorithm can be called while iterating over the bucket because it
212 // preserves the order of entries in the bucket and will return the clone of
213 // the given 'iterator'.
214 Node* CloneBucket(size_t index, Node* iterator = nullptr) {
215 DCHECK(!buckets_owned_.IsBitSet(index));
216 Node* clone_current = nullptr;
217 Node* clone_previous = nullptr;
218 Node* clone_iterator = nullptr;
219 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
220 clone_current = node->Dup(allocator_, nullptr);
221 if (node == iterator) {
222 clone_iterator = clone_current;
223 }
224 if (clone_previous == nullptr) {
225 buckets_[index] = clone_current;
226 } else {
227 clone_previous->SetNext(clone_current);
228 }
229 clone_previous = clone_current;
230 }
231 buckets_owned_.SetBit(index);
232 return clone_iterator;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000233 }
234
David Brazdil6d340c42015-03-03 11:54:54 +0000235 // Iterates over buckets with impure instructions (even indices) and deletes
236 // the ones on which 'cond' returns true.
237 template<typename Functor>
238 void DeleteAllImpureWhich(Functor cond) {
239 for (size_t i = 0; i < num_buckets_; i += 2) {
240 Node* node = buckets_[i];
241 Node* previous = nullptr;
242
243 if (node == nullptr) {
244 continue;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000245 }
David Brazdil6d340c42015-03-03 11:54:54 +0000246
247 if (!buckets_owned_.IsBitSet(i)) {
248 // Bucket is not owned but maybe we won't need to change it at all.
249 // Iterate as long as the entries don't satisfy 'cond'.
250 while (node != nullptr) {
251 if (cond(node)) {
252 // We do need to delete an entry but we do not own the bucket.
253 // Clone the bucket, make sure 'previous' and 'node' point to
254 // the cloned entries and break.
255 previous = CloneBucket(i, previous);
256 node = (previous == nullptr) ? buckets_[i] : previous->GetNext();
257 break;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000258 }
David Brazdil6d340c42015-03-03 11:54:54 +0000259 previous = node;
260 node = node->GetNext();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000261 }
262 }
David Brazdil6d340c42015-03-03 11:54:54 +0000263
264 // By this point we either own the bucket and can start deleting entries,
265 // or we do not own it but no entries matched 'cond'.
266 DCHECK(buckets_owned_.IsBitSet(i) || node == nullptr);
267
268 // We iterate over the remainder of entries and delete those that match
269 // the given condition.
270 while (node != nullptr) {
271 Node* next = node->GetNext();
272 if (cond(node)) {
273 if (previous == nullptr) {
274 buckets_[i] = next;
275 } else {
276 previous->SetNext(next);
277 }
278 } else {
279 previous = node;
280 }
281 node = next;
282 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000283 }
284 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100285
David Brazdil6d340c42015-03-03 11:54:54 +0000286 // Computes a bucket count such that the load factor is reasonable.
287 // This is estimated as (num_entries_ * 1.5) and rounded up to nearest pow2.
288 size_t IdealBucketCount() const {
289 size_t bucket_count = RoundUpToPowerOfTwo(num_entries_ + (num_entries_ >> 1));
290 if (bucket_count > kMinimumNumberOfBuckets) {
291 return bucket_count;
292 } else {
293 return kMinimumNumberOfBuckets;
294 }
295 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000296
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000297 // Generates a hash code for an instruction.
David Brazdil6d340c42015-03-03 11:54:54 +0000298 size_t HashCode(HInstruction* instruction) const {
299 size_t hash_code = instruction->ComputeHashCode();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000300 // Pure instructions are put into odd buckets to speed up deletion. Note that in the
301 // case of irreducible loops, we don't put pure instructions in odd buckets, as we
302 // need to delete them when entering the loop.
303 if (instruction->GetSideEffects().HasDependencies() ||
304 instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) {
David Brazdil6d340c42015-03-03 11:54:54 +0000305 return (hash_code << 1) | 0;
306 } else {
307 return (hash_code << 1) | 1;
308 }
309 }
310
311 // Converts a hash code to a bucket index.
312 size_t BucketIndex(size_t hash_code) const {
313 return hash_code & (num_buckets_ - 1);
314 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000315
316 ArenaAllocator* const allocator_;
317
David Brazdil6d340c42015-03-03 11:54:54 +0000318 // The internal bucket implementation of the set.
319 size_t const num_buckets_;
320 Node** const buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000321
David Brazdil6d340c42015-03-03 11:54:54 +0000322 // Flags specifying which buckets were copied into the set from its parent.
323 // If a flag is not set, the corresponding bucket points to entries in the
324 // parent and must be cloned prior to making changes.
325 ArenaBitVector buckets_owned_;
326
327 // The number of entries in the set.
328 size_t num_entries_;
329
330 static constexpr size_t kMinimumNumberOfBuckets = 8;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000331
332 DISALLOW_COPY_AND_ASSIGN(ValueSet);
333};
334
335/**
336 * Optimization phase that removes redundant instruction.
337 */
338class GlobalValueNumberer : public ValueObject {
339 public:
340 GlobalValueNumberer(ArenaAllocator* allocator,
341 HGraph* graph,
342 const SideEffectsAnalysis& side_effects)
343 : graph_(graph),
344 allocator_(allocator),
345 side_effects_(side_effects),
David Brazdil4283aa92016-04-20 14:24:12 +0100346 sets_(graph->GetBlocks().size(), nullptr, allocator->Adapter(kArenaAllocGvn)),
347 visited_blocks_(
348 allocator, graph->GetBlocks().size(), /* expandable */ false, kArenaAllocGvn) {}
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000349
350 void Run();
351
352 private:
353 // Per-block GVN. Will also update the ValueSet of the dominated and
354 // successor blocks.
355 void VisitBasicBlock(HBasicBlock* block);
356
357 HGraph* graph_;
358 ArenaAllocator* const allocator_;
359 const SideEffectsAnalysis& side_effects_;
360
David Brazdil4283aa92016-04-20 14:24:12 +0100361 ValueSet* FindSetFor(HBasicBlock* block) const {
362 ValueSet* result = sets_[block->GetBlockId()];
363 DCHECK(result != nullptr) << "Could not find set for block B" << block->GetBlockId();
364 return result;
365 }
366
367 void AbandonSetFor(HBasicBlock* block) {
368 DCHECK(sets_[block->GetBlockId()] != nullptr)
369 << "Block B" << block->GetBlockId() << " expected to have a set";
370 sets_[block->GetBlockId()] = nullptr;
371 }
372
373 // Returns false if the GlobalValueNumberer has already visited all blocks
374 // which may reference `block`.
375 bool WillBeReferencedAgain(HBasicBlock* block) const;
376
377 // Iterates over visited blocks and finds one which has a ValueSet such that:
378 // (a) it will not be referenced in the future, and
379 // (b) it can hold a copy of `reference_set` with a reasonable load factor.
380 HBasicBlock* FindVisitedBlockWithRecyclableSet(HBasicBlock* block,
381 const ValueSet& reference_set) const;
382
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000383 // ValueSet for blocks. Initially null, but for an individual block they
384 // are allocated and populated by the dominator, and updated by all blocks
385 // in the path from the dominator to the block.
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100386 ArenaVector<ValueSet*> sets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000387
David Brazdil4283aa92016-04-20 14:24:12 +0100388 // BitVector which serves as a fast-access map from block id to
389 // visited/unvisited boolean.
390 ArenaBitVector visited_blocks_;
391
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000392 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
393};
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100394
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000395void GlobalValueNumberer::Run() {
396 DCHECK(side_effects_.HasRun());
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100397 sets_[graph_->GetEntryBlock()->GetBlockId()] = new (allocator_) ValueSet(allocator_);
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000398
399 // Use the reverse post order to ensure the non back-edge predecessors of a block are
400 // visited before the block itself.
401 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
402 VisitBasicBlock(it.Current());
403 }
404}
405
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100406void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000407 ValueSet* set = nullptr;
David Brazdil4283aa92016-04-20 14:24:12 +0100408 HBasicBlock* skip_predecessor = nullptr;
409
Vladimir Marko60584552015-09-03 13:35:12 +0000410 const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors();
411 if (predecessors.size() == 0 || predecessors[0]->IsEntryBlock()) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000412 // The entry block should only accumulate constant instructions, and
413 // the builder puts constants only in the entry block.
414 // Therefore, there is no need to propagate the value set to the next block.
415 set = new (allocator_) ValueSet(allocator_);
416 } else {
417 HBasicBlock* dominator = block->GetDominator();
David Brazdil4283aa92016-04-20 14:24:12 +0100418 ValueSet* dominator_set = FindSetFor(dominator);
419
Vladimir Marko60584552015-09-03 13:35:12 +0000420 if (dominator->GetSuccessors().size() == 1) {
David Brazdil4283aa92016-04-20 14:24:12 +0100421 // `block` is a direct successor of its dominator. No need to clone the
422 // dominator's set, `block` can take over its ownership including its buckets.
423 DCHECK_EQ(dominator->GetSingleSuccessor(), block);
424 AbandonSetFor(dominator);
David Brazdil6d340c42015-03-03 11:54:54 +0000425 set = dominator_set;
David Brazdil4283aa92016-04-20 14:24:12 +0100426 // Since we took over the dominator's ValueSet, we must not reference it
427 // again. If `dominator` is also one of the predecessors, we skip it.
428 skip_predecessor = dominator;
David Brazdil6d340c42015-03-03 11:54:54 +0000429 } else {
David Brazdil4283aa92016-04-20 14:24:12 +0100430 HBasicBlock* recyclable = FindVisitedBlockWithRecyclableSet(block, *dominator_set);
431 if (recyclable == nullptr) {
432 // No block with a recyclable ValueSet found. Allocate a new one and
433 // copy `dominator_set` into it.
434 set = new (allocator_) ValueSet(allocator_, *dominator_set);
435 } else {
436 // Block with a recyclable ValueSet found. Clone `dominator_set` into it.
437 set = FindSetFor(recyclable);
438 AbandonSetFor(recyclable);
439 set->PopulateFrom(*dominator_set);
440 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100441 }
David Brazdil4283aa92016-04-20 14:24:12 +0100442
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000443 if (!set->IsEmpty()) {
444 if (block->IsLoopHeader()) {
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000445 if (block->GetLoopInformation()->IsIrreducible()) {
446 // To satisfy our linear scan algorithm, no instruction should flow in an irreducible
447 // loop header.
448 set->Clear();
449 } else {
450 DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
451 set->Kill(side_effects_.GetLoopEffects(block));
452 }
Vladimir Marko60584552015-09-03 13:35:12 +0000453 } else if (predecessors.size() > 1) {
454 for (HBasicBlock* predecessor : predecessors) {
David Brazdil4283aa92016-04-20 14:24:12 +0100455 if (predecessor != skip_predecessor) {
456 set->IntersectWith(FindSetFor(predecessor));
457 if (set->IsEmpty()) {
458 break;
459 }
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000460 }
461 }
462 }
463 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100464 }
465
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100466 sets_[block->GetBlockId()] = set;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100467
468 HInstruction* current = block->GetFirstInstruction();
469 while (current != nullptr) {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100470 // Save the next instruction in case `current` is removed from the graph.
471 HInstruction* next = current->GetNext();
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000472 // Do not kill the set with the side effects of the instruction just now: if
473 // the instruction is GVN'ed, we don't need to kill.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100474 if (current->CanBeMoved()) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800475 if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
476 // For commutative ops, (x op y) will be treated the same as (y op x)
477 // after fixed ordering.
478 current->AsBinaryOperation()->OrderInputs();
479 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100480 HInstruction* existing = set->Lookup(current);
481 if (existing != nullptr) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800482 // This replacement doesn't make more OrderInputs() necessary since
483 // current is either used by an instruction that it dominates,
484 // which hasn't been visited yet due to the order we visit instructions.
485 // Or current is used by a phi, and we don't do OrderInputs() on a phi anyway.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100486 current->ReplaceWith(existing);
487 current->GetBlock()->RemoveInstruction(current);
488 } else {
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000489 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100490 set->Add(current);
491 }
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000492 } else {
493 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100494 }
495 current = next;
496 }
David Brazdil4283aa92016-04-20 14:24:12 +0100497
498 visited_blocks_.SetBit(block->GetBlockId());
499}
500
501bool GlobalValueNumberer::WillBeReferencedAgain(HBasicBlock* block) const {
502 DCHECK(visited_blocks_.IsBitSet(block->GetBlockId()));
503
504 for (auto dominated_block : block->GetDominatedBlocks()) {
505 if (!visited_blocks_.IsBitSet(dominated_block->GetBlockId())) {
506 return true;
507 }
508 }
509
510 for (auto successor : block->GetSuccessors()) {
511 if (!visited_blocks_.IsBitSet(successor->GetBlockId())) {
512 return true;
513 }
514 }
515
516 return false;
517}
518
519HBasicBlock* GlobalValueNumberer::FindVisitedBlockWithRecyclableSet(
520 HBasicBlock* block, const ValueSet& reference_set) const {
521 HBasicBlock* secondary_match = nullptr;
522
523 for (size_t block_id : visited_blocks_.Indexes()) {
524 ValueSet* current_set = sets_[block_id];
525 if (current_set == nullptr) {
526 // Set was already recycled.
527 continue;
528 }
529
530 HBasicBlock* current_block = block->GetGraph()->GetBlocks()[block_id];
531
532 // We test if `current_set` has enough buckets to store a copy of
533 // `reference_set` with a reasonable load factor. If we find a set whose
534 // number of buckets matches perfectly, we return right away. If we find one
535 // that is larger, we return it if no perfectly-matching set is found.
536 // Note that we defer testing WillBeReferencedAgain until all other criteria
537 // have been satisfied because it might be expensive.
538 if (current_set->CanHoldCopyOf(reference_set, /* exact_match */ true)) {
539 if (!WillBeReferencedAgain(current_block)) {
540 return current_block;
541 }
542 } else if (secondary_match == nullptr &&
543 current_set->CanHoldCopyOf(reference_set, /* exact_match */ false)) {
544 if (!WillBeReferencedAgain(current_block)) {
545 secondary_match = current_block;
546 }
547 }
548 }
549
550 return secondary_match;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100551}
552
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000553void GVNOptimization::Run() {
554 GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
555 gvn.Run();
556}
557
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100558} // namespace art