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
3 // found in the LICENSE file.
4 
5 #include "src/compiler/load-elimination.h"
6 
7 #include "src/compiler/common-operator.h"
8 #include "src/compiler/js-graph.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/simplified-operator.h"
11 
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15 
16 namespace {
17 
18 enum Aliasing { kNoAlias, kMayAlias, kMustAlias };
19 
QueryAlias(Node * a,Node * b)20 Aliasing QueryAlias(Node* a, Node* b) {
21   if (a == b) return kMustAlias;
22   if (!NodeProperties::GetType(a)->Maybe(NodeProperties::GetType(b))) {
23     return kNoAlias;
24   }
25   switch (b->opcode()) {
26     case IrOpcode::kAllocate: {
27       switch (a->opcode()) {
28         case IrOpcode::kAllocate:
29         case IrOpcode::kHeapConstant:
30         case IrOpcode::kParameter:
31           return kNoAlias;
32         default:
33           break;
34       }
35       break;
36     }
37     case IrOpcode::kFinishRegion:
38       return QueryAlias(a, b->InputAt(0));
39     default:
40       break;
41   }
42   switch (a->opcode()) {
43     case IrOpcode::kAllocate: {
44       switch (b->opcode()) {
45         case IrOpcode::kHeapConstant:
46         case IrOpcode::kParameter:
47           return kNoAlias;
48         default:
49           break;
50       }
51       break;
52     }
53     case IrOpcode::kFinishRegion:
54       return QueryAlias(a->InputAt(0), b);
55     default:
56       break;
57   }
58   return kMayAlias;
59 }
60 
MayAlias(Node * a,Node * b)61 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; }
62 
MustAlias(Node * a,Node * b)63 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
64 
65 }  // namespace
66 
Reduce(Node * node)67 Reduction LoadElimination::Reduce(Node* node) {
68   if (FLAG_trace_turbo_load_elimination) {
69     if (node->op()->EffectInputCount() > 0) {
70       PrintF(" visit #%d:%s", node->id(), node->op()->mnemonic());
71       if (node->op()->ValueInputCount() > 0) {
72         PrintF("(");
73         for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
74           if (i > 0) PrintF(", ");
75           Node* const value = NodeProperties::GetValueInput(node, i);
76           PrintF("#%d:%s", value->id(), value->op()->mnemonic());
77         }
78         PrintF(")");
79       }
80       PrintF("\n");
81       for (int i = 0; i < node->op()->EffectInputCount(); ++i) {
82         Node* const effect = NodeProperties::GetEffectInput(node, i);
83         if (AbstractState const* const state = node_states_.Get(effect)) {
84           PrintF("  state[%i]: #%d:%s\n", i, effect->id(),
85                  effect->op()->mnemonic());
86           state->Print();
87         } else {
88           PrintF("  no state[%i]: #%d:%s\n", i, effect->id(),
89                  effect->op()->mnemonic());
90         }
91       }
92     }
93   }
94   switch (node->opcode()) {
95     case IrOpcode::kArrayBufferWasNeutered:
96       return ReduceArrayBufferWasNeutered(node);
97     case IrOpcode::kCheckMaps:
98       return ReduceCheckMaps(node);
99     case IrOpcode::kEnsureWritableFastElements:
100       return ReduceEnsureWritableFastElements(node);
101     case IrOpcode::kMaybeGrowFastElements:
102       return ReduceMaybeGrowFastElements(node);
103     case IrOpcode::kTransitionElementsKind:
104       return ReduceTransitionElementsKind(node);
105     case IrOpcode::kLoadField:
106       return ReduceLoadField(node);
107     case IrOpcode::kStoreField:
108       return ReduceStoreField(node);
109     case IrOpcode::kLoadElement:
110       return ReduceLoadElement(node);
111     case IrOpcode::kStoreElement:
112       return ReduceStoreElement(node);
113     case IrOpcode::kStoreTypedElement:
114       return ReduceStoreTypedElement(node);
115     case IrOpcode::kEffectPhi:
116       return ReduceEffectPhi(node);
117     case IrOpcode::kDead:
118       break;
119     case IrOpcode::kStart:
120       return ReduceStart(node);
121     default:
122       return ReduceOtherNode(node);
123   }
124   return NoChange();
125 }
126 
127 namespace {
128 
IsCompatibleCheck(Node const * a,Node const * b)129 bool IsCompatibleCheck(Node const* a, Node const* b) {
130   if (a->op() != b->op()) return false;
131   for (int i = a->op()->ValueInputCount(); --i >= 0;) {
132     if (!MustAlias(a->InputAt(i), b->InputAt(i))) return false;
133   }
134   return true;
135 }
136 
137 }  // namespace
138 
Lookup(Node * node) const139 Node* LoadElimination::AbstractChecks::Lookup(Node* node) const {
140   for (Node* const check : nodes_) {
141     if (check && IsCompatibleCheck(check, node)) {
142       return check;
143     }
144   }
145   return nullptr;
146 }
147 
Equals(AbstractChecks const * that) const148 bool LoadElimination::AbstractChecks::Equals(AbstractChecks const* that) const {
149   if (this == that) return true;
150   for (size_t i = 0; i < arraysize(nodes_); ++i) {
151     if (Node* this_node = this->nodes_[i]) {
152       for (size_t j = 0;; ++j) {
153         if (j == arraysize(nodes_)) return false;
154         if (that->nodes_[j] == this_node) break;
155       }
156     }
157   }
158   for (size_t i = 0; i < arraysize(nodes_); ++i) {
159     if (Node* that_node = that->nodes_[i]) {
160       for (size_t j = 0;; ++j) {
161         if (j == arraysize(nodes_)) return false;
162         if (this->nodes_[j] == that_node) break;
163       }
164     }
165   }
166   return true;
167 }
168 
Merge(AbstractChecks const * that,Zone * zone) const169 LoadElimination::AbstractChecks const* LoadElimination::AbstractChecks::Merge(
170     AbstractChecks const* that, Zone* zone) const {
171   if (this->Equals(that)) return this;
172   AbstractChecks* copy = new (zone) AbstractChecks(zone);
173   for (Node* const this_node : this->nodes_) {
174     if (this_node == nullptr) continue;
175     for (Node* const that_node : that->nodes_) {
176       if (this_node == that_node) {
177         copy->nodes_[copy->next_index_++] = this_node;
178         break;
179       }
180     }
181   }
182   copy->next_index_ %= arraysize(nodes_);
183   return copy;
184 }
185 
Print() const186 void LoadElimination::AbstractChecks::Print() const {
187   for (Node* const node : nodes_) {
188     if (node != nullptr) {
189       PrintF("    #%d:%s\n", node->id(), node->op()->mnemonic());
190     }
191   }
192 }
193 
Lookup(Node * object,Node * index) const194 Node* LoadElimination::AbstractElements::Lookup(Node* object,
195                                                 Node* index) const {
196   for (Element const element : elements_) {
197     if (element.object == nullptr) continue;
198     DCHECK_NOT_NULL(element.index);
199     DCHECK_NOT_NULL(element.value);
200     if (MustAlias(object, element.object) && MustAlias(index, element.index)) {
201       return element.value;
202     }
203   }
204   return nullptr;
205 }
206 
207 LoadElimination::AbstractElements const*
Kill(Node * object,Node * index,Zone * zone) const208 LoadElimination::AbstractElements::Kill(Node* object, Node* index,
209                                         Zone* zone) const {
210   for (Element const element : this->elements_) {
211     if (element.object == nullptr) continue;
212     if (MayAlias(object, element.object)) {
213       AbstractElements* that = new (zone) AbstractElements(zone);
214       for (Element const element : this->elements_) {
215         if (element.object == nullptr) continue;
216         DCHECK_NOT_NULL(element.index);
217         DCHECK_NOT_NULL(element.value);
218         if (!MayAlias(object, element.object) ||
219             !NodeProperties::GetType(index)->Maybe(
220                 NodeProperties::GetType(element.index))) {
221           that->elements_[that->next_index_++] = element;
222         }
223       }
224       that->next_index_ %= arraysize(elements_);
225       return that;
226     }
227   }
228   return this;
229 }
230 
Equals(AbstractElements const * that) const231 bool LoadElimination::AbstractElements::Equals(
232     AbstractElements const* that) const {
233   if (this == that) return true;
234   for (size_t i = 0; i < arraysize(elements_); ++i) {
235     Element this_element = this->elements_[i];
236     if (this_element.object == nullptr) continue;
237     for (size_t j = 0;; ++j) {
238       if (j == arraysize(elements_)) return false;
239       Element that_element = that->elements_[j];
240       if (this_element.object == that_element.object &&
241           this_element.index == that_element.index &&
242           this_element.value == that_element.value) {
243         break;
244       }
245     }
246   }
247   for (size_t i = 0; i < arraysize(elements_); ++i) {
248     Element that_element = that->elements_[i];
249     if (that_element.object == nullptr) continue;
250     for (size_t j = 0;; ++j) {
251       if (j == arraysize(elements_)) return false;
252       Element this_element = this->elements_[j];
253       if (that_element.object == this_element.object &&
254           that_element.index == this_element.index &&
255           that_element.value == this_element.value) {
256         break;
257       }
258     }
259   }
260   return true;
261 }
262 
263 LoadElimination::AbstractElements const*
Merge(AbstractElements const * that,Zone * zone) const264 LoadElimination::AbstractElements::Merge(AbstractElements const* that,
265                                          Zone* zone) const {
266   if (this->Equals(that)) return this;
267   AbstractElements* copy = new (zone) AbstractElements(zone);
268   for (Element const this_element : this->elements_) {
269     if (this_element.object == nullptr) continue;
270     for (Element const that_element : that->elements_) {
271       if (this_element.object == that_element.object &&
272           this_element.index == that_element.index &&
273           this_element.value == that_element.value) {
274         copy->elements_[copy->next_index_++] = this_element;
275         break;
276       }
277     }
278   }
279   copy->next_index_ %= arraysize(elements_);
280   return copy;
281 }
282 
Print() const283 void LoadElimination::AbstractElements::Print() const {
284   for (Element const& element : elements_) {
285     if (element.object) {
286       PrintF("    #%d:%s @ #%d:%s -> #%d:%s\n", element.object->id(),
287              element.object->op()->mnemonic(), element.index->id(),
288              element.index->op()->mnemonic(), element.value->id(),
289              element.value->op()->mnemonic());
290     }
291   }
292 }
293 
Lookup(Node * object) const294 Node* LoadElimination::AbstractField::Lookup(Node* object) const {
295   for (auto pair : info_for_node_) {
296     if (MustAlias(object, pair.first)) return pair.second;
297   }
298   return nullptr;
299 }
300 
Kill(Node * object,Zone * zone) const301 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill(
302     Node* object, Zone* zone) const {
303   for (auto pair : this->info_for_node_) {
304     if (MayAlias(object, pair.first)) {
305       AbstractField* that = new (zone) AbstractField(zone);
306       for (auto pair : this->info_for_node_) {
307         if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
308       }
309       return that;
310     }
311   }
312   return this;
313 }
314 
Print() const315 void LoadElimination::AbstractField::Print() const {
316   for (auto pair : info_for_node_) {
317     PrintF("    #%d:%s -> #%d:%s\n", pair.first->id(),
318            pair.first->op()->mnemonic(), pair.second->id(),
319            pair.second->op()->mnemonic());
320   }
321 }
322 
Equals(AbstractState const * that) const323 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
324   if (this->checks_) {
325     if (!that->checks_ || !that->checks_->Equals(this->checks_)) {
326       return false;
327     }
328   } else if (that->checks_) {
329     return false;
330   }
331   if (this->elements_) {
332     if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
333       return false;
334     }
335   } else if (that->elements_) {
336     return false;
337   }
338   for (size_t i = 0u; i < arraysize(fields_); ++i) {
339     AbstractField const* this_field = this->fields_[i];
340     AbstractField const* that_field = that->fields_[i];
341     if (this_field) {
342       if (!that_field || !that_field->Equals(this_field)) return false;
343     } else if (that_field) {
344       return false;
345     }
346   }
347   return true;
348 }
349 
Merge(AbstractState const * that,Zone * zone)350 void LoadElimination::AbstractState::Merge(AbstractState const* that,
351                                            Zone* zone) {
352   // Merge the information we have about the checks.
353   if (this->checks_) {
354     this->checks_ =
355         that->checks_ ? that->checks_->Merge(this->checks_, zone) : nullptr;
356   }
357 
358   // Merge the information we have about the elements.
359   if (this->elements_) {
360     this->elements_ = that->elements_
361                           ? that->elements_->Merge(this->elements_, zone)
362                           : nullptr;
363   }
364 
365   // Merge the information we have about the fields.
366   for (size_t i = 0; i < arraysize(fields_); ++i) {
367     if (this->fields_[i]) {
368       if (that->fields_[i]) {
369         this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone);
370       } else {
371         this->fields_[i] = nullptr;
372       }
373     }
374   }
375 }
376 
LookupCheck(Node * node) const377 Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
378   return this->checks_ ? this->checks_->Lookup(node) : nullptr;
379 }
380 
AddCheck(Node * node,Zone * zone) const381 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck(
382     Node* node, Zone* zone) const {
383   AbstractState* that = new (zone) AbstractState(*this);
384   if (that->checks_) {
385     that->checks_ = that->checks_->Extend(node, zone);
386   } else {
387     that->checks_ = new (zone) AbstractChecks(node, zone);
388   }
389   return that;
390 }
391 
LookupElement(Node * object,Node * index) const392 Node* LoadElimination::AbstractState::LookupElement(Node* object,
393                                                     Node* index) const {
394   if (this->elements_) {
395     return this->elements_->Lookup(object, index);
396   }
397   return nullptr;
398 }
399 
400 LoadElimination::AbstractState const*
AddElement(Node * object,Node * index,Node * value,Zone * zone) const401 LoadElimination::AbstractState::AddElement(Node* object, Node* index,
402                                            Node* value, Zone* zone) const {
403   AbstractState* that = new (zone) AbstractState(*this);
404   if (that->elements_) {
405     that->elements_ = that->elements_->Extend(object, index, value, zone);
406   } else {
407     that->elements_ = new (zone) AbstractElements(object, index, value, zone);
408   }
409   return that;
410 }
411 
412 LoadElimination::AbstractState const*
KillElement(Node * object,Node * index,Zone * zone) const413 LoadElimination::AbstractState::KillElement(Node* object, Node* index,
414                                             Zone* zone) const {
415   if (this->elements_) {
416     AbstractElements const* that_elements =
417         this->elements_->Kill(object, index, zone);
418     if (this->elements_ != that_elements) {
419       AbstractState* that = new (zone) AbstractState(*this);
420       that->elements_ = that_elements;
421       return that;
422     }
423   }
424   return this;
425 }
426 
AddField(Node * object,size_t index,Node * value,Zone * zone) const427 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
428     Node* object, size_t index, Node* value, Zone* zone) const {
429   AbstractState* that = new (zone) AbstractState(*this);
430   if (that->fields_[index]) {
431     that->fields_[index] = that->fields_[index]->Extend(object, value, zone);
432   } else {
433     that->fields_[index] = new (zone) AbstractField(object, value, zone);
434   }
435   return that;
436 }
437 
KillField(Node * object,size_t index,Zone * zone) const438 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
439     Node* object, size_t index, Zone* zone) const {
440   if (AbstractField const* this_field = this->fields_[index]) {
441     this_field = this_field->Kill(object, zone);
442     if (this->fields_[index] != this_field) {
443       AbstractState* that = new (zone) AbstractState(*this);
444       that->fields_[index] = this_field;
445       return that;
446     }
447   }
448   return this;
449 }
450 
451 LoadElimination::AbstractState const*
KillFields(Node * object,Zone * zone) const452 LoadElimination::AbstractState::KillFields(Node* object, Zone* zone) const {
453   for (size_t i = 0;; ++i) {
454     if (i == arraysize(fields_)) return this;
455     if (AbstractField const* this_field = this->fields_[i]) {
456       AbstractField const* that_field = this_field->Kill(object, zone);
457       if (that_field != this_field) {
458         AbstractState* that = new (zone) AbstractState(*this);
459         that->fields_[i] = this_field;
460         while (++i < arraysize(fields_)) {
461           if (this->fields_[i] != nullptr) {
462             that->fields_[i] = this->fields_[i]->Kill(object, zone);
463           }
464         }
465         return that;
466       }
467     }
468   }
469 }
470 
LookupField(Node * object,size_t index) const471 Node* LoadElimination::AbstractState::LookupField(Node* object,
472                                                   size_t index) const {
473   if (AbstractField const* this_field = this->fields_[index]) {
474     return this_field->Lookup(object);
475   }
476   return nullptr;
477 }
478 
Print() const479 void LoadElimination::AbstractState::Print() const {
480   if (checks_) {
481     PrintF("   checks:\n");
482     checks_->Print();
483   }
484   if (elements_) {
485     PrintF("   elements:\n");
486     elements_->Print();
487   }
488   for (size_t i = 0; i < arraysize(fields_); ++i) {
489     if (AbstractField const* const field = fields_[i]) {
490       PrintF("   field %zu:\n", i);
491       field->Print();
492     }
493   }
494 }
495 
496 LoadElimination::AbstractState const*
Get(Node * node) const497 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
498   size_t const id = node->id();
499   if (id < info_for_node_.size()) return info_for_node_[id];
500   return nullptr;
501 }
502 
Set(Node * node,AbstractState const * state)503 void LoadElimination::AbstractStateForEffectNodes::Set(
504     Node* node, AbstractState const* state) {
505   size_t const id = node->id();
506   if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
507   info_for_node_[id] = state;
508 }
509 
ReduceArrayBufferWasNeutered(Node * node)510 Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
511   Node* const effect = NodeProperties::GetEffectInput(node);
512   AbstractState const* state = node_states_.Get(effect);
513   if (state == nullptr) return NoChange();
514   if (Node* const check = state->LookupCheck(node)) {
515     ReplaceWithValue(node, check, effect);
516     return Replace(check);
517   }
518   state = state->AddCheck(node, zone());
519   return UpdateState(node, state);
520 }
521 
ReduceCheckMaps(Node * node)522 Reduction LoadElimination::ReduceCheckMaps(Node* node) {
523   Node* const object = NodeProperties::GetValueInput(node, 0);
524   Node* const effect = NodeProperties::GetEffectInput(node);
525   AbstractState const* state = node_states_.Get(effect);
526   if (state == nullptr) return NoChange();
527   int const map_input_count = node->op()->ValueInputCount() - 1;
528   if (Node* const object_map =
529           state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
530     for (int i = 0; i < map_input_count; ++i) {
531       Node* map = NodeProperties::GetValueInput(node, 1 + i);
532       if (map == object_map) return Replace(effect);
533     }
534   }
535   if (map_input_count == 1) {
536     Node* const map0 = NodeProperties::GetValueInput(node, 1);
537     state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), map0,
538                             zone());
539   }
540   return UpdateState(node, state);
541 }
542 
ReduceEnsureWritableFastElements(Node * node)543 Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
544   Node* const object = NodeProperties::GetValueInput(node, 0);
545   Node* const elements = NodeProperties::GetValueInput(node, 1);
546   Node* const effect = NodeProperties::GetEffectInput(node);
547   AbstractState const* state = node_states_.Get(effect);
548   if (state == nullptr) return NoChange();
549   Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
550   if (Node* const elements_map =
551           state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) {
552     // Check if the {elements} already have the fixed array map.
553     if (elements_map == fixed_array_map) {
554       ReplaceWithValue(node, elements, effect);
555       return Replace(elements);
556     }
557   }
558   // We know that the resulting elements have the fixed array map.
559   state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
560                           fixed_array_map, zone());
561   // Kill the previous elements on {object}.
562   state =
563       state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
564   // Add the new elements on {object}.
565   state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
566                           zone());
567   return UpdateState(node, state);
568 }
569 
ReduceMaybeGrowFastElements(Node * node)570 Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
571   GrowFastElementsFlags flags = GrowFastElementsFlagsOf(node->op());
572   Node* const object = NodeProperties::GetValueInput(node, 0);
573   Node* const effect = NodeProperties::GetEffectInput(node);
574   AbstractState const* state = node_states_.Get(effect);
575   if (state == nullptr) return NoChange();
576   if (flags & GrowFastElementsFlag::kDoubleElements) {
577     // We know that the resulting elements have the fixed double array map.
578     Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant();
579     state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
580                             fixed_double_array_map, zone());
581   } else {
582     // We know that the resulting elements have the fixed array map.
583     Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
584     state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
585                             fixed_array_map, zone());
586   }
587   if (flags & GrowFastElementsFlag::kArrayObject) {
588     // Kill the previous Array::length on {object}.
589     state =
590         state->KillField(object, FieldIndexOf(JSArray::kLengthOffset), zone());
591   }
592   // Kill the previous elements on {object}.
593   state =
594       state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
595   // Add the new elements on {object}.
596   state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
597                           zone());
598   return UpdateState(node, state);
599 }
600 
ReduceTransitionElementsKind(Node * node)601 Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
602   Node* const object = NodeProperties::GetValueInput(node, 0);
603   Node* const source_map = NodeProperties::GetValueInput(node, 1);
604   Node* const target_map = NodeProperties::GetValueInput(node, 2);
605   Node* const effect = NodeProperties::GetEffectInput(node);
606   AbstractState const* state = node_states_.Get(effect);
607   if (state == nullptr) return NoChange();
608   if (Node* const object_map =
609           state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
610     if (target_map == object_map) {
611       // The {object} already has the {target_map}, so this TransitionElements
612       // {node} is fully redundant (independent of what {source_map} is).
613       return Replace(effect);
614     }
615     state =
616         state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
617     if (source_map == object_map) {
618       state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset),
619                               target_map, zone());
620     }
621   } else {
622     state =
623         state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
624   }
625   ElementsTransition transition = ElementsTransitionOf(node->op());
626   switch (transition) {
627     case ElementsTransition::kFastTransition:
628       break;
629     case ElementsTransition::kSlowTransition:
630       // Kill the elements as well.
631       state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
632                                zone());
633       break;
634   }
635   return UpdateState(node, state);
636 }
637 
ReduceLoadField(Node * node)638 Reduction LoadElimination::ReduceLoadField(Node* node) {
639   FieldAccess const& access = FieldAccessOf(node->op());
640   Node* const object = NodeProperties::GetValueInput(node, 0);
641   Node* const effect = NodeProperties::GetEffectInput(node);
642   Node* const control = NodeProperties::GetControlInput(node);
643   AbstractState const* state = node_states_.Get(effect);
644   if (state == nullptr) return NoChange();
645   int field_index = FieldIndexOf(access);
646   if (field_index >= 0) {
647     if (Node* replacement = state->LookupField(object, field_index)) {
648       // Make sure we don't resurrect dead {replacement} nodes.
649       if (!replacement->IsDead()) {
650         // We might need to guard the {replacement} if the type of the
651         // {node} is more precise than the type of the {replacement}.
652         Type* const node_type = NodeProperties::GetType(node);
653         if (!NodeProperties::GetType(replacement)->Is(node_type)) {
654           replacement = graph()->NewNode(common()->TypeGuard(node_type),
655                                          replacement, control);
656         }
657         ReplaceWithValue(node, replacement, effect);
658         return Replace(replacement);
659       }
660     }
661     state = state->AddField(object, field_index, node, zone());
662   }
663   return UpdateState(node, state);
664 }
665 
ReduceStoreField(Node * node)666 Reduction LoadElimination::ReduceStoreField(Node* node) {
667   FieldAccess const& access = FieldAccessOf(node->op());
668   Node* const object = NodeProperties::GetValueInput(node, 0);
669   Node* const new_value = NodeProperties::GetValueInput(node, 1);
670   Node* const effect = NodeProperties::GetEffectInput(node);
671   AbstractState const* state = node_states_.Get(effect);
672   if (state == nullptr) return NoChange();
673   int field_index = FieldIndexOf(access);
674   if (field_index >= 0) {
675     Node* const old_value = state->LookupField(object, field_index);
676     if (old_value == new_value) {
677       // This store is fully redundant.
678       return Replace(effect);
679     }
680     // Kill all potentially aliasing fields and record the new value.
681     state = state->KillField(object, field_index, zone());
682     state = state->AddField(object, field_index, new_value, zone());
683   } else {
684     // Unsupported StoreField operator.
685     state = state->KillFields(object, zone());
686   }
687   return UpdateState(node, state);
688 }
689 
ReduceLoadElement(Node * node)690 Reduction LoadElimination::ReduceLoadElement(Node* node) {
691   Node* const object = NodeProperties::GetValueInput(node, 0);
692   Node* const index = NodeProperties::GetValueInput(node, 1);
693   Node* const effect = NodeProperties::GetEffectInput(node);
694   Node* const control = NodeProperties::GetControlInput(node);
695   AbstractState const* state = node_states_.Get(effect);
696   if (state == nullptr) return NoChange();
697   if (Node* replacement = state->LookupElement(object, index)) {
698     // Make sure we don't resurrect dead {replacement} nodes.
699     if (!replacement->IsDead()) {
700       // We might need to guard the {replacement} if the type of the
701       // {node} is more precise than the type of the {replacement}.
702       Type* const node_type = NodeProperties::GetType(node);
703       if (!NodeProperties::GetType(replacement)->Is(node_type)) {
704         replacement = graph()->NewNode(common()->TypeGuard(node_type),
705                                        replacement, control);
706       }
707       ReplaceWithValue(node, replacement, effect);
708       return Replace(replacement);
709     }
710   }
711   state = state->AddElement(object, index, node, zone());
712   return UpdateState(node, state);
713 }
714 
ReduceStoreElement(Node * node)715 Reduction LoadElimination::ReduceStoreElement(Node* node) {
716   ElementAccess const& access = ElementAccessOf(node->op());
717   Node* const object = NodeProperties::GetValueInput(node, 0);
718   Node* const index = NodeProperties::GetValueInput(node, 1);
719   Node* const new_value = NodeProperties::GetValueInput(node, 2);
720   Node* const effect = NodeProperties::GetEffectInput(node);
721   AbstractState const* state = node_states_.Get(effect);
722   if (state == nullptr) return NoChange();
723   Node* const old_value = state->LookupElement(object, index);
724   if (old_value == new_value) {
725     // This store is fully redundant.
726     return Replace(effect);
727   }
728   // Kill all potentially aliasing elements.
729   state = state->KillElement(object, index, zone());
730   // Only record the new value if the store doesn't have an implicit truncation.
731   switch (access.machine_type.representation()) {
732     case MachineRepresentation::kNone:
733     case MachineRepresentation::kBit:
734       UNREACHABLE();
735       break;
736     case MachineRepresentation::kWord8:
737     case MachineRepresentation::kWord16:
738     case MachineRepresentation::kWord32:
739     case MachineRepresentation::kWord64:
740     case MachineRepresentation::kFloat32:
741       // TODO(turbofan): Add support for doing the truncations.
742       break;
743     case MachineRepresentation::kFloat64:
744     case MachineRepresentation::kSimd128:
745     case MachineRepresentation::kTaggedSigned:
746     case MachineRepresentation::kTaggedPointer:
747     case MachineRepresentation::kTagged:
748       state = state->AddElement(object, index, new_value, zone());
749       break;
750   }
751   return UpdateState(node, state);
752 }
753 
ReduceStoreTypedElement(Node * node)754 Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
755   Node* const effect = NodeProperties::GetEffectInput(node);
756   AbstractState const* state = node_states_.Get(effect);
757   if (state == nullptr) return NoChange();
758   return UpdateState(node, state);
759 }
760 
ReduceEffectPhi(Node * node)761 Reduction LoadElimination::ReduceEffectPhi(Node* node) {
762   Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
763   Node* const control = NodeProperties::GetControlInput(node);
764   AbstractState const* state0 = node_states_.Get(effect0);
765   if (state0 == nullptr) return NoChange();
766   if (control->opcode() == IrOpcode::kLoop) {
767     // Here we rely on having only reducible loops:
768     // The loop entry edge always dominates the header, so we can just take
769     // the state from the first input, and compute the loop state based on it.
770     AbstractState const* state = ComputeLoopState(node, state0);
771     return UpdateState(node, state);
772   }
773   DCHECK_EQ(IrOpcode::kMerge, control->opcode());
774 
775   // Shortcut for the case when we do not know anything about some input.
776   int const input_count = node->op()->EffectInputCount();
777   for (int i = 1; i < input_count; ++i) {
778     Node* const effect = NodeProperties::GetEffectInput(node, i);
779     if (node_states_.Get(effect) == nullptr) return NoChange();
780   }
781 
782   // Make a copy of the first input's state and merge with the state
783   // from other inputs.
784   AbstractState* state = new (zone()) AbstractState(*state0);
785   for (int i = 1; i < input_count; ++i) {
786     Node* const input = NodeProperties::GetEffectInput(node, i);
787     state->Merge(node_states_.Get(input), zone());
788   }
789   return UpdateState(node, state);
790 }
791 
ReduceStart(Node * node)792 Reduction LoadElimination::ReduceStart(Node* node) {
793   return UpdateState(node, empty_state());
794 }
795 
ReduceOtherNode(Node * node)796 Reduction LoadElimination::ReduceOtherNode(Node* node) {
797   if (node->op()->EffectInputCount() == 1) {
798     if (node->op()->EffectOutputCount() == 1) {
799       Node* const effect = NodeProperties::GetEffectInput(node);
800       AbstractState const* state = node_states_.Get(effect);
801       // If we do not know anything about the predecessor, do not propagate
802       // just yet because we will have to recompute anyway once we compute
803       // the predecessor.
804       if (state == nullptr) return NoChange();
805       // Check if this {node} has some uncontrolled side effects.
806       if (!node->op()->HasProperty(Operator::kNoWrite)) {
807         state = empty_state();
808       }
809       return UpdateState(node, state);
810     } else {
811       // Effect terminators should be handled specially.
812       return NoChange();
813     }
814   }
815   DCHECK_EQ(0, node->op()->EffectInputCount());
816   DCHECK_EQ(0, node->op()->EffectOutputCount());
817   return NoChange();
818 }
819 
UpdateState(Node * node,AbstractState const * state)820 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
821   AbstractState const* original = node_states_.Get(node);
822   // Only signal that the {node} has Changed, if the information about {state}
823   // has changed wrt. the {original}.
824   if (state != original) {
825     if (original == nullptr || !state->Equals(original)) {
826       node_states_.Set(node, state);
827       return Changed(node);
828     }
829   }
830   return NoChange();
831 }
832 
ComputeLoopState(Node * node,AbstractState const * state) const833 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
834     Node* node, AbstractState const* state) const {
835   Node* const control = NodeProperties::GetControlInput(node);
836   ZoneQueue<Node*> queue(zone());
837   ZoneSet<Node*> visited(zone());
838   visited.insert(node);
839   for (int i = 1; i < control->InputCount(); ++i) {
840     queue.push(node->InputAt(i));
841   }
842   while (!queue.empty()) {
843     Node* const current = queue.front();
844     queue.pop();
845     if (visited.find(current) == visited.end()) {
846       visited.insert(current);
847       if (!current->op()->HasProperty(Operator::kNoWrite)) {
848         switch (current->opcode()) {
849           case IrOpcode::kEnsureWritableFastElements: {
850             Node* const object = NodeProperties::GetValueInput(current, 0);
851             state = state->KillField(
852                 object, FieldIndexOf(JSObject::kElementsOffset), zone());
853             break;
854           }
855           case IrOpcode::kMaybeGrowFastElements: {
856             GrowFastElementsFlags flags =
857                 GrowFastElementsFlagsOf(current->op());
858             Node* const object = NodeProperties::GetValueInput(current, 0);
859             state = state->KillField(
860                 object, FieldIndexOf(JSObject::kElementsOffset), zone());
861             if (flags & GrowFastElementsFlag::kArrayObject) {
862               state = state->KillField(
863                   object, FieldIndexOf(JSArray::kLengthOffset), zone());
864             }
865             break;
866           }
867           case IrOpcode::kTransitionElementsKind: {
868             Node* const object = NodeProperties::GetValueInput(current, 0);
869             state = state->KillField(
870                 object, FieldIndexOf(HeapObject::kMapOffset), zone());
871             state = state->KillField(
872                 object, FieldIndexOf(JSObject::kElementsOffset), zone());
873             break;
874           }
875           case IrOpcode::kStoreField: {
876             FieldAccess const& access = FieldAccessOf(current->op());
877             Node* const object = NodeProperties::GetValueInput(current, 0);
878             int field_index = FieldIndexOf(access);
879             if (field_index < 0) {
880               state = state->KillFields(object, zone());
881             } else {
882               state = state->KillField(object, field_index, zone());
883             }
884             break;
885           }
886           case IrOpcode::kStoreElement: {
887             Node* const object = NodeProperties::GetValueInput(current, 0);
888             Node* const index = NodeProperties::GetValueInput(current, 1);
889             state = state->KillElement(object, index, zone());
890             break;
891           }
892           case IrOpcode::kStoreBuffer:
893           case IrOpcode::kStoreTypedElement: {
894             // Doesn't affect anything we track with the state currently.
895             break;
896           }
897           default:
898             return empty_state();
899         }
900       }
901       for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
902         queue.push(NodeProperties::GetEffectInput(current, i));
903       }
904     }
905   }
906   return state;
907 }
908 
909 // static
FieldIndexOf(int offset)910 int LoadElimination::FieldIndexOf(int offset) {
911   DCHECK_EQ(0, offset % kPointerSize);
912   int field_index = offset / kPointerSize;
913   if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
914   return field_index;
915 }
916 
917 // static
FieldIndexOf(FieldAccess const & access)918 int LoadElimination::FieldIndexOf(FieldAccess const& access) {
919   MachineRepresentation rep = access.machine_type.representation();
920   switch (rep) {
921     case MachineRepresentation::kNone:
922     case MachineRepresentation::kBit:
923     case MachineRepresentation::kSimd128:
924       UNREACHABLE();
925       break;
926     case MachineRepresentation::kWord32:
927     case MachineRepresentation::kWord64:
928       if (rep != MachineType::PointerRepresentation()) {
929         return -1;  // We currently only track pointer size fields.
930       }
931       break;
932     case MachineRepresentation::kWord8:
933     case MachineRepresentation::kWord16:
934     case MachineRepresentation::kFloat32:
935       return -1;  // Currently untracked.
936     case MachineRepresentation::kFloat64:
937       if (kDoubleSize != kPointerSize) {
938         return -1;  // We currently only track pointer size fields.
939       }
940     // Fall through.
941     case MachineRepresentation::kTaggedSigned:
942     case MachineRepresentation::kTaggedPointer:
943     case MachineRepresentation::kTagged:
944       // TODO(bmeurer): Check that we never do overlapping load/stores of
945       // individual parts of Float64 values.
946       break;
947   }
948   if (access.base_is_tagged != kTaggedBase) {
949     return -1;  // We currently only track tagged objects.
950   }
951   return FieldIndexOf(access.offset);
952 }
953 
common() const954 CommonOperatorBuilder* LoadElimination::common() const {
955   return jsgraph()->common();
956 }
957 
graph() const958 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
959 
960 }  // namespace compiler
961 }  // namespace internal
962 }  // namespace v8
963