blob: e6b6326726eee80d67506d93098d305de1f4d9ec [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"
David Sehrc431b9d2018-03-02 12:01:51 -080020#include "base/bit_vector-inl.h"
Vladimir Marko52d52f52017-10-10 11:04:48 +010021#include "base/scoped_arena_allocator.h"
22#include "base/scoped_arena_containers.h"
David Sehrc431b9d2018-03-02 12:01:51 -080023#include "base/utils.h"
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000024#include "side_effects_analysis.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010025
26namespace art {
27
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000028/**
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000029 * A ValueSet holds instructions that can replace other instructions. It is updated
30 * through the `Add` method, and the `Kill` method. The `Kill` method removes
31 * instructions that are affected by the given side effect.
32 *
33 * The `Lookup` method returns an equivalent instruction to the given instruction
34 * if there is one in the set. In GVN, we would say those instructions have the
35 * same "number".
36 */
Vladimir Marko2aaa4b52015-09-17 17:03:26 +010037class ValueSet : public ArenaObject<kArenaAllocGvn> {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000038 public:
David Brazdil6d340c42015-03-03 11:54:54 +000039 // Constructs an empty ValueSet which owns all its buckets.
Vladimir Marko52d52f52017-10-10 11:04:48 +010040 explicit ValueSet(ScopedArenaAllocator* allocator)
David Brazdil6d340c42015-03-03 11:54:54 +000041 : allocator_(allocator),
42 num_buckets_(kMinimumNumberOfBuckets),
Vladimir Marko5233f932015-09-29 19:01:15 +010043 buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
Vladimir Markof6a35de2016-03-21 12:01:50 +000044 buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
David Brazdil4283aa92016-04-20 14:24:12 +010045 num_entries_(0u) {
David Brazdil6d340c42015-03-03 11:54:54 +000046 // ArenaAllocator returns zeroed memory, so no need to set buckets to null.
47 DCHECK(IsPowerOfTwo(num_buckets_));
Vladimir Marko52d52f52017-10-10 11:04:48 +010048 std::fill_n(buckets_, num_buckets_, nullptr);
David Brazdil6d340c42015-03-03 11:54:54 +000049 buckets_owned_.SetInitialBits(num_buckets_);
50 }
51
52 // Copy constructor. Depending on the load factor, it will either make a deep
53 // copy (all buckets owned) or a shallow one (buckets pointing to the parent).
Vladimir Marko52d52f52017-10-10 11:04:48 +010054 ValueSet(ScopedArenaAllocator* allocator, const ValueSet& other)
David Brazdil6d340c42015-03-03 11:54:54 +000055 : allocator_(allocator),
David Brazdil4283aa92016-04-20 14:24:12 +010056 num_buckets_(other.IdealBucketCount()),
Vladimir Marko5233f932015-09-29 19:01:15 +010057 buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)),
Vladimir Markof6a35de2016-03-21 12:01:50 +000058 buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn),
David Brazdil4283aa92016-04-20 14:24:12 +010059 num_entries_(0u) {
David Brazdil6d340c42015-03-03 11:54:54 +000060 // ArenaAllocator returns zeroed memory, so entries of buckets_ and
Mathieu Chartier2cebb242015-04-21 16:50:40 -070061 // buckets_owned_ are initialized to null and false, respectively.
David Brazdil6d340c42015-03-03 11:54:54 +000062 DCHECK(IsPowerOfTwo(num_buckets_));
Vladimir Marko52d52f52017-10-10 11:04:48 +010063 PopulateFromInternal(other);
David Brazdil4283aa92016-04-20 14:24:12 +010064 }
65
66 // Erases all values in this set and populates it with values from `other`.
67 void PopulateFrom(const ValueSet& other) {
68 if (this == &other) {
69 return;
70 }
Vladimir Marko52d52f52017-10-10 11:04:48 +010071 PopulateFromInternal(other);
David Brazdil4283aa92016-04-20 14:24:12 +010072 }
73
74 // Returns true if `this` has enough buckets so that if `other` is copied into
75 // it, the load factor will not cross the upper threshold.
David Brazdild9743792016-04-21 14:00:15 +010076 // If `exact_match` is set, true is returned only if `this` has the ideal
77 // number of buckets. Larger number of buckets is allowed otherwise.
David Brazdil4283aa92016-04-20 14:24:12 +010078 bool CanHoldCopyOf(const ValueSet& other, bool exact_match) {
79 if (exact_match) {
80 return other.IdealBucketCount() == num_buckets_;
David Brazdil6d340c42015-03-03 11:54:54 +000081 } else {
David Brazdil4283aa92016-04-20 14:24:12 +010082 return other.IdealBucketCount() <= num_buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000083 }
84 }
85
86 // Adds an instruction in the set.
87 void Add(HInstruction* instruction) {
88 DCHECK(Lookup(instruction) == nullptr);
David Brazdil6d340c42015-03-03 11:54:54 +000089 size_t hash_code = HashCode(instruction);
90 size_t index = BucketIndex(hash_code);
91
92 if (!buckets_owned_.IsBitSet(index)) {
93 CloneBucket(index);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000094 }
David Brazdil6d340c42015-03-03 11:54:54 +000095 buckets_[index] = new (allocator_) Node(instruction, hash_code, buckets_[index]);
96 ++num_entries_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000097 }
98
David Brazdil6d340c42015-03-03 11:54:54 +000099 // If in the set, returns an equivalent instruction to the given instruction.
100 // Returns null otherwise.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000101 HInstruction* Lookup(HInstruction* instruction) const {
David Brazdil6d340c42015-03-03 11:54:54 +0000102 size_t hash_code = HashCode(instruction);
103 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000104
David Brazdil6d340c42015-03-03 11:54:54 +0000105 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000106 if (node->GetHashCode() == hash_code) {
David Brazdil6d340c42015-03-03 11:54:54 +0000107 HInstruction* existing = node->GetInstruction();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000108 if (existing->Equals(instruction)) {
109 return existing;
110 }
111 }
112 }
113 return nullptr;
114 }
115
David Brazdil6d340c42015-03-03 11:54:54 +0000116 // Returns whether instruction is in the set.
117 bool Contains(HInstruction* instruction) const {
118 size_t hash_code = HashCode(instruction);
119 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000120
David Brazdil6d340c42015-03-03 11:54:54 +0000121 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
122 if (node->GetInstruction() == instruction) {
123 return true;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000124 }
125 }
David Brazdil6d340c42015-03-03 11:54:54 +0000126 return false;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000127 }
128
David Brazdil6d340c42015-03-03 11:54:54 +0000129 // Removes all instructions in the set affected by the given side effects.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000130 void Kill(SideEffects side_effects) {
David Brazdil6d340c42015-03-03 11:54:54 +0000131 DeleteAllImpureWhich([side_effects](Node* node) {
Aart Bik854a02b2015-07-14 16:07:00 -0700132 return node->GetInstruction()->GetSideEffects().MayDependOn(side_effects);
David Brazdil6d340c42015-03-03 11:54:54 +0000133 });
134 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000135
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000136 void Clear() {
137 num_entries_ = 0;
138 for (size_t i = 0; i < num_buckets_; ++i) {
139 buckets_[i] = nullptr;
140 }
141 buckets_owned_.SetInitialBits(num_buckets_);
142 }
143
David Brazdil6d340c42015-03-03 11:54:54 +0000144 // Updates this set by intersecting with instructions in a predecessor's set.
145 void IntersectWith(ValueSet* predecessor) {
146 if (IsEmpty()) {
147 return;
148 } else if (predecessor->IsEmpty()) {
149 Clear();
150 } else {
151 // Pure instructions do not need to be tested because only impure
152 // instructions can be killed.
153 DeleteAllImpureWhich([predecessor](Node* node) {
154 return !predecessor->Contains(node->GetInstruction());
155 });
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100156 }
157 }
158
David Brazdil6d340c42015-03-03 11:54:54 +0000159 bool IsEmpty() const { return num_entries_ == 0; }
160 size_t GetNumberOfEntries() const { return num_entries_; }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100161
David Brazdil6d340c42015-03-03 11:54:54 +0000162 private:
David Brazdild9743792016-04-21 14:00:15 +0100163 // Copies all entries from `other` to `this`.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100164 void PopulateFromInternal(const ValueSet& other) {
David Brazdil4283aa92016-04-20 14:24:12 +0100165 DCHECK_NE(this, &other);
166 DCHECK_GE(num_buckets_, other.IdealBucketCount());
167
168 if (num_buckets_ == other.num_buckets_) {
169 // Hash table remains the same size. We copy the bucket pointers and leave
170 // all buckets_owned_ bits false.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100171 buckets_owned_.ClearAllBits();
David Brazdil4283aa92016-04-20 14:24:12 +0100172 memcpy(buckets_, other.buckets_, num_buckets_ * sizeof(Node*));
173 } else {
174 // Hash table size changes. We copy and rehash all entries, and set all
175 // buckets_owned_ bits to true.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100176 std::fill_n(buckets_, num_buckets_, nullptr);
David Brazdil4283aa92016-04-20 14:24:12 +0100177 for (size_t i = 0; i < other.num_buckets_; ++i) {
178 for (Node* node = other.buckets_[i]; node != nullptr; node = node->GetNext()) {
179 size_t new_index = BucketIndex(node->GetHashCode());
180 buckets_[new_index] = node->Dup(allocator_, buckets_[new_index]);
181 }
182 }
183 buckets_owned_.SetInitialBits(num_buckets_);
184 }
185
186 num_entries_ = other.num_entries_;
187 }
188
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100189 class Node : public ArenaObject<kArenaAllocGvn> {
David Brazdil6d340c42015-03-03 11:54:54 +0000190 public:
191 Node(HInstruction* instruction, size_t hash_code, Node* next)
192 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
193
194 size_t GetHashCode() const { return hash_code_; }
195 HInstruction* GetInstruction() const { return instruction_; }
196 Node* GetNext() const { return next_; }
197 void SetNext(Node* node) { next_ = node; }
198
Vladimir Marko52d52f52017-10-10 11:04:48 +0100199 Node* Dup(ScopedArenaAllocator* allocator, Node* new_next = nullptr) {
David Brazdil6d340c42015-03-03 11:54:54 +0000200 return new (allocator) Node(instruction_, hash_code_, new_next);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100201 }
202
David Brazdil6d340c42015-03-03 11:54:54 +0000203 private:
204 HInstruction* const instruction_;
205 const size_t hash_code_;
206 Node* next_;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100207
David Brazdil6d340c42015-03-03 11:54:54 +0000208 DISALLOW_COPY_AND_ASSIGN(Node);
209 };
210
211 // Creates our own copy of a bucket that is currently pointing to a parent.
212 // This algorithm can be called while iterating over the bucket because it
213 // preserves the order of entries in the bucket and will return the clone of
214 // the given 'iterator'.
215 Node* CloneBucket(size_t index, Node* iterator = nullptr) {
216 DCHECK(!buckets_owned_.IsBitSet(index));
217 Node* clone_current = nullptr;
218 Node* clone_previous = nullptr;
219 Node* clone_iterator = nullptr;
220 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
221 clone_current = node->Dup(allocator_, nullptr);
222 if (node == iterator) {
223 clone_iterator = clone_current;
224 }
225 if (clone_previous == nullptr) {
226 buckets_[index] = clone_current;
227 } else {
228 clone_previous->SetNext(clone_current);
229 }
230 clone_previous = clone_current;
231 }
232 buckets_owned_.SetBit(index);
233 return clone_iterator;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000234 }
235
David Brazdil6d340c42015-03-03 11:54:54 +0000236 // Iterates over buckets with impure instructions (even indices) and deletes
237 // the ones on which 'cond' returns true.
238 template<typename Functor>
239 void DeleteAllImpureWhich(Functor cond) {
240 for (size_t i = 0; i < num_buckets_; i += 2) {
241 Node* node = buckets_[i];
242 Node* previous = nullptr;
243
244 if (node == nullptr) {
245 continue;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000246 }
David Brazdil6d340c42015-03-03 11:54:54 +0000247
248 if (!buckets_owned_.IsBitSet(i)) {
249 // Bucket is not owned but maybe we won't need to change it at all.
250 // Iterate as long as the entries don't satisfy 'cond'.
251 while (node != nullptr) {
252 if (cond(node)) {
253 // We do need to delete an entry but we do not own the bucket.
254 // Clone the bucket, make sure 'previous' and 'node' point to
255 // the cloned entries and break.
256 previous = CloneBucket(i, previous);
257 node = (previous == nullptr) ? buckets_[i] : previous->GetNext();
258 break;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000259 }
David Brazdil6d340c42015-03-03 11:54:54 +0000260 previous = node;
261 node = node->GetNext();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000262 }
263 }
David Brazdil6d340c42015-03-03 11:54:54 +0000264
265 // By this point we either own the bucket and can start deleting entries,
266 // or we do not own it but no entries matched 'cond'.
267 DCHECK(buckets_owned_.IsBitSet(i) || node == nullptr);
268
269 // We iterate over the remainder of entries and delete those that match
270 // the given condition.
271 while (node != nullptr) {
272 Node* next = node->GetNext();
273 if (cond(node)) {
274 if (previous == nullptr) {
275 buckets_[i] = next;
276 } else {
277 previous->SetNext(next);
278 }
279 } else {
280 previous = node;
281 }
282 node = next;
283 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000284 }
285 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100286
David Brazdil6d340c42015-03-03 11:54:54 +0000287 // Computes a bucket count such that the load factor is reasonable.
288 // This is estimated as (num_entries_ * 1.5) and rounded up to nearest pow2.
289 size_t IdealBucketCount() const {
290 size_t bucket_count = RoundUpToPowerOfTwo(num_entries_ + (num_entries_ >> 1));
291 if (bucket_count > kMinimumNumberOfBuckets) {
292 return bucket_count;
293 } else {
294 return kMinimumNumberOfBuckets;
295 }
296 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000297
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000298 // Generates a hash code for an instruction.
David Brazdil6d340c42015-03-03 11:54:54 +0000299 size_t HashCode(HInstruction* instruction) const {
300 size_t hash_code = instruction->ComputeHashCode();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000301 // Pure instructions are put into odd buckets to speed up deletion. Note that in the
302 // case of irreducible loops, we don't put pure instructions in odd buckets, as we
303 // need to delete them when entering the loop.
Mingyao Yang217eb062017-12-11 15:20:07 -0800304 // ClinitCheck is treated as a pure instruction since it's only executed
305 // once.
306 bool pure = !instruction->GetSideEffects().HasDependencies() ||
307 instruction->IsClinitCheck();
308 if (!pure || instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) {
David Brazdil6d340c42015-03-03 11:54:54 +0000309 return (hash_code << 1) | 0;
310 } else {
311 return (hash_code << 1) | 1;
312 }
313 }
314
315 // Converts a hash code to a bucket index.
316 size_t BucketIndex(size_t hash_code) const {
317 return hash_code & (num_buckets_ - 1);
318 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000319
Vladimir Marko52d52f52017-10-10 11:04:48 +0100320 ScopedArenaAllocator* const allocator_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000321
David Brazdil6d340c42015-03-03 11:54:54 +0000322 // The internal bucket implementation of the set.
323 size_t const num_buckets_;
324 Node** const buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000325
David Brazdil6d340c42015-03-03 11:54:54 +0000326 // Flags specifying which buckets were copied into the set from its parent.
327 // If a flag is not set, the corresponding bucket points to entries in the
328 // parent and must be cloned prior to making changes.
329 ArenaBitVector buckets_owned_;
330
331 // The number of entries in the set.
332 size_t num_entries_;
333
334 static constexpr size_t kMinimumNumberOfBuckets = 8;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000335
336 DISALLOW_COPY_AND_ASSIGN(ValueSet);
337};
338
339/**
340 * Optimization phase that removes redundant instruction.
341 */
342class GlobalValueNumberer : public ValueObject {
343 public:
Vladimir Marko52d52f52017-10-10 11:04:48 +0100344 GlobalValueNumberer(HGraph* graph,
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000345 const SideEffectsAnalysis& side_effects)
346 : graph_(graph),
Vladimir Marko52d52f52017-10-10 11:04:48 +0100347 allocator_(graph->GetArenaStack()),
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000348 side_effects_(side_effects),
Vladimir Marko52d52f52017-10-10 11:04:48 +0100349 sets_(graph->GetBlocks().size(), nullptr, allocator_.Adapter(kArenaAllocGvn)),
David Brazdil4283aa92016-04-20 14:24:12 +0100350 visited_blocks_(
Vladimir Marko52d52f52017-10-10 11:04:48 +0100351 &allocator_, graph->GetBlocks().size(), /* expandable */ false, kArenaAllocGvn) {
352 visited_blocks_.ClearAllBits();
353 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000354
Aart Bik24773202018-04-26 10:28:51 -0700355 bool Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000356
357 private:
358 // Per-block GVN. Will also update the ValueSet of the dominated and
359 // successor blocks.
360 void VisitBasicBlock(HBasicBlock* block);
361
362 HGraph* graph_;
Vladimir Marko52d52f52017-10-10 11:04:48 +0100363 ScopedArenaAllocator allocator_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000364 const SideEffectsAnalysis& side_effects_;
365
David Brazdil4283aa92016-04-20 14:24:12 +0100366 ValueSet* FindSetFor(HBasicBlock* block) const {
367 ValueSet* result = sets_[block->GetBlockId()];
368 DCHECK(result != nullptr) << "Could not find set for block B" << block->GetBlockId();
369 return result;
370 }
371
372 void AbandonSetFor(HBasicBlock* block) {
373 DCHECK(sets_[block->GetBlockId()] != nullptr)
374 << "Block B" << block->GetBlockId() << " expected to have a set";
375 sets_[block->GetBlockId()] = nullptr;
376 }
377
378 // Returns false if the GlobalValueNumberer has already visited all blocks
379 // which may reference `block`.
380 bool WillBeReferencedAgain(HBasicBlock* block) const;
381
382 // Iterates over visited blocks and finds one which has a ValueSet such that:
383 // (a) it will not be referenced in the future, and
384 // (b) it can hold a copy of `reference_set` with a reasonable load factor.
385 HBasicBlock* FindVisitedBlockWithRecyclableSet(HBasicBlock* block,
386 const ValueSet& reference_set) const;
387
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000388 // ValueSet for blocks. Initially null, but for an individual block they
389 // are allocated and populated by the dominator, and updated by all blocks
390 // in the path from the dominator to the block.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100391 ScopedArenaVector<ValueSet*> sets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000392
David Brazdil4283aa92016-04-20 14:24:12 +0100393 // BitVector which serves as a fast-access map from block id to
Roland Levillain5e8d5f02016-10-18 18:03:43 +0100394 // visited/unvisited Boolean.
David Brazdil4283aa92016-04-20 14:24:12 +0100395 ArenaBitVector visited_blocks_;
396
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000397 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
398};
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100399
Aart Bik24773202018-04-26 10:28:51 -0700400bool GlobalValueNumberer::Run() {
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000401 DCHECK(side_effects_.HasRun());
Vladimir Marko52d52f52017-10-10 11:04:48 +0100402 sets_[graph_->GetEntryBlock()->GetBlockId()] = new (&allocator_) ValueSet(&allocator_);
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000403
404 // Use the reverse post order to ensure the non back-edge predecessors of a block are
405 // visited before the block itself.
Vladimir Marko2c45bc92016-10-25 16:54:12 +0100406 for (HBasicBlock* block : graph_->GetReversePostOrder()) {
407 VisitBasicBlock(block);
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000408 }
Aart Bik24773202018-04-26 10:28:51 -0700409 return true;
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000410}
411
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100412void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000413 ValueSet* set = nullptr;
David Brazdil4283aa92016-04-20 14:24:12 +0100414
Vladimir Marko60584552015-09-03 13:35:12 +0000415 const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors();
416 if (predecessors.size() == 0 || predecessors[0]->IsEntryBlock()) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000417 // The entry block should only accumulate constant instructions, and
418 // the builder puts constants only in the entry block.
419 // Therefore, there is no need to propagate the value set to the next block.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100420 set = new (&allocator_) ValueSet(&allocator_);
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000421 } else {
422 HBasicBlock* dominator = block->GetDominator();
David Brazdil4283aa92016-04-20 14:24:12 +0100423 ValueSet* dominator_set = FindSetFor(dominator);
424
Vladimir Marko60584552015-09-03 13:35:12 +0000425 if (dominator->GetSuccessors().size() == 1) {
David Brazdil4283aa92016-04-20 14:24:12 +0100426 // `block` is a direct successor of its dominator. No need to clone the
427 // dominator's set, `block` can take over its ownership including its buckets.
428 DCHECK_EQ(dominator->GetSingleSuccessor(), block);
429 AbandonSetFor(dominator);
David Brazdil6d340c42015-03-03 11:54:54 +0000430 set = dominator_set;
431 } else {
David Brazdild9743792016-04-21 14:00:15 +0100432 // Try to find a basic block which will never be referenced again and whose
433 // ValueSet can therefore be recycled. We will need to copy `dominator_set`
434 // into the recycled set, so we pass `dominator_set` as a reference for size.
David Brazdil4283aa92016-04-20 14:24:12 +0100435 HBasicBlock* recyclable = FindVisitedBlockWithRecyclableSet(block, *dominator_set);
436 if (recyclable == nullptr) {
David Brazdild9743792016-04-21 14:00:15 +0100437 // No block with a suitable ValueSet found. Allocate a new one and
David Brazdil4283aa92016-04-20 14:24:12 +0100438 // copy `dominator_set` into it.
Vladimir Marko52d52f52017-10-10 11:04:48 +0100439 set = new (&allocator_) ValueSet(&allocator_, *dominator_set);
David Brazdil4283aa92016-04-20 14:24:12 +0100440 } else {
441 // Block with a recyclable ValueSet found. Clone `dominator_set` into it.
442 set = FindSetFor(recyclable);
443 AbandonSetFor(recyclable);
444 set->PopulateFrom(*dominator_set);
445 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100446 }
David Brazdil4283aa92016-04-20 14:24:12 +0100447
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000448 if (!set->IsEmpty()) {
449 if (block->IsLoopHeader()) {
Nicolas Geoffray77ce6432016-05-10 14:35:34 +0100450 if (block->GetLoopInformation()->ContainsIrreducibleLoop()) {
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000451 // To satisfy our linear scan algorithm, no instruction should flow in an irreducible
Nicolas Geoffray77ce6432016-05-10 14:35:34 +0100452 // loop header. We clear the set at entry of irreducible loops and any loop containing
453 // an irreducible loop, as in both cases, GVN can extend the liveness of an instruction
454 // across the irreducible loop.
455 // Note that, if we're not compiling OSR, we could still do GVN and introduce
456 // phis at irreducible loop headers. We decided it was not worth the complexity.
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000457 set->Clear();
458 } else {
Nicolas Geoffray77ce6432016-05-10 14:35:34 +0100459 DCHECK(!block->GetLoopInformation()->IsIrreducible());
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000460 DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
461 set->Kill(side_effects_.GetLoopEffects(block));
462 }
Vladimir Marko60584552015-09-03 13:35:12 +0000463 } else if (predecessors.size() > 1) {
464 for (HBasicBlock* predecessor : predecessors) {
David Brazdild9743792016-04-21 14:00:15 +0100465 set->IntersectWith(FindSetFor(predecessor));
466 if (set->IsEmpty()) {
467 break;
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000468 }
469 }
470 }
471 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100472 }
473
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100474 sets_[block->GetBlockId()] = set;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100475
476 HInstruction* current = block->GetFirstInstruction();
477 while (current != nullptr) {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100478 // Save the next instruction in case `current` is removed from the graph.
479 HInstruction* next = current->GetNext();
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000480 // Do not kill the set with the side effects of the instruction just now: if
481 // the instruction is GVN'ed, we don't need to kill.
Artem Serov4d277ba2018-06-05 20:54:42 +0100482 //
483 // BoundType is a special case example of an instruction which shouldn't be moved but can be
484 // GVN'ed.
485 if (current->CanBeMoved() || current->IsBoundType()) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800486 if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
487 // For commutative ops, (x op y) will be treated the same as (y op x)
488 // after fixed ordering.
489 current->AsBinaryOperation()->OrderInputs();
490 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100491 HInstruction* existing = set->Lookup(current);
492 if (existing != nullptr) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800493 // This replacement doesn't make more OrderInputs() necessary since
494 // current is either used by an instruction that it dominates,
495 // which hasn't been visited yet due to the order we visit instructions.
496 // Or current is used by a phi, and we don't do OrderInputs() on a phi anyway.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100497 current->ReplaceWith(existing);
498 current->GetBlock()->RemoveInstruction(current);
499 } else {
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000500 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100501 set->Add(current);
502 }
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000503 } else {
504 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100505 }
506 current = next;
507 }
David Brazdil4283aa92016-04-20 14:24:12 +0100508
509 visited_blocks_.SetBit(block->GetBlockId());
510}
511
512bool GlobalValueNumberer::WillBeReferencedAgain(HBasicBlock* block) const {
513 DCHECK(visited_blocks_.IsBitSet(block->GetBlockId()));
514
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100515 for (const HBasicBlock* dominated_block : block->GetDominatedBlocks()) {
David Brazdil4283aa92016-04-20 14:24:12 +0100516 if (!visited_blocks_.IsBitSet(dominated_block->GetBlockId())) {
517 return true;
518 }
519 }
520
Vladimir Marko7d157fc2017-05-10 16:29:23 +0100521 for (const HBasicBlock* successor : block->GetSuccessors()) {
David Brazdil4283aa92016-04-20 14:24:12 +0100522 if (!visited_blocks_.IsBitSet(successor->GetBlockId())) {
523 return true;
524 }
525 }
526
527 return false;
528}
529
530HBasicBlock* GlobalValueNumberer::FindVisitedBlockWithRecyclableSet(
531 HBasicBlock* block, const ValueSet& reference_set) const {
532 HBasicBlock* secondary_match = nullptr;
533
534 for (size_t block_id : visited_blocks_.Indexes()) {
535 ValueSet* current_set = sets_[block_id];
536 if (current_set == nullptr) {
537 // Set was already recycled.
538 continue;
539 }
540
541 HBasicBlock* current_block = block->GetGraph()->GetBlocks()[block_id];
542
543 // We test if `current_set` has enough buckets to store a copy of
544 // `reference_set` with a reasonable load factor. If we find a set whose
545 // number of buckets matches perfectly, we return right away. If we find one
546 // that is larger, we return it if no perfectly-matching set is found.
547 // Note that we defer testing WillBeReferencedAgain until all other criteria
548 // have been satisfied because it might be expensive.
549 if (current_set->CanHoldCopyOf(reference_set, /* exact_match */ true)) {
550 if (!WillBeReferencedAgain(current_block)) {
551 return current_block;
552 }
553 } else if (secondary_match == nullptr &&
554 current_set->CanHoldCopyOf(reference_set, /* exact_match */ false)) {
555 if (!WillBeReferencedAgain(current_block)) {
556 secondary_match = current_block;
557 }
558 }
559 }
560
561 return secondary_match;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100562}
563
Aart Bik24773202018-04-26 10:28:51 -0700564bool GVNOptimization::Run() {
Vladimir Marko52d52f52017-10-10 11:04:48 +0100565 GlobalValueNumberer gvn(graph_, side_effects_);
Aart Bik24773202018-04-26 10:28:51 -0700566 return gvn.Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000567}
568
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100569} // namespace art