Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: src/compiler/load-elimination.cc

Issue 2164253002: [turbofan] New GraphReducer based LoadElimination. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Address comments. REBASE. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler/load-elimination.h" 5 #include "src/compiler/load-elimination.h"
6 6
7 #include "src/compiler/graph.h"
8 #include "src/compiler/node-marker.h"
9 #include "src/compiler/node-properties.h" 7 #include "src/compiler/node-properties.h"
10 #include "src/compiler/node.h"
11 #include "src/compiler/simplified-operator.h" 8 #include "src/compiler/simplified-operator.h"
12 9
13 namespace v8 { 10 namespace v8 {
14 namespace internal { 11 namespace internal {
15 namespace compiler { 12 namespace compiler {
16 13
17 #ifdef DEBUG
18 #define TRACE(...) \
19 do { \
20 if (FLAG_trace_turbo_load_elimination) PrintF(__VA_ARGS__); \
21 } while (false)
22 #else
23 #define TRACE(...)
24 #endif
25
26 namespace { 14 namespace {
27 15
28 const size_t kMaxTrackedFields = 16;
29
30 Node* ActualValue(Node* node) {
31 switch (node->opcode()) {
32 case IrOpcode::kCheckBounds:
33 case IrOpcode::kCheckNumber:
34 case IrOpcode::kCheckTaggedPointer:
35 case IrOpcode::kCheckTaggedSigned:
36 case IrOpcode::kFinishRegion:
37 return ActualValue(NodeProperties::GetValueInput(node, 0));
38 default:
39 return node;
40 }
41 }
42
43 enum Aliasing { kNoAlias, kMayAlias, kMustAlias }; 16 enum Aliasing { kNoAlias, kMayAlias, kMustAlias };
44 17
45 Aliasing QueryAlias(Node* a, Node* b) { 18 Aliasing QueryAlias(Node* a, Node* b) {
46 if (a == b) return kMustAlias; 19 if (a == b) return kMustAlias;
47 if (b->opcode() == IrOpcode::kAllocate) { 20 if (b->opcode() == IrOpcode::kAllocate) {
48 switch (a->opcode()) { 21 switch (a->opcode()) {
49 case IrOpcode::kAllocate: 22 case IrOpcode::kAllocate:
50 case IrOpcode::kHeapConstant: 23 case IrOpcode::kHeapConstant:
51 case IrOpcode::kParameter: 24 case IrOpcode::kParameter:
52 return kNoAlias; 25 return kNoAlias;
26 case IrOpcode::kFinishRegion:
27 return QueryAlias(a->InputAt(0), b);
53 default: 28 default:
54 break; 29 break;
55 } 30 }
56 } 31 }
57 if (a->opcode() == IrOpcode::kAllocate) { 32 if (a->opcode() == IrOpcode::kAllocate) {
58 switch (b->opcode()) { 33 switch (b->opcode()) {
59 case IrOpcode::kHeapConstant: 34 case IrOpcode::kHeapConstant:
60 case IrOpcode::kParameter: 35 case IrOpcode::kParameter:
61 return kNoAlias; 36 return kNoAlias;
37 case IrOpcode::kFinishRegion:
38 return QueryAlias(a, b->InputAt(0));
62 default: 39 default:
63 break; 40 break;
64 } 41 }
65 } 42 }
66 return kMayAlias; 43 return kMayAlias;
67 } 44 }
68 45
69 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; } 46 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; }
70 47
71 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; } 48 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
72 49
73 // Abstract state to approximate the current state of a certain field along the 50 } // namespace
74 // effect paths through the graph. 51
75 class AbstractField final : public ZoneObject { 52 Reduction LoadElimination::Reduce(Node* node) {
76 public: 53 switch (node->opcode()) {
77 explicit AbstractField(Zone* zone) : info_for_node_(zone) {} 54 case IrOpcode::kLoadField:
78 AbstractField(Node* object, Node* value, Zone* zone) : info_for_node_(zone) { 55 return ReduceLoadField(node);
79 info_for_node_.insert(std::make_pair(object, value)); 56 case IrOpcode::kStoreField:
80 } 57 return ReduceStoreField(node);
81 58 case IrOpcode::kLoadElement:
82 AbstractField const* Extend(Node* object, Node* value, Zone* zone) const { 59 return ReduceLoadElement(node);
83 AbstractField* that = new (zone) AbstractField(zone); 60 case IrOpcode::kStoreElement:
84 that->info_for_node_ = this->info_for_node_; 61 return ReduceStoreElement(node);
85 that->info_for_node_.insert(std::make_pair(object, value)); 62 case IrOpcode::kEffectPhi:
86 return that; 63 return ReduceEffectPhi(node);
87 } 64 case IrOpcode::kDead:
88 Node* Lookup(Node* object) const { 65 break;
89 for (auto pair : info_for_node_) { 66 case IrOpcode::kStart:
90 if (MustAlias(object, pair.first)) return pair.second; 67 return ReduceStart(node);
91 } 68 default:
92 return nullptr; 69 return ReduceOtherNode(node);
93 } 70 }
94 AbstractField const* Kill(Node* object, Zone* zone) const { 71 return NoChange();
95 for (auto pair : this->info_for_node_) { 72 }
96 if (MayAlias(object, pair.first)) { 73
97 AbstractField* that = new (zone) AbstractField(zone); 74 Node* LoadElimination::AbstractElements::Lookup(Node* object,
98 for (auto pair : this->info_for_node_) { 75 Node* index) const {
99 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); 76 for (Element const element : elements_) {
77 if (element.object == nullptr) continue;
78 DCHECK_NOT_NULL(element.index);
79 DCHECK_NOT_NULL(element.value);
80 if (MustAlias(object, element.object) && MustAlias(index, element.index)) {
81 return element.value;
82 }
83 }
84 return nullptr;
85 }
86
87 LoadElimination::AbstractElements const*
88 LoadElimination::AbstractElements::Kill(Node* object, Node* index,
89 Zone* zone) const {
90 for (Element const element : this->elements_) {
91 if (element.object == nullptr) continue;
92 if (MayAlias(object, element.object)) {
93 AbstractElements* that = new (zone) AbstractElements(zone);
94 for (Element const element : this->elements_) {
95 if (element.object == nullptr) continue;
96 DCHECK_NOT_NULL(element.index);
97 DCHECK_NOT_NULL(element.value);
98 if (!MayAlias(object, element.object) ||
99 !MayAlias(index, element.index)) {
100 that->elements_[that->next_index_++] = element;
100 } 101 }
101 return that; 102 }
102 } 103 return that;
103 } 104 }
104 return this; 105 }
105 } 106 return this;
106 bool Equals(AbstractField const* that) const { 107 }
107 return this == that || this->info_for_node_ == that->info_for_node_; 108
108 } 109 bool LoadElimination::AbstractElements::Equals(
109 AbstractField const* Merge(AbstractField const* that, Zone* zone) const { 110 AbstractElements const* that) const {
110 if (this->Equals(that)) return this; 111 if (this == that) return true;
111 AbstractField* copy = new (zone) AbstractField(zone); 112 for (size_t i = 0; i < arraysize(elements_); ++i) {
112 for (auto this_it : this->info_for_node_) { 113 Element this_element = this->elements_[i];
113 Node* this_object = this_it.first; 114 if (this_element.object == nullptr) continue;
114 Node* this_value = this_it.second; 115 for (size_t j = 0;; ++j) {
115 auto that_it = that->info_for_node_.find(this_object); 116 if (j == arraysize(elements_)) return false;
116 if (that_it != that->info_for_node_.end() && 117 Element that_element = that->elements_[j];
117 that_it->second == this_value) { 118 if (this_element.object == that_element.object &&
118 copy->info_for_node_.insert(this_it); 119 this_element.index == that_element.index &&
119 } 120 this_element.value == that_element.value) {
120 } 121 break;
121 return copy; 122 }
122 } 123 }
123 124 }
124 private: 125 for (size_t i = 0; i < arraysize(elements_); ++i) {
125 ZoneMap<Node*, Node*> info_for_node_; 126 Element that_element = that->elements_[i];
126 }; 127 if (that_element.object == nullptr) continue;
127 128 for (size_t j = 0;; ++j) {
128 // Abstract state to track the state of all fields along the effect paths 129 if (j == arraysize(elements_)) return false;
129 // through the graph. 130 Element this_element = this->elements_[j];
130 class AbstractState final : public ZoneObject { 131 if (that_element.object == this_element.object &&
131 public: 132 that_element.index == this_element.index &&
132 AbstractState() { 133 that_element.value == this_element.value) {
133 for (size_t i = 0; i < kMaxTrackedFields; ++i) fields_[i] = nullptr; 134 break;
134 } 135 }
135 136 }
136 AbstractState const* Extend(Node* object, size_t index, Node* value, 137 }
137 Zone* zone) const { 138 return true;
138 AbstractState* that = new (zone) AbstractState(*this); 139 }
139 AbstractField const* that_field = that->fields_[index]; 140
140 if (that_field) { 141 LoadElimination::AbstractElements const*
141 that_field = that_field->Extend(object, value, zone); 142 LoadElimination::AbstractElements::Merge(AbstractElements const* that,
143 Zone* zone) const {
144 if (this->Equals(that)) return this;
145 AbstractElements* copy = new (zone) AbstractElements(zone);
146 for (Element const this_element : this->elements_) {
147 if (this_element.object == nullptr) continue;
148 for (Element const that_element : that->elements_) {
149 if (this_element.object == that_element.object &&
150 this_element.index == that_element.index &&
151 this_element.value == that_element.value) {
152 copy->elements_[copy->next_index_++] = this_element;
153 }
154 }
155 }
156 return copy;
157 }
158
159 Node* LoadElimination::AbstractField::Lookup(Node* object) const {
160 for (auto pair : info_for_node_) {
161 if (MustAlias(object, pair.first)) return pair.second;
162 }
163 return nullptr;
164 }
165
166 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill(
167 Node* object, Zone* zone) const {
168 for (auto pair : this->info_for_node_) {
169 if (MayAlias(object, pair.first)) {
170 AbstractField* that = new (zone) AbstractField(zone);
171 for (auto pair : this->info_for_node_) {
172 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
173 }
174 return that;
175 }
176 }
177 return this;
178 }
179
180 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
181 if (this->elements_) {
182 if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
183 return false;
184 }
185 } else if (that->elements_) {
186 return false;
187 }
188 for (size_t i = 0u; i < arraysize(fields_); ++i) {
189 AbstractField const* this_field = this->fields_[i];
190 AbstractField const* that_field = that->fields_[i];
191 if (this_field) {
192 if (!that_field || !that_field->Equals(this_field)) return false;
193 } else if (that_field) {
194 return false;
195 }
196 }
197 return true;
198 }
199
200 void LoadElimination::AbstractState::Merge(AbstractState const* that,
201 Zone* zone) {
202 // Merge the information we have about the elements.
203 if (this->elements_) {
204 this->elements_ = that->elements_
205 ? that->elements_->Merge(this->elements_, zone)
206 : that->elements_;
207 } else {
208 this->elements_ = that->elements_;
209 }
210
211 // Merge the information we have about the fields.
212 for (size_t i = 0; i < arraysize(fields_); ++i) {
213 if (this->fields_[i]) {
214 if (that->fields_[i]) {
215 this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone);
216 } else {
217 this->fields_[i] = nullptr;
218 }
219 }
220 }
221 }
222
223 Node* LoadElimination::AbstractState::LookupElement(Node* object,
224 Node* index) const {
225 if (this->elements_) {
226 return this->elements_->Lookup(object, index);
227 }
228 return nullptr;
229 }
230
231 LoadElimination::AbstractState const*
232 LoadElimination::AbstractState::AddElement(Node* object, Node* index,
233 Node* value, Zone* zone) const {
234 AbstractState* that = new (zone) AbstractState(*this);
235 if (that->elements_) {
236 that->elements_ = that->elements_->Extend(object, index, value, zone);
237 } else {
238 that->elements_ = new (zone) AbstractElements(object, index, value, zone);
239 }
240 return that;
241 }
242
243 LoadElimination::AbstractState const*
244 LoadElimination::AbstractState::KillElement(Node* object, Node* index,
245 Zone* zone) const {
246 if (this->elements_) {
247 AbstractElements const* that_elements =
248 this->elements_->Kill(object, index, zone);
249 if (this->elements_ != that_elements) {
250 AbstractState* that = new (zone) AbstractState(*this);
251 that->elements_ = that_elements;
252 return that;
253 }
254 }
255 return this;
256 }
257
258 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
259 Node* object, size_t index, Node* value, Zone* zone) const {
260 AbstractState* that = new (zone) AbstractState(*this);
261 if (that->fields_[index]) {
262 that->fields_[index] = that->fields_[index]->Extend(object, value, zone);
263 } else {
264 that->fields_[index] = new (zone) AbstractField(object, value, zone);
265 }
266 return that;
267 }
268
269 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
270 Node* object, size_t index, Zone* zone) const {
271 if (AbstractField const* this_field = this->fields_[index]) {
272 this_field = this_field->Kill(object, zone);
273 if (this->fields_[index] != this_field) {
274 AbstractState* that = new (zone) AbstractState(*this);
275 that->fields_[index] = this_field;
276 return that;
277 }
278 }
279 return this;
280 }
281
282 Node* LoadElimination::AbstractState::LookupField(Node* object,
283 size_t index) const {
284 if (AbstractField const* this_field = this->fields_[index]) {
285 return this_field->Lookup(object);
286 }
287 return nullptr;
288 }
289
290 LoadElimination::AbstractState const*
291 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
292 size_t const id = node->id();
293 if (id < info_for_node_.size()) return info_for_node_[id];
294 return nullptr;
295 }
296
297 void LoadElimination::AbstractStateForEffectNodes::Set(
298 Node* node, AbstractState const* state) {
299 size_t const id = node->id();
300 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
301 info_for_node_[id] = state;
302 }
303
304 Reduction LoadElimination::ReduceLoadField(Node* node) {
305 FieldAccess const& access = FieldAccessOf(node->op());
306 Node* const object = NodeProperties::GetValueInput(node, 0);
307 Node* const effect = NodeProperties::GetEffectInput(node);
308 AbstractState const* state = node_states_.Get(effect);
309 if (state == nullptr) return NoChange();
310 int field_index = FieldIndexOf(access);
311 if (field_index >= 0) {
312 if (Node* const replacement = state->LookupField(object, field_index)) {
313 // Make sure the {replacement} has at least as good type
314 // as the original {node}.
315 if (NodeProperties::GetType(replacement)
316 ->Is(NodeProperties::GetType(node))) {
317 ReplaceWithValue(node, replacement, effect);
318 DCHECK(!replacement->IsDead());
319 return Replace(replacement);
320 }
321 }
322 state = state->AddField(object, field_index, node, zone());
323 }
324 return UpdateState(node, state);
325 }
326
327 Reduction LoadElimination::ReduceStoreField(Node* node) {
328 FieldAccess const& access = FieldAccessOf(node->op());
329 Node* const object = NodeProperties::GetValueInput(node, 0);
330 Node* const new_value = NodeProperties::GetValueInput(node, 1);
331 Node* const effect = NodeProperties::GetEffectInput(node);
332 AbstractState const* state = node_states_.Get(effect);
333 if (state == nullptr) return NoChange();
334 int field_index = FieldIndexOf(access);
335 if (field_index >= 0) {
336 Node* const old_value = state->LookupField(object, field_index);
337 if (old_value == new_value) {
338 // This store is fully redundant.
339 return Replace(effect);
340 }
341 // Kill all potentially aliasing fields and record the new value.
342 state = state->KillField(object, field_index, zone());
343 state = state->AddField(object, field_index, new_value, zone());
344 } else {
345 // Unsupported StoreField operator.
346 state = empty_state();
347 }
348 return UpdateState(node, state);
349 }
350
351 Reduction LoadElimination::ReduceLoadElement(Node* node) {
352 Node* const object = NodeProperties::GetValueInput(node, 0);
353 Node* const index = NodeProperties::GetValueInput(node, 1);
354 Node* const effect = NodeProperties::GetEffectInput(node);
355 AbstractState const* state = node_states_.Get(effect);
356 if (state == nullptr) return NoChange();
357 if (Node* const replacement = state->LookupElement(object, index)) {
358 // Make sure the {replacement} has at least as good type
359 // as the original {node}.
360 if (NodeProperties::GetType(replacement)
361 ->Is(NodeProperties::GetType(node))) {
362 ReplaceWithValue(node, replacement, effect);
363 DCHECK(!replacement->IsDead());
364 return Replace(replacement);
365 }
366 }
367 state = state->AddElement(object, index, node, zone());
368 return UpdateState(node, state);
369 }
370
371 Reduction LoadElimination::ReduceStoreElement(Node* node) {
372 ElementAccess const& access = ElementAccessOf(node->op());
373 Node* const object = NodeProperties::GetValueInput(node, 0);
374 Node* const index = NodeProperties::GetValueInput(node, 1);
375 Node* const new_value = NodeProperties::GetValueInput(node, 2);
376 Node* const effect = NodeProperties::GetEffectInput(node);
377 AbstractState const* state = node_states_.Get(effect);
378 if (state == nullptr) return NoChange();
379 Node* const old_value = state->LookupElement(object, index);
380 if (old_value == new_value) {
381 // This store is fully redundant.
382 return Replace(effect);
383 }
384 // Kill all potentially aliasing elements.
385 state = state->KillElement(object, index, zone());
386 // Only record the new value if the store doesn't have an implicit truncation.
387 switch (access.machine_type.representation()) {
388 case MachineRepresentation::kNone:
389 case MachineRepresentation::kBit:
390 UNREACHABLE();
391 break;
392 case MachineRepresentation::kWord8:
393 case MachineRepresentation::kWord16:
394 case MachineRepresentation::kWord32:
395 case MachineRepresentation::kWord64:
396 case MachineRepresentation::kFloat32:
397 // TODO(turbofan): Add support for doing the truncations.
398 break;
399 case MachineRepresentation::kFloat64:
400 case MachineRepresentation::kSimd128:
401 case MachineRepresentation::kTagged:
402 state = state->AddElement(object, index, new_value, zone());
403 break;
404 }
405 return UpdateState(node, state);
406 }
407
408 Reduction LoadElimination::ReduceEffectPhi(Node* node) {
409 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
410 Node* const control = NodeProperties::GetControlInput(node);
411 AbstractState const* state0 = node_states_.Get(effect0);
412 if (state0 == nullptr) return NoChange();
413 if (control->opcode() == IrOpcode::kLoop) {
414 // Here we rely on having only reducible loops:
415 // The loop entry edge always dominates the header, so we can just take
416 // the state from the first input, and compute the loop state based on it.
417 AbstractState const* state = ComputeLoopState(node, state0);
418 return UpdateState(node, state);
419 }
420 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
421
422 // Shortcut for the case when we do not know anything about some input.
423 int const input_count = node->op()->EffectInputCount();
424 for (int i = 1; i < input_count; ++i) {
425 Node* const effect = NodeProperties::GetEffectInput(node, i);
426 if (node_states_.Get(effect) == nullptr) return NoChange();
427 }
428
429 // Make a copy of the first input's state and merge with the state
430 // from other inputs.
431 AbstractState* state = new (zone()) AbstractState(*state0);
432 for (int i = 1; i < input_count; ++i) {
433 Node* const input = NodeProperties::GetEffectInput(node, i);
434 state->Merge(node_states_.Get(input), zone());
435 }
436 return UpdateState(node, state);
437 }
438
439 Reduction LoadElimination::ReduceStart(Node* node) {
440 return UpdateState(node, empty_state());
441 }
442
443 Reduction LoadElimination::ReduceOtherNode(Node* node) {
444 if (node->op()->EffectInputCount() == 1) {
445 if (node->op()->EffectOutputCount() == 1) {
446 Node* const effect = NodeProperties::GetEffectInput(node);
447 AbstractState const* state = node_states_.Get(effect);
448 // If we do not know anything about the predecessor, do not propagate
449 // just yet because we will have to recompute anyway once we compute
450 // the predecessor.
451 if (state == nullptr) return NoChange();
452 // Check if this {node} has some uncontrolled side effects.
453 if (!node->op()->HasProperty(Operator::kNoWrite)) {
454 state = empty_state();
455 }
456 return UpdateState(node, state);
142 } else { 457 } else {
143 that_field = new (zone) AbstractField(object, value, zone); 458 // Effect terminators should be handled specially.
144 } 459 return NoChange();
145 that->fields_[index] = that_field; 460 }
146 return that; 461 }
147 } 462 DCHECK_EQ(0, node->op()->EffectInputCount());
148 AbstractState const* Kill(Node* object, size_t index, Zone* zone) const { 463 DCHECK_EQ(0, node->op()->EffectOutputCount());
149 if (!this->fields_[index]) return this; 464 return NoChange();
150 AbstractState* that = new (zone) AbstractState(*this); 465 }
151 that->fields_[index] = nullptr; 466
152 return that; 467 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
153 } 468 AbstractState const* original = node_states_.Get(node);
154 AbstractState const* Merge(AbstractState const* that, Zone* zone) const { 469 // Only signal that the {node} has Changed, if the information about {state}
155 if (this->Equals(that)) return this; 470 // has changed wrt. the {original}.
156 AbstractState* copy = new (zone) AbstractState(); 471 if (state != original) {
157 for (size_t i = 0; i < kMaxTrackedFields; ++i) { 472 if (original == nullptr || !state->Equals(original)) {
158 AbstractField const* this_field = this->fields_[i]; 473 node_states_.Set(node, state);
159 AbstractField const* that_field = that->fields_[i]; 474 return Changed(node);
160 if (this_field && that_field) { 475 }
161 copy->fields_[i] = this_field->Merge(that_field, zone); 476 }
162 } 477 return NoChange();
163 } 478 }
164 return copy; 479
165 } 480 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
166 Node* Lookup(Node* object, size_t index) const { 481 Node* node, AbstractState const* state) const {
167 AbstractField const* this_field = this->fields_[index]; 482 Node* const control = NodeProperties::GetControlInput(node);
168 if (this_field) return this_field->Lookup(object); 483 ZoneQueue<Node*> queue(zone());
169 return nullptr; 484 ZoneSet<Node*> visited(zone());
170 } 485 visited.insert(node);
171 bool Equals(AbstractState const* that) const { 486 for (int i = 1; i < control->InputCount(); ++i) {
172 if (this == that) return true; 487 queue.push(node->InputAt(i));
173 for (size_t i = 0; i < kMaxTrackedFields; ++i) { 488 }
174 AbstractField const* this_field = this->fields_[i]; 489 while (!queue.empty()) {
175 AbstractField const* that_field = that->fields_[i]; 490 Node* const current = queue.front();
176 if (this_field) { 491 queue.pop();
177 if (!that_field || !this_field->Equals(that_field)) return false; 492 if (visited.find(current) == visited.end()) {
178 } else if (that_field) { 493 visited.insert(current);
179 return false; 494 if (!current->op()->HasProperty(Operator::kNoWrite)) {
180 } 495 switch (current->opcode()) {
181 DCHECK(this_field == that_field || this_field->Equals(that_field)); 496 case IrOpcode::kStoreField: {
182 } 497 FieldAccess const& access = FieldAccessOf(current->op());
183 return true; 498 Node* const object = NodeProperties::GetValueInput(current, 0);
184 } 499 int field_index = FieldIndexOf(access);
185 500 if (field_index < 0) return empty_state();
186 private: 501 state = state->KillField(object, field_index, zone());
187 AbstractField const* fields_[kMaxTrackedFields]; 502 break;
188 };
189
190 class LoadEliminationAnalysis final {
191 public:
192 LoadEliminationAnalysis(Graph* graph, Zone* zone)
193 : candidates_(zone),
194 empty_state_(),
195 queue_(zone),
196 node_states_(graph->NodeCount(), zone) {}
197
198 void Run(Node* start) {
199 TRACE("--{Analysis phase}--\n");
200 UpdateState(start, empty_state());
201 while (!queue_.empty()) {
202 Node* const node = queue_.top();
203 queue_.pop();
204 VisitNode(node);
205 }
206 TRACE("--{Replacement phase}--\n");
207 ZoneMap<Node*, Node*> replacements(zone());
208 for (Node* const node : candidates_) {
209 switch (node->opcode()) {
210 case IrOpcode::kLoadField: {
211 FieldAccess const& access = FieldAccessOf(node->op());
212 Node* const object =
213 ActualValue(NodeProperties::GetValueInput(node, 0));
214 Node* const effect = NodeProperties::GetEffectInput(node);
215 AbstractState const* state = GetState(effect);
216 int field_index = FieldIndexOf(access);
217 DCHECK_LE(0, field_index);
218 if (Node* value = state->Lookup(object, field_index)) {
219 auto it = replacements.find(value);
220 if (it != replacements.end()) value = it->second;
221 Type* const value_type = NodeProperties::GetType(value);
222 if (value_type->Is(access.type)) {
223 replacements.insert(std::make_pair(node, value));
224 TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n",
225 node->id(), value->id(), value->op()->mnemonic());
226 NodeProperties::ReplaceUses(node, value, effect);
227 node->Kill();
228 } else {
229 TRACE(
230 " - Cannot replace redundant #%d:LoadField with #%d:%s,"
231 " because types don't agree",
232 node->id(), value->id(), value->op()->mnemonic());
233 }
234 } 503 }
235 break; 504 case IrOpcode::kStoreElement: {
505 Node* const object = NodeProperties::GetValueInput(current, 0);
506 Node* const index = NodeProperties::GetValueInput(current, 1);
507 state = state->KillElement(object, index, zone());
508 break;
509 }
510 default:
511 return empty_state();
236 } 512 }
237 case IrOpcode::kStoreField: { 513 }
238 FieldAccess const& access = FieldAccessOf(node->op()); 514 for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
239 Node* const object = 515 queue.push(NodeProperties::GetEffectInput(current, i));
240 ActualValue(NodeProperties::GetValueInput(node, 0)); 516 }
241 Node* const value = 517 }
242 ActualValue(NodeProperties::GetValueInput(node, 1)); 518 }
243 Node* const effect = NodeProperties::GetEffectInput(node); 519 return state;
244 AbstractState const* state = GetState(effect); 520 }
245 int field_index = FieldIndexOf(access); 521
246 DCHECK_LE(0, field_index); 522 // static
247 if (value == state->Lookup(object, field_index)) { 523 int LoadElimination::FieldIndexOf(FieldAccess const& access) {
248 TRACE(" - Killing redundant #%d:StoreField\n", node->id()); 524 switch (access.machine_type.representation()) {
249 NodeProperties::ReplaceUses(node, value, effect); 525 case MachineRepresentation::kNone:
250 node->Kill(); 526 case MachineRepresentation::kBit:
251 } 527 UNREACHABLE();
252 break; 528 break;
253 } 529 case MachineRepresentation::kWord8:
254 default: 530 case MachineRepresentation::kWord16:
255 UNREACHABLE(); 531 case MachineRepresentation::kWord32:
256 } 532 case MachineRepresentation::kWord64:
257 } 533 case MachineRepresentation::kFloat32:
258 } 534 return -1; // Currently untracked.
259 535 case MachineRepresentation::kFloat64:
260 private: 536 case MachineRepresentation::kSimd128:
261 void VisitNode(Node* node) { 537 case MachineRepresentation::kTagged:
262 TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic()); 538 // TODO(bmeurer): Check that we never do overlapping load/stores of
263 switch (node->opcode()) { 539 // individual parts of Float64/Simd128 values.
264 case IrOpcode::kEffectPhi: 540 break;
265 return VisitEffectPhi(node); 541 }
266 case IrOpcode::kLoadField: 542 DCHECK_EQ(kTaggedBase, access.base_is_tagged);
267 return VisitLoadField(node); 543 DCHECK_EQ(0, access.offset % kPointerSize);
268 case IrOpcode::kStoreElement: 544 int field_index = access.offset / kPointerSize;
269 return VisitStoreElement(node); 545 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
270 case IrOpcode::kStoreField: 546 return field_index;
271 return VisitStoreField(node);
272 case IrOpcode::kDeoptimize:
273 case IrOpcode::kReturn:
274 case IrOpcode::kTerminate:
275 case IrOpcode::kThrow:
276 break;
277 default:
278 return VisitOtherNode(node);
279 }
280 }
281
282 void VisitEffectPhi(Node* node) {
283 int const input_count = node->InputCount() - 1;
284 DCHECK_LT(0, input_count);
285 Node* const control = NodeProperties::GetControlInput(node);
286 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
287 AbstractState const* state = GetState(effect0);
288 if (state == nullptr) return;
289 if (control->opcode() == IrOpcode::kMerge) {
290 for (int i = 1; i < input_count; ++i) {
291 Node* const effecti = NodeProperties::GetEffectInput(node, i);
292 if (GetState(effecti) == nullptr) return;
293 }
294 }
295 for (int i = 1; i < input_count; ++i) {
296 Node* const effecti = NodeProperties::GetEffectInput(node, i);
297 if (AbstractState const* statei = GetState(effecti)) {
298 state = state->Merge(statei, zone());
299 }
300 }
301 UpdateState(node, state);
302 }
303
304 void VisitLoadField(Node* node) {
305 FieldAccess const& access = FieldAccessOf(node->op());
306 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
307 Node* const effect = NodeProperties::GetEffectInput(node);
308 AbstractState const* state = GetState(effect);
309 int field_index = FieldIndexOf(access);
310 if (field_index >= 0) {
311 Node* const value = state->Lookup(object, field_index);
312 if (!value) {
313 TRACE(" Node #%d:LoadField is not redundant\n", node->id());
314 state = state->Extend(object, field_index, node, zone());
315 } else if (!NodeProperties::GetType(value)->Is(access.type)) {
316 TRACE(
317 " Node #%d:LoadField is redundant for #%d:%s, but"
318 " types don't agree\n",
319 node->id(), value->id(), value->op()->mnemonic());
320 state = state->Extend(object, field_index, node, zone());
321 } else if (value) {
322 TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n",
323 node->id(), value->id(), value->op()->mnemonic());
324 candidates_.insert(node);
325 }
326 } else {
327 TRACE(" Node #%d:LoadField is unsupported\n", node->id());
328 }
329 UpdateState(node, state);
330 }
331
332 void VisitStoreField(Node* node) {
333 FieldAccess const& access = FieldAccessOf(node->op());
334 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0));
335 Node* const new_value = NodeProperties::GetValueInput(node, 1);
336 Node* const effect = NodeProperties::GetEffectInput(node);
337 AbstractState const* state = GetState(effect);
338 int field_index = FieldIndexOf(access);
339 if (field_index >= 0) {
340 Node* const old_value = state->Lookup(object, field_index);
341 if (old_value == new_value) {
342 TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n",
343 node->id(), new_value->id(), new_value->op()->mnemonic());
344 candidates_.insert(node);
345 }
346 TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n",
347 field_index, object->id(), object->op()->mnemonic());
348 state = state->Kill(object, field_index, zone());
349 TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n",
350 node->id(), new_value->id(), new_value->op()->mnemonic(),
351 field_index, object->id(), object->op()->mnemonic());
352 state = state->Extend(object, field_index, new_value, zone());
353 } else {
354 TRACE(" Node #%d:StoreField is unsupported\n", node->id());
355 state = empty_state();
356 }
357 UpdateState(node, state);
358 }
359
360 void VisitStoreElement(Node* node) {
361 Node* const effect = NodeProperties::GetEffectInput(node);
362 AbstractState const* state = GetState(effect);
363 UpdateState(node, state);
364 }
365
366 void VisitOtherNode(Node* node) {
367 DCHECK_EQ(1, node->op()->EffectInputCount());
368 DCHECK_EQ(1, node->op()->EffectOutputCount());
369 Node* const effect = NodeProperties::GetEffectInput(node);
370 AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite)
371 ? GetState(effect)
372 : empty_state();
373 UpdateState(node, state);
374 }
375
376 int FieldIndexOf(FieldAccess const& access) const {
377 switch (access.machine_type.representation()) {
378 case MachineRepresentation::kNone:
379 case MachineRepresentation::kBit:
380 UNREACHABLE();
381 break;
382 case MachineRepresentation::kWord8:
383 case MachineRepresentation::kWord16:
384 case MachineRepresentation::kWord32:
385 case MachineRepresentation::kWord64:
386 case MachineRepresentation::kFloat32:
387 return -1; // Currently untracked.
388 case MachineRepresentation::kFloat64:
389 case MachineRepresentation::kSimd128:
390 case MachineRepresentation::kTagged:
391 // TODO(bmeurer): Check that we never do overlapping load/stores of
392 // individual parts of Float64/Simd128 values.
393 break;
394 }
395 DCHECK_EQ(kTaggedBase, access.base_is_tagged);
396 DCHECK_EQ(0, access.offset % kPointerSize);
397 int field_index = access.offset / kPointerSize;
398 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
399 return field_index;
400 }
401
402 AbstractState const* GetState(Node* node) const {
403 return node_states_[node->id()];
404 }
405 void SetState(Node* node, AbstractState const* state) {
406 node_states_[node->id()] = state;
407 }
408
409 void UpdateState(Node* node, AbstractState const* new_state) {
410 AbstractState const* old_state = GetState(node);
411 if (old_state && old_state->Equals(new_state)) return;
412 SetState(node, new_state);
413 EnqueueUses(node);
414 }
415
416 void EnqueueUses(Node* node) {
417 for (Edge const edge : node->use_edges()) {
418 if (NodeProperties::IsEffectEdge(edge)) {
419 queue_.push(edge.from());
420 }
421 }
422 }
423
424 AbstractState const* empty_state() const { return &empty_state_; }
425 Zone* zone() const { return node_states_.get_allocator().zone(); }
426
427 ZoneSet<Node*> candidates_;
428 AbstractState const empty_state_;
429 ZoneStack<Node*> queue_;
430 ZoneVector<AbstractState const*> node_states_;
431
432 DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis);
433 };
434
435 } // namespace
436
437 void LoadElimination::Run() {
438 LoadEliminationAnalysis analysis(graph(), zone());
439 analysis.Run(graph()->start());
440 } 547 }
441 548
442 } // namespace compiler 549 } // namespace compiler
443 } // namespace internal 550 } // namespace internal
444 } // namespace v8 551 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/load-elimination.h ('k') | src/compiler/pipeline.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698