blob: 81f2c3fa87ed9f95b8b8c9ffa79f8de523342950 [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#ifndef ART_COMPILER_OPTIMIZING_GVN_H_
18#define ART_COMPILER_OPTIMIZING_GVN_H_
19
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010020#include "nodes.h"
Nicolas Geoffray5e6916c2014-11-18 16:53:35 +000021#include "optimization.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010022
23namespace art {
24
25/**
26 * A node in the collision list of a ValueSet. Encodes the instruction,
27 * the hash code, and the next node in the collision list.
28 */
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070029class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010030 public:
31 ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
32 : instruction_(instruction), hash_code_(hash_code), next_(next) {}
33
34 size_t GetHashCode() const { return hash_code_; }
35 HInstruction* GetInstruction() const { return instruction_; }
36 ValueSetNode* GetNext() const { return next_; }
37 void SetNext(ValueSetNode* node) { next_ = node; }
38
39 private:
40 HInstruction* const instruction_;
41 const size_t hash_code_;
42 ValueSetNode* next_;
43
44 DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
45};
46
47/**
48 * A ValueSet holds instructions that can replace other instructions. It is updated
49 * through the `Add` method, and the `Kill` method. The `Kill` method removes
50 * instructions that are affected by the given side effect.
51 *
52 * The `Lookup` method returns an equivalent instruction to the given instruction
53 * if there is one in the set. In GVN, we would say those instructions have the
54 * same "number".
55 */
Ian Rogers6a3c1fc2014-10-31 00:33:20 -070056class ValueSet : public ArenaObject<kArenaAllocMisc> {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010057 public:
58 explicit ValueSet(ArenaAllocator* allocator)
59 : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
60 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
61 table_[i] = nullptr;
62 }
63 }
64
65 // Adds an instruction in the set.
66 void Add(HInstruction* instruction) {
67 DCHECK(Lookup(instruction) == nullptr);
68 size_t hash_code = instruction->ComputeHashCode();
69 size_t index = hash_code % kDefaultNumberOfEntries;
70 if (table_[index] == nullptr) {
71 table_[index] = instruction;
72 } else {
73 collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
74 }
75 ++number_of_entries_;
76 }
77
78 // If in the set, returns an equivalent instruction to the given instruction. Returns
79 // null otherwise.
80 HInstruction* Lookup(HInstruction* instruction) const {
81 size_t hash_code = instruction->ComputeHashCode();
82 size_t index = hash_code % kDefaultNumberOfEntries;
83 HInstruction* existing = table_[index];
84 if (existing != nullptr && existing->Equals(instruction)) {
85 return existing;
86 }
87
88 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
89 if (node->GetHashCode() == hash_code) {
90 existing = node->GetInstruction();
91 if (existing->Equals(instruction)) {
92 return existing;
93 }
94 }
95 }
96 return nullptr;
97 }
98
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +000099 // Returns whether `instruction` is in the set.
100 HInstruction* IdentityLookup(HInstruction* instruction) const {
101 size_t hash_code = instruction->ComputeHashCode();
102 size_t index = hash_code % kDefaultNumberOfEntries;
103 HInstruction* existing = table_[index];
104 if (existing != nullptr && existing == instruction) {
105 return existing;
106 }
107
108 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
109 if (node->GetHashCode() == hash_code) {
110 existing = node->GetInstruction();
111 if (existing == instruction) {
112 return existing;
113 }
114 }
115 }
116 return nullptr;
117 }
118
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100119 // Removes all instructions in the set that are affected by the given side effects.
120 void Kill(SideEffects side_effects) {
121 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
122 HInstruction* instruction = table_[i];
123 if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
124 table_[i] = nullptr;
125 --number_of_entries_;
126 }
127 }
128
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000129 for (ValueSetNode* current = collisions_, *previous = nullptr;
130 current != nullptr;
131 current = current->GetNext()) {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100132 HInstruction* instruction = current->GetInstruction();
133 if (instruction->GetSideEffects().DependsOn(side_effects)) {
134 if (previous == nullptr) {
135 collisions_ = current->GetNext();
136 } else {
137 previous->SetNext(current->GetNext());
138 }
139 --number_of_entries_;
140 } else {
141 previous = current;
142 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100143 }
144 }
145
146 // Returns a copy of this set.
147 ValueSet* Copy() const {
148 ValueSet* copy = new (allocator_) ValueSet(allocator_);
149
150 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
151 copy->table_[i] = table_[i];
152 }
153
154 // Note that the order will be inverted in the copy. This is fine, as the order is not
155 // relevant for a ValueSet.
156 for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
157 copy->collisions_ = new (allocator_) ValueSetNode(
158 node->GetInstruction(), node->GetHashCode(), copy->collisions_);
159 }
160
161 copy->number_of_entries_ = number_of_entries_;
162 return copy;
163 }
164
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000165 void Clear() {
166 number_of_entries_ = 0;
167 collisions_ = nullptr;
168 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
169 table_[i] = nullptr;
170 }
171 }
172
173 // Update this `ValueSet` by intersecting with instructions in `other`.
174 void IntersectionWith(ValueSet* other) {
175 if (IsEmpty()) {
176 return;
177 } else if (other->IsEmpty()) {
178 Clear();
179 } else {
180 for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
181 if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
182 --number_of_entries_;
183 table_[i] = nullptr;
184 }
185 }
186 for (ValueSetNode* current = collisions_, *previous = nullptr;
187 current != nullptr;
188 current = current->GetNext()) {
189 if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
190 if (previous == nullptr) {
191 collisions_ = current->GetNext();
192 } else {
193 previous->SetNext(current->GetNext());
194 }
195 --number_of_entries_;
196 } else {
197 previous = current;
198 }
199 }
200 }
201 }
202
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100203 bool IsEmpty() const { return number_of_entries_ == 0; }
204 size_t GetNumberOfEntries() const { return number_of_entries_; }
205
206 private:
207 static constexpr size_t kDefaultNumberOfEntries = 8;
208
209 ArenaAllocator* const allocator_;
210
211 // The number of entries in the set.
212 size_t number_of_entries_;
213
214 // The internal implementation of the set. It uses a combination of a hash code based
215 // fixed-size list, and a linked list to handle hash code collisions.
216 // TODO: Tune the fixed size list original size, and support growing it.
217 ValueSetNode* collisions_;
218 HInstruction* table_[kDefaultNumberOfEntries];
219
220 DISALLOW_COPY_AND_ASSIGN(ValueSet);
221};
222
223/**
224 * Optimization phase that removes redundant instruction.
225 */
Nicolas Geoffray31596742014-11-24 15:28:45 +0000226class GlobalValueNumberer : public ValueObject {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100227 public:
228 GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
Nicolas Geoffray31596742014-11-24 15:28:45 +0000229 : graph_(graph),
Nicolas Geoffray5e6916c2014-11-18 16:53:35 +0000230 allocator_(allocator),
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100231 block_effects_(allocator, graph->GetBlocks().Size()),
232 loop_effects_(allocator, graph->GetBlocks().Size()),
Nicolas Geoffraydbca6fa2014-11-27 12:01:59 +0000233 sets_(allocator, graph->GetBlocks().Size()) {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100234 size_t number_of_blocks = graph->GetBlocks().Size();
235 block_effects_.SetSize(number_of_blocks);
236 loop_effects_.SetSize(number_of_blocks);
237 sets_.SetSize(number_of_blocks);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100238
239 for (size_t i = 0; i < number_of_blocks; ++i) {
240 block_effects_.Put(i, SideEffects::None());
241 loop_effects_.Put(i, SideEffects::None());
242 }
243 }
244
Nicolas Geoffray31596742014-11-24 15:28:45 +0000245 void Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100246
247 private:
248 // Per-block GVN. Will also update the ValueSet of the dominated and
249 // successor blocks.
250 void VisitBasicBlock(HBasicBlock* block);
251
252 // Compute side effects of individual blocks and loops. The GVN algorithm
253 // will use these side effects to update the ValueSet of individual blocks.
254 void ComputeSideEffects();
255
256 void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
257 SideEffects GetLoopEffects(HBasicBlock* block) const;
258 SideEffects GetBlockEffects(HBasicBlock* block) const;
259
Nicolas Geoffray31596742014-11-24 15:28:45 +0000260 HGraph* graph_;
261
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100262 ArenaAllocator* const allocator_;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100263
264 // Side effects of individual blocks, that is the union of the side effects
265 // of the instructions in the block.
266 GrowableArray<SideEffects> block_effects_;
267
268 // Side effects of loops, that is the union of the side effects of the
269 // blocks contained in that loop.
270 GrowableArray<SideEffects> loop_effects_;
271
272 // ValueSet for blocks. Initially null, but for an individual block they
273 // are allocated and populated by the dominator, and updated by all blocks
274 // in the path from the dominator to the block.
275 GrowableArray<ValueSet*> sets_;
276
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700277 ART_FRIEND_TEST(GVNTest, LoopSideEffects);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100278 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
279};
280
Nicolas Geoffray31596742014-11-24 15:28:45 +0000281class GVNOptimization : public HOptimization {
282 public:
283 explicit GVNOptimization(HGraph* graph) : HOptimization(graph, true, "GVN") {}
284
285 void Run() OVERRIDE {
286 GlobalValueNumberer gvn(graph_->GetArena(), graph_);
287 gvn.Run();
288 }
289
290 private:
291 DISALLOW_COPY_AND_ASSIGN(GVNOptimization);
292};
293
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100294} // namespace art
295
296#endif // ART_COMPILER_OPTIMIZING_GVN_H_