blob: f7eb2adc6cddd40858e605ce987bb76f6fb9ec0c [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 Brazdil6d340c42015-03-03 11:54:54 +000044 num_entries_(0) {
45 // 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).
52 ValueSet(ArenaAllocator* allocator, const ValueSet& to_copy)
53 : allocator_(allocator),
54 num_buckets_(to_copy.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 Brazdil6d340c42015-03-03 11:54:54 +000057 num_entries_(to_copy.num_entries_) {
58 // 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_));
61 if (num_buckets_ == to_copy.num_buckets_) {
62 // Hash table remains the same size. We copy the bucket pointers and leave
63 // all buckets_owned_ bits false.
64 memcpy(buckets_, to_copy.buckets_, num_buckets_ * sizeof(Node*));
65 } else {
66 // Hash table size changes. We copy and rehash all entries, and set all
67 // buckets_owned_ bits to true.
68 for (size_t i = 0; i < to_copy.num_buckets_; ++i) {
69 for (Node* node = to_copy.buckets_[i]; node != nullptr; node = node->GetNext()) {
70 size_t new_index = BucketIndex(node->GetHashCode());
71 buckets_[new_index] = node->Dup(allocator_, buckets_[new_index]);
72 }
73 }
74 buckets_owned_.SetInitialBits(num_buckets_);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000075 }
76 }
77
78 // Adds an instruction in the set.
79 void Add(HInstruction* instruction) {
80 DCHECK(Lookup(instruction) == nullptr);
David Brazdil6d340c42015-03-03 11:54:54 +000081 size_t hash_code = HashCode(instruction);
82 size_t index = BucketIndex(hash_code);
83
84 if (!buckets_owned_.IsBitSet(index)) {
85 CloneBucket(index);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000086 }
David Brazdil6d340c42015-03-03 11:54:54 +000087 buckets_[index] = new (allocator_) Node(instruction, hash_code, buckets_[index]);
88 ++num_entries_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000089 }
90
David Brazdil6d340c42015-03-03 11:54:54 +000091 // If in the set, returns an equivalent instruction to the given instruction.
92 // Returns null otherwise.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000093 HInstruction* Lookup(HInstruction* instruction) const {
David Brazdil6d340c42015-03-03 11:54:54 +000094 size_t hash_code = HashCode(instruction);
95 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000096
David Brazdil6d340c42015-03-03 11:54:54 +000097 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000098 if (node->GetHashCode() == hash_code) {
David Brazdil6d340c42015-03-03 11:54:54 +000099 HInstruction* existing = node->GetInstruction();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000100 if (existing->Equals(instruction)) {
101 return existing;
102 }
103 }
104 }
105 return nullptr;
106 }
107
David Brazdil6d340c42015-03-03 11:54:54 +0000108 // Returns whether instruction is in the set.
109 bool Contains(HInstruction* instruction) const {
110 size_t hash_code = HashCode(instruction);
111 size_t index = BucketIndex(hash_code);
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000112
David Brazdil6d340c42015-03-03 11:54:54 +0000113 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
114 if (node->GetInstruction() == instruction) {
115 return true;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000116 }
117 }
David Brazdil6d340c42015-03-03 11:54:54 +0000118 return false;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000119 }
120
David Brazdil6d340c42015-03-03 11:54:54 +0000121 // Removes all instructions in the set affected by the given side effects.
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000122 void Kill(SideEffects side_effects) {
David Brazdil6d340c42015-03-03 11:54:54 +0000123 DeleteAllImpureWhich([side_effects](Node* node) {
Aart Bik854a02b2015-07-14 16:07:00 -0700124 return node->GetInstruction()->GetSideEffects().MayDependOn(side_effects);
David Brazdil6d340c42015-03-03 11:54:54 +0000125 });
126 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000127
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000128 void Clear() {
129 num_entries_ = 0;
130 for (size_t i = 0; i < num_buckets_; ++i) {
131 buckets_[i] = nullptr;
132 }
133 buckets_owned_.SetInitialBits(num_buckets_);
134 }
135
David Brazdil6d340c42015-03-03 11:54:54 +0000136 // Updates this set by intersecting with instructions in a predecessor's set.
137 void IntersectWith(ValueSet* predecessor) {
138 if (IsEmpty()) {
139 return;
140 } else if (predecessor->IsEmpty()) {
141 Clear();
142 } else {
143 // Pure instructions do not need to be tested because only impure
144 // instructions can be killed.
145 DeleteAllImpureWhich([predecessor](Node* node) {
146 return !predecessor->Contains(node->GetInstruction());
147 });
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100148 }
149 }
150
David Brazdil6d340c42015-03-03 11:54:54 +0000151 bool IsEmpty() const { return num_entries_ == 0; }
152 size_t GetNumberOfEntries() const { return num_entries_; }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100153
David Brazdil6d340c42015-03-03 11:54:54 +0000154 private:
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100155 class Node : public ArenaObject<kArenaAllocGvn> {
David Brazdil6d340c42015-03-03 11:54:54 +0000156 public:
157 Node(HInstruction* instruction, size_t hash_code, Node* next)
158 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
159
160 size_t GetHashCode() const { return hash_code_; }
161 HInstruction* GetInstruction() const { return instruction_; }
162 Node* GetNext() const { return next_; }
163 void SetNext(Node* node) { next_ = node; }
164
165 Node* Dup(ArenaAllocator* allocator, Node* new_next = nullptr) {
166 return new (allocator) Node(instruction_, hash_code_, new_next);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100167 }
168
David Brazdil6d340c42015-03-03 11:54:54 +0000169 private:
170 HInstruction* const instruction_;
171 const size_t hash_code_;
172 Node* next_;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100173
David Brazdil6d340c42015-03-03 11:54:54 +0000174 DISALLOW_COPY_AND_ASSIGN(Node);
175 };
176
177 // Creates our own copy of a bucket that is currently pointing to a parent.
178 // This algorithm can be called while iterating over the bucket because it
179 // preserves the order of entries in the bucket and will return the clone of
180 // the given 'iterator'.
181 Node* CloneBucket(size_t index, Node* iterator = nullptr) {
182 DCHECK(!buckets_owned_.IsBitSet(index));
183 Node* clone_current = nullptr;
184 Node* clone_previous = nullptr;
185 Node* clone_iterator = nullptr;
186 for (Node* node = buckets_[index]; node != nullptr; node = node->GetNext()) {
187 clone_current = node->Dup(allocator_, nullptr);
188 if (node == iterator) {
189 clone_iterator = clone_current;
190 }
191 if (clone_previous == nullptr) {
192 buckets_[index] = clone_current;
193 } else {
194 clone_previous->SetNext(clone_current);
195 }
196 clone_previous = clone_current;
197 }
198 buckets_owned_.SetBit(index);
199 return clone_iterator;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000200 }
201
David Brazdil6d340c42015-03-03 11:54:54 +0000202 // Iterates over buckets with impure instructions (even indices) and deletes
203 // the ones on which 'cond' returns true.
204 template<typename Functor>
205 void DeleteAllImpureWhich(Functor cond) {
206 for (size_t i = 0; i < num_buckets_; i += 2) {
207 Node* node = buckets_[i];
208 Node* previous = nullptr;
209
210 if (node == nullptr) {
211 continue;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000212 }
David Brazdil6d340c42015-03-03 11:54:54 +0000213
214 if (!buckets_owned_.IsBitSet(i)) {
215 // Bucket is not owned but maybe we won't need to change it at all.
216 // Iterate as long as the entries don't satisfy 'cond'.
217 while (node != nullptr) {
218 if (cond(node)) {
219 // We do need to delete an entry but we do not own the bucket.
220 // Clone the bucket, make sure 'previous' and 'node' point to
221 // the cloned entries and break.
222 previous = CloneBucket(i, previous);
223 node = (previous == nullptr) ? buckets_[i] : previous->GetNext();
224 break;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000225 }
David Brazdil6d340c42015-03-03 11:54:54 +0000226 previous = node;
227 node = node->GetNext();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000228 }
229 }
David Brazdil6d340c42015-03-03 11:54:54 +0000230
231 // By this point we either own the bucket and can start deleting entries,
232 // or we do not own it but no entries matched 'cond'.
233 DCHECK(buckets_owned_.IsBitSet(i) || node == nullptr);
234
235 // We iterate over the remainder of entries and delete those that match
236 // the given condition.
237 while (node != nullptr) {
238 Node* next = node->GetNext();
239 if (cond(node)) {
240 if (previous == nullptr) {
241 buckets_[i] = next;
242 } else {
243 previous->SetNext(next);
244 }
245 } else {
246 previous = node;
247 }
248 node = next;
249 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000250 }
251 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100252
David Brazdil6d340c42015-03-03 11:54:54 +0000253 // Computes a bucket count such that the load factor is reasonable.
254 // This is estimated as (num_entries_ * 1.5) and rounded up to nearest pow2.
255 size_t IdealBucketCount() const {
256 size_t bucket_count = RoundUpToPowerOfTwo(num_entries_ + (num_entries_ >> 1));
257 if (bucket_count > kMinimumNumberOfBuckets) {
258 return bucket_count;
259 } else {
260 return kMinimumNumberOfBuckets;
261 }
262 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000263
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000264 // Generates a hash code for an instruction.
David Brazdil6d340c42015-03-03 11:54:54 +0000265 size_t HashCode(HInstruction* instruction) const {
266 size_t hash_code = instruction->ComputeHashCode();
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000267 // Pure instructions are put into odd buckets to speed up deletion. Note that in the
268 // case of irreducible loops, we don't put pure instructions in odd buckets, as we
269 // need to delete them when entering the loop.
270 if (instruction->GetSideEffects().HasDependencies() ||
271 instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) {
David Brazdil6d340c42015-03-03 11:54:54 +0000272 return (hash_code << 1) | 0;
273 } else {
274 return (hash_code << 1) | 1;
275 }
276 }
277
278 // Converts a hash code to a bucket index.
279 size_t BucketIndex(size_t hash_code) const {
280 return hash_code & (num_buckets_ - 1);
281 }
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000282
283 ArenaAllocator* const allocator_;
284
David Brazdil6d340c42015-03-03 11:54:54 +0000285 // The internal bucket implementation of the set.
286 size_t const num_buckets_;
287 Node** const buckets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000288
David Brazdil6d340c42015-03-03 11:54:54 +0000289 // Flags specifying which buckets were copied into the set from its parent.
290 // If a flag is not set, the corresponding bucket points to entries in the
291 // parent and must be cloned prior to making changes.
292 ArenaBitVector buckets_owned_;
293
294 // The number of entries in the set.
295 size_t num_entries_;
296
297 static constexpr size_t kMinimumNumberOfBuckets = 8;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000298
299 DISALLOW_COPY_AND_ASSIGN(ValueSet);
300};
301
302/**
303 * Optimization phase that removes redundant instruction.
304 */
305class GlobalValueNumberer : public ValueObject {
306 public:
307 GlobalValueNumberer(ArenaAllocator* allocator,
308 HGraph* graph,
309 const SideEffectsAnalysis& side_effects)
310 : graph_(graph),
311 allocator_(allocator),
312 side_effects_(side_effects),
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100313 sets_(graph->GetBlocks().size(), nullptr, allocator->Adapter(kArenaAllocGvn)) {}
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000314
315 void Run();
316
317 private:
318 // Per-block GVN. Will also update the ValueSet of the dominated and
319 // successor blocks.
320 void VisitBasicBlock(HBasicBlock* block);
321
322 HGraph* graph_;
323 ArenaAllocator* const allocator_;
324 const SideEffectsAnalysis& side_effects_;
325
326 // ValueSet for blocks. Initially null, but for an individual block they
327 // are allocated and populated by the dominator, and updated by all blocks
328 // in the path from the dominator to the block.
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100329 ArenaVector<ValueSet*> sets_;
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000330
331 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
332};
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100333
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000334void GlobalValueNumberer::Run() {
335 DCHECK(side_effects_.HasRun());
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100336 sets_[graph_->GetEntryBlock()->GetBlockId()] = new (allocator_) ValueSet(allocator_);
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000337
338 // Use the reverse post order to ensure the non back-edge predecessors of a block are
339 // visited before the block itself.
340 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
341 VisitBasicBlock(it.Current());
342 }
343}
344
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100345void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000346 ValueSet* set = nullptr;
Vladimir Marko60584552015-09-03 13:35:12 +0000347 const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors();
348 if (predecessors.size() == 0 || predecessors[0]->IsEntryBlock()) {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000349 // The entry block should only accumulate constant instructions, and
350 // the builder puts constants only in the entry block.
351 // Therefore, there is no need to propagate the value set to the next block.
352 set = new (allocator_) ValueSet(allocator_);
353 } else {
354 HBasicBlock* dominator = block->GetDominator();
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100355 ValueSet* dominator_set = sets_[dominator->GetBlockId()];
Vladimir Marko60584552015-09-03 13:35:12 +0000356 if (dominator->GetSuccessors().size() == 1) {
Vladimir Markoec7802a2015-10-01 20:57:57 +0100357 DCHECK_EQ(dominator->GetSuccessors()[0], block);
David Brazdil6d340c42015-03-03 11:54:54 +0000358 set = dominator_set;
359 } else {
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000360 // We have to copy if the dominator has other successors, or `block` is not a successor
361 // of the dominator.
David Brazdil6d340c42015-03-03 11:54:54 +0000362 set = new (allocator_) ValueSet(allocator_, *dominator_set);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100363 }
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000364 if (!set->IsEmpty()) {
365 if (block->IsLoopHeader()) {
Nicolas Geoffray15bd2282016-01-05 15:55:41 +0000366 if (block->GetLoopInformation()->IsIrreducible()) {
367 // To satisfy our linear scan algorithm, no instruction should flow in an irreducible
368 // loop header.
369 set->Clear();
370 } else {
371 DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
372 set->Kill(side_effects_.GetLoopEffects(block));
373 }
Vladimir Marko60584552015-09-03 13:35:12 +0000374 } else if (predecessors.size() > 1) {
375 for (HBasicBlock* predecessor : predecessors) {
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100376 set->IntersectWith(sets_[predecessor->GetBlockId()]);
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000377 if (set->IsEmpty()) {
378 break;
379 }
380 }
381 }
382 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100383 }
384
Vladimir Marko2aaa4b52015-09-17 17:03:26 +0100385 sets_[block->GetBlockId()] = set;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100386
387 HInstruction* current = block->GetFirstInstruction();
388 while (current != nullptr) {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100389 // Save the next instruction in case `current` is removed from the graph.
390 HInstruction* next = current->GetNext();
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000391 // Do not kill the set with the side effects of the instruction just now: if
392 // the instruction is GVN'ed, we don't need to kill.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100393 if (current->CanBeMoved()) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800394 if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
395 // For commutative ops, (x op y) will be treated the same as (y op x)
396 // after fixed ordering.
397 current->AsBinaryOperation()->OrderInputs();
398 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100399 HInstruction* existing = set->Lookup(current);
400 if (existing != nullptr) {
Mingyao Yangdc5ac732015-02-25 11:28:05 -0800401 // This replacement doesn't make more OrderInputs() necessary since
402 // current is either used by an instruction that it dominates,
403 // which hasn't been visited yet due to the order we visit instructions.
404 // Or current is used by a phi, and we don't do OrderInputs() on a phi anyway.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100405 current->ReplaceWith(existing);
406 current->GetBlock()->RemoveInstruction(current);
407 } else {
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000408 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100409 set->Add(current);
410 }
Nicolas Geoffray729645a2015-11-19 13:29:02 +0000411 } else {
412 set->Kill(current->GetSideEffects());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100413 }
414 current = next;
415 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100416}
417
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000418void GVNOptimization::Run() {
419 GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
420 gvn.Run();
421}
422
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100423} // namespace art