blob: 2e385115322381a88a5264fab89b45a22f606f58 [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
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000223class SideEffectsAnalysis : public HOptimization {
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100224 public:
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000225 explicit SideEffectsAnalysis(HGraph* graph)
226 : HOptimization(graph, true, "SideEffects"),
227 graph_(graph),
228 block_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()),
229 loop_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()) {}
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100230
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100231 SideEffects GetLoopEffects(HBasicBlock* block) const;
232 SideEffects GetBlockEffects(HBasicBlock* block) const;
233
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000234 // Compute side effects of individual blocks and loops.
235 void Run();
236
237 bool HasRun() const { return has_run_; }
238
239 private:
240 void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
241
Nicolas Geoffray31596742014-11-24 15:28:45 +0000242 HGraph* graph_;
243
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000244 // Checked in debug build, to ensure the pass has been run prior to
245 // running a pass that depends on it.
246 bool has_run_ = false;
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100247
248 // Side effects of individual blocks, that is the union of the side effects
249 // of the instructions in the block.
250 GrowableArray<SideEffects> block_effects_;
251
252 // Side effects of loops, that is the union of the side effects of the
253 // blocks contained in that loop.
254 GrowableArray<SideEffects> loop_effects_;
255
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000256 ART_FRIEND_TEST(GVNTest, LoopSideEffects);
257 DISALLOW_COPY_AND_ASSIGN(SideEffectsAnalysis);
258};
259
260/**
261 * Optimization phase that removes redundant instruction.
262 */
263class GlobalValueNumberer : public ValueObject {
264 public:
265 GlobalValueNumberer(ArenaAllocator* allocator,
266 HGraph* graph,
267 const SideEffectsAnalysis& side_effects)
268 : graph_(graph),
269 allocator_(allocator),
270 side_effects_(side_effects),
271 sets_(allocator, graph->GetBlocks().Size(), nullptr) {}
272
273 void Run();
274
275 private:
276 // Per-block GVN. Will also update the ValueSet of the dominated and
277 // successor blocks.
278 void VisitBasicBlock(HBasicBlock* block);
279
280 HGraph* graph_;
281 ArenaAllocator* const allocator_;
282 const SideEffectsAnalysis& side_effects_;
283
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100284 // ValueSet for blocks. Initially null, but for an individual block they
285 // are allocated and populated by the dominator, and updated by all blocks
286 // in the path from the dominator to the block.
287 GrowableArray<ValueSet*> sets_;
288
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100289 DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
290};
291
Nicolas Geoffray31596742014-11-24 15:28:45 +0000292class GVNOptimization : public HOptimization {
293 public:
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000294 GVNOptimization(HGraph* graph, const SideEffectsAnalysis& side_effects)
295 : HOptimization(graph, true, "GVN"), side_effects_(side_effects) {}
Nicolas Geoffray31596742014-11-24 15:28:45 +0000296
297 void Run() OVERRIDE {
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000298 GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
Nicolas Geoffray31596742014-11-24 15:28:45 +0000299 gvn.Run();
300 }
301
302 private:
Nicolas Geoffray86dde162015-01-26 12:54:33 +0000303 const SideEffectsAnalysis& side_effects_;
304
Nicolas Geoffray31596742014-11-24 15:28:45 +0000305 DISALLOW_COPY_AND_ASSIGN(GVNOptimization);
306};
307
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100308} // namespace art
309
310#endif // ART_COMPILER_OPTIMIZING_GVN_H_