• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/heap/factory.h"
12 #include "src/objects-inl.h"
13 
14 namespace v8 {
15 namespace internal {
16 namespace compiler {
17 
18 namespace {
19 
IsRename(Node * node)20 bool IsRename(Node* node) {
21   switch (node->opcode()) {
22     case IrOpcode::kCheckHeapObject:
23     case IrOpcode::kFinishRegion:
24     case IrOpcode::kTypeGuard:
25       return true;
26     default:
27       return false;
28   }
29 }
30 
ResolveRenames(Node * node)31 Node* ResolveRenames(Node* node) {
32   while (IsRename(node)) {
33     node = node->InputAt(0);
34   }
35   return node;
36 }
37 
MayAlias(Node * a,Node * b)38 bool MayAlias(Node* a, Node* b) {
39   if (a != b) {
40     if (!NodeProperties::GetType(a).Maybe(NodeProperties::GetType(b))) {
41       return false;
42     } else if (IsRename(b)) {
43       return MayAlias(a, b->InputAt(0));
44     } else if (IsRename(a)) {
45       return MayAlias(a->InputAt(0), b);
46     } else if (b->opcode() == IrOpcode::kAllocate) {
47       switch (a->opcode()) {
48         case IrOpcode::kAllocate:
49         case IrOpcode::kHeapConstant:
50         case IrOpcode::kParameter:
51           return false;
52         default:
53           break;
54       }
55     } else if (a->opcode() == IrOpcode::kAllocate) {
56       switch (b->opcode()) {
57         case IrOpcode::kHeapConstant:
58         case IrOpcode::kParameter:
59           return false;
60         default:
61           break;
62       }
63     }
64   }
65   return true;
66 }
67 
MustAlias(Node * a,Node * b)68 bool MustAlias(Node* a, Node* b) {
69   return ResolveRenames(a) == ResolveRenames(b);
70 }
71 
72 }  // namespace
73 
Reduce(Node * node)74 Reduction LoadElimination::Reduce(Node* node) {
75   if (FLAG_trace_turbo_load_elimination) {
76     if (node->op()->EffectInputCount() > 0) {
77       PrintF(" visit #%d:%s", node->id(), node->op()->mnemonic());
78       if (node->op()->ValueInputCount() > 0) {
79         PrintF("(");
80         for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
81           if (i > 0) PrintF(", ");
82           Node* const value = NodeProperties::GetValueInput(node, i);
83           PrintF("#%d:%s", value->id(), value->op()->mnemonic());
84         }
85         PrintF(")");
86       }
87       PrintF("\n");
88       for (int i = 0; i < node->op()->EffectInputCount(); ++i) {
89         Node* const effect = NodeProperties::GetEffectInput(node, i);
90         if (AbstractState const* const state = node_states_.Get(effect)) {
91           PrintF("  state[%i]: #%d:%s\n", i, effect->id(),
92                  effect->op()->mnemonic());
93           state->Print();
94         } else {
95           PrintF("  no state[%i]: #%d:%s\n", i, effect->id(),
96                  effect->op()->mnemonic());
97         }
98       }
99     }
100   }
101   switch (node->opcode()) {
102     case IrOpcode::kArrayBufferWasNeutered:
103       return ReduceArrayBufferWasNeutered(node);
104     case IrOpcode::kMapGuard:
105       return ReduceMapGuard(node);
106     case IrOpcode::kCheckMaps:
107       return ReduceCheckMaps(node);
108     case IrOpcode::kCompareMaps:
109       return ReduceCompareMaps(node);
110     case IrOpcode::kEnsureWritableFastElements:
111       return ReduceEnsureWritableFastElements(node);
112     case IrOpcode::kMaybeGrowFastElements:
113       return ReduceMaybeGrowFastElements(node);
114     case IrOpcode::kTransitionElementsKind:
115       return ReduceTransitionElementsKind(node);
116     case IrOpcode::kLoadField:
117       return ReduceLoadField(node);
118     case IrOpcode::kStoreField:
119       return ReduceStoreField(node);
120     case IrOpcode::kLoadElement:
121       return ReduceLoadElement(node);
122     case IrOpcode::kStoreElement:
123       return ReduceStoreElement(node);
124     case IrOpcode::kTransitionAndStoreElement:
125       return ReduceTransitionAndStoreElement(node);
126     case IrOpcode::kStoreTypedElement:
127       return ReduceStoreTypedElement(node);
128     case IrOpcode::kEffectPhi:
129       return ReduceEffectPhi(node);
130     case IrOpcode::kDead:
131       break;
132     case IrOpcode::kStart:
133       return ReduceStart(node);
134     default:
135       return ReduceOtherNode(node);
136   }
137   return NoChange();
138 }
139 
140 namespace {
141 
LoadEliminationIsCompatibleCheck(Node const * a,Node const * b)142 bool LoadEliminationIsCompatibleCheck(Node const* a, Node const* b) {
143   if (a->op() != b->op()) return false;
144   for (int i = a->op()->ValueInputCount(); --i >= 0;) {
145     if (!MustAlias(a->InputAt(i), b->InputAt(i))) return false;
146   }
147   return true;
148 }
149 
150 }  // namespace
151 
Lookup(Node * node) const152 Node* LoadElimination::AbstractChecks::Lookup(Node* node) const {
153   for (Node* const check : nodes_) {
154     if (check && !check->IsDead() &&
155         LoadEliminationIsCompatibleCheck(check, node)) {
156       return check;
157     }
158   }
159   return nullptr;
160 }
161 
Equals(AbstractChecks const * that) const162 bool LoadElimination::AbstractChecks::Equals(AbstractChecks const* that) const {
163   if (this == that) return true;
164   for (size_t i = 0; i < arraysize(nodes_); ++i) {
165     if (Node* this_node = this->nodes_[i]) {
166       for (size_t j = 0;; ++j) {
167         if (j == arraysize(nodes_)) return false;
168         if (that->nodes_[j] == this_node) break;
169       }
170     }
171   }
172   for (size_t i = 0; i < arraysize(nodes_); ++i) {
173     if (Node* that_node = that->nodes_[i]) {
174       for (size_t j = 0;; ++j) {
175         if (j == arraysize(nodes_)) return false;
176         if (this->nodes_[j] == that_node) break;
177       }
178     }
179   }
180   return true;
181 }
182 
Merge(AbstractChecks const * that,Zone * zone) const183 LoadElimination::AbstractChecks const* LoadElimination::AbstractChecks::Merge(
184     AbstractChecks const* that, Zone* zone) const {
185   if (this->Equals(that)) return this;
186   AbstractChecks* copy = new (zone) AbstractChecks(zone);
187   for (Node* const this_node : this->nodes_) {
188     if (this_node == nullptr) continue;
189     for (Node* const that_node : that->nodes_) {
190       if (this_node == that_node) {
191         copy->nodes_[copy->next_index_++] = this_node;
192         break;
193       }
194     }
195   }
196   copy->next_index_ %= arraysize(nodes_);
197   return copy;
198 }
199 
Print() const200 void LoadElimination::AbstractChecks::Print() const {
201   for (Node* const node : nodes_) {
202     if (node != nullptr) {
203       PrintF("    #%d:%s\n", node->id(), node->op()->mnemonic());
204     }
205   }
206 }
207 
208 namespace {
209 
IsCompatible(MachineRepresentation r1,MachineRepresentation r2)210 bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
211   if (r1 == r2) return true;
212   return IsAnyTagged(r1) && IsAnyTagged(r2);
213 }
214 
215 }  // namespace
216 
Lookup(Node * object,Node * index,MachineRepresentation representation) const217 Node* LoadElimination::AbstractElements::Lookup(
218     Node* object, Node* index, MachineRepresentation representation) const {
219   for (Element const element : elements_) {
220     if (element.object == nullptr) continue;
221     DCHECK_NOT_NULL(element.index);
222     DCHECK_NOT_NULL(element.value);
223     if (MustAlias(object, element.object) && MustAlias(index, element.index) &&
224         IsCompatible(representation, element.representation)) {
225       return element.value;
226     }
227   }
228   return nullptr;
229 }
230 
231 LoadElimination::AbstractElements const*
Kill(Node * object,Node * index,Zone * zone) const232 LoadElimination::AbstractElements::Kill(Node* object, Node* index,
233                                         Zone* zone) const {
234   for (Element const element : this->elements_) {
235     if (element.object == nullptr) continue;
236     if (MayAlias(object, element.object)) {
237       AbstractElements* that = new (zone) AbstractElements(zone);
238       for (Element const element : this->elements_) {
239         if (element.object == nullptr) continue;
240         DCHECK_NOT_NULL(element.index);
241         DCHECK_NOT_NULL(element.value);
242         if (!MayAlias(object, element.object) ||
243             !NodeProperties::GetType(index).Maybe(
244                 NodeProperties::GetType(element.index))) {
245           that->elements_[that->next_index_++] = element;
246         }
247       }
248       that->next_index_ %= arraysize(elements_);
249       return that;
250     }
251   }
252   return this;
253 }
254 
Equals(AbstractElements const * that) const255 bool LoadElimination::AbstractElements::Equals(
256     AbstractElements const* that) const {
257   if (this == that) return true;
258   for (size_t i = 0; i < arraysize(elements_); ++i) {
259     Element this_element = this->elements_[i];
260     if (this_element.object == nullptr) continue;
261     for (size_t j = 0;; ++j) {
262       if (j == arraysize(elements_)) return false;
263       Element that_element = that->elements_[j];
264       if (this_element.object == that_element.object &&
265           this_element.index == that_element.index &&
266           this_element.value == that_element.value) {
267         break;
268       }
269     }
270   }
271   for (size_t i = 0; i < arraysize(elements_); ++i) {
272     Element that_element = that->elements_[i];
273     if (that_element.object == nullptr) continue;
274     for (size_t j = 0;; ++j) {
275       if (j == arraysize(elements_)) return false;
276       Element this_element = this->elements_[j];
277       if (that_element.object == this_element.object &&
278           that_element.index == this_element.index &&
279           that_element.value == this_element.value) {
280         break;
281       }
282     }
283   }
284   return true;
285 }
286 
287 LoadElimination::AbstractElements const*
Merge(AbstractElements const * that,Zone * zone) const288 LoadElimination::AbstractElements::Merge(AbstractElements const* that,
289                                          Zone* zone) const {
290   if (this->Equals(that)) return this;
291   AbstractElements* copy = new (zone) AbstractElements(zone);
292   for (Element const this_element : this->elements_) {
293     if (this_element.object == nullptr) continue;
294     for (Element const that_element : that->elements_) {
295       if (this_element.object == that_element.object &&
296           this_element.index == that_element.index &&
297           this_element.value == that_element.value) {
298         copy->elements_[copy->next_index_++] = this_element;
299         break;
300       }
301     }
302   }
303   copy->next_index_ %= arraysize(elements_);
304   return copy;
305 }
306 
Print() const307 void LoadElimination::AbstractElements::Print() const {
308   for (Element const& element : elements_) {
309     if (element.object) {
310       PrintF("    #%d:%s @ #%d:%s -> #%d:%s\n", element.object->id(),
311              element.object->op()->mnemonic(), element.index->id(),
312              element.index->op()->mnemonic(), element.value->id(),
313              element.value->op()->mnemonic());
314     }
315   }
316 }
317 
Lookup(Node * object) const318 Node* LoadElimination::AbstractField::Lookup(Node* object) const {
319   for (auto pair : info_for_node_) {
320     if (pair.first->IsDead()) continue;
321     if (MustAlias(object, pair.first)) return pair.second.value;
322   }
323   return nullptr;
324 }
325 
326 namespace {
327 
MayAlias(MaybeHandle<Name> x,MaybeHandle<Name> y)328 bool MayAlias(MaybeHandle<Name> x, MaybeHandle<Name> y) {
329   if (!x.address()) return true;
330   if (!y.address()) return true;
331   if (x.address() != y.address()) return false;
332   return true;
333 }
334 
335 }  // namespace
336 
337 class LoadElimination::AliasStateInfo {
338  public:
AliasStateInfo(const AbstractState * state,Node * object,Handle<Map> map)339   AliasStateInfo(const AbstractState* state, Node* object, Handle<Map> map)
340       : state_(state), object_(object), map_(map) {}
AliasStateInfo(const AbstractState * state,Node * object)341   AliasStateInfo(const AbstractState* state, Node* object)
342       : state_(state), object_(object) {}
343 
344   bool MayAlias(Node* other) const;
345 
346  private:
347   const AbstractState* state_;
348   Node* object_;
349   MaybeHandle<Map> map_;
350 };
351 
Kill(const AliasStateInfo & alias_info,MaybeHandle<Name> name,Zone * zone) const352 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill(
353     const AliasStateInfo& alias_info, MaybeHandle<Name> name,
354     Zone* zone) const {
355   for (auto pair : this->info_for_node_) {
356     if (pair.first->IsDead()) continue;
357     if (alias_info.MayAlias(pair.first)) {
358       AbstractField* that = new (zone) AbstractField(zone);
359       for (auto pair : this->info_for_node_) {
360         if (!alias_info.MayAlias(pair.first) ||
361             !MayAlias(name, pair.second.name)) {
362           that->info_for_node_.insert(pair);
363         }
364       }
365       return that;
366     }
367   }
368   return this;
369 }
370 
Print() const371 void LoadElimination::AbstractField::Print() const {
372   for (auto pair : info_for_node_) {
373     PrintF("    #%d:%s -> #%d:%s\n", pair.first->id(),
374            pair.first->op()->mnemonic(), pair.second.value->id(),
375            pair.second.value->op()->mnemonic());
376   }
377 }
378 
AbstractMaps(Zone * zone)379 LoadElimination::AbstractMaps::AbstractMaps(Zone* zone)
380     : info_for_node_(zone) {}
381 
AbstractMaps(Node * object,ZoneHandleSet<Map> maps,Zone * zone)382 LoadElimination::AbstractMaps::AbstractMaps(Node* object,
383                                             ZoneHandleSet<Map> maps, Zone* zone)
384     : info_for_node_(zone) {
385   object = ResolveRenames(object);
386   info_for_node_.insert(std::make_pair(object, maps));
387 }
388 
Lookup(Node * object,ZoneHandleSet<Map> * object_maps) const389 bool LoadElimination::AbstractMaps::Lookup(
390     Node* object, ZoneHandleSet<Map>* object_maps) const {
391   auto it = info_for_node_.find(ResolveRenames(object));
392   if (it == info_for_node_.end()) return false;
393   *object_maps = it->second;
394   return true;
395 }
396 
Kill(const AliasStateInfo & alias_info,Zone * zone) const397 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Kill(
398     const AliasStateInfo& alias_info, Zone* zone) const {
399   for (auto pair : this->info_for_node_) {
400     if (alias_info.MayAlias(pair.first)) {
401       AbstractMaps* that = new (zone) AbstractMaps(zone);
402       for (auto pair : this->info_for_node_) {
403         if (!alias_info.MayAlias(pair.first)) that->info_for_node_.insert(pair);
404       }
405       return that;
406     }
407   }
408   return this;
409 }
410 
Merge(AbstractMaps const * that,Zone * zone) const411 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Merge(
412     AbstractMaps const* that, Zone* zone) const {
413   if (this->Equals(that)) return this;
414   AbstractMaps* copy = new (zone) AbstractMaps(zone);
415   for (auto this_it : this->info_for_node_) {
416     Node* this_object = this_it.first;
417     ZoneHandleSet<Map> this_maps = this_it.second;
418     auto that_it = that->info_for_node_.find(this_object);
419     if (that_it != that->info_for_node_.end() && that_it->second == this_maps) {
420       copy->info_for_node_.insert(this_it);
421     }
422   }
423   return copy;
424 }
425 
Extend(Node * object,ZoneHandleSet<Map> maps,Zone * zone) const426 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Extend(
427     Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
428   AbstractMaps* that = new (zone) AbstractMaps(zone);
429   that->info_for_node_ = this->info_for_node_;
430   object = ResolveRenames(object);
431   that->info_for_node_[object] = maps;
432   return that;
433 }
434 
Print() const435 void LoadElimination::AbstractMaps::Print() const {
436   AllowHandleDereference allow_handle_dereference;
437   StdoutStream os;
438   for (auto pair : info_for_node_) {
439     os << "    #" << pair.first->id() << ":" << pair.first->op()->mnemonic()
440        << std::endl;
441     ZoneHandleSet<Map> const& maps = pair.second;
442     for (size_t i = 0; i < maps.size(); ++i) {
443       os << "     - " << Brief(*maps[i]) << std::endl;
444     }
445   }
446 }
447 
Equals(AbstractState const * that) const448 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
449   if (this->checks_) {
450     if (!that->checks_ || !that->checks_->Equals(this->checks_)) {
451       return false;
452     }
453   } else if (that->checks_) {
454     return false;
455   }
456   if (this->elements_) {
457     if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
458       return false;
459     }
460   } else if (that->elements_) {
461     return false;
462   }
463   for (size_t i = 0u; i < arraysize(fields_); ++i) {
464     AbstractField const* this_field = this->fields_[i];
465     AbstractField const* that_field = that->fields_[i];
466     if (this_field) {
467       if (!that_field || !that_field->Equals(this_field)) return false;
468     } else if (that_field) {
469       return false;
470     }
471   }
472   if (this->maps_) {
473     if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
474       return false;
475     }
476   } else if (that->maps_) {
477     return false;
478   }
479   return true;
480 }
481 
Merge(AbstractState const * that,Zone * zone)482 void LoadElimination::AbstractState::Merge(AbstractState const* that,
483                                            Zone* zone) {
484   // Merge the information we have about the checks.
485   if (this->checks_) {
486     this->checks_ =
487         that->checks_ ? that->checks_->Merge(this->checks_, zone) : nullptr;
488   }
489 
490   // Merge the information we have about the elements.
491   if (this->elements_) {
492     this->elements_ = that->elements_
493                           ? that->elements_->Merge(this->elements_, zone)
494                           : nullptr;
495   }
496 
497   // Merge the information we have about the fields.
498   for (size_t i = 0; i < arraysize(fields_); ++i) {
499     if (this->fields_[i]) {
500       if (that->fields_[i]) {
501         this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone);
502       } else {
503         this->fields_[i] = nullptr;
504       }
505     }
506   }
507 
508   // Merge the information we have about the maps.
509   if (this->maps_) {
510     this->maps_ = that->maps_ ? that->maps_->Merge(this->maps_, zone) : nullptr;
511   }
512 }
513 
LookupCheck(Node * node) const514 Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
515   return this->checks_ ? this->checks_->Lookup(node) : nullptr;
516 }
517 
AddCheck(Node * node,Zone * zone) const518 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck(
519     Node* node, Zone* zone) const {
520   AbstractState* that = new (zone) AbstractState(*this);
521   if (that->checks_) {
522     that->checks_ = that->checks_->Extend(node, zone);
523   } else {
524     that->checks_ = new (zone) AbstractChecks(node, zone);
525   }
526   return that;
527 }
528 
LookupMaps(Node * object,ZoneHandleSet<Map> * object_map) const529 bool LoadElimination::AbstractState::LookupMaps(
530     Node* object, ZoneHandleSet<Map>* object_map) const {
531   return this->maps_ && this->maps_->Lookup(object, object_map);
532 }
533 
SetMaps(Node * object,ZoneHandleSet<Map> maps,Zone * zone) const534 LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps(
535     Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
536   AbstractState* that = new (zone) AbstractState(*this);
537   if (that->maps_) {
538     that->maps_ = that->maps_->Extend(object, maps, zone);
539   } else {
540     that->maps_ = new (zone) AbstractMaps(object, maps, zone);
541   }
542   return that;
543 }
544 
KillMaps(const AliasStateInfo & alias_info,Zone * zone) const545 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
546     const AliasStateInfo& alias_info, Zone* zone) const {
547   if (this->maps_) {
548     AbstractMaps const* that_maps = this->maps_->Kill(alias_info, zone);
549     if (this->maps_ != that_maps) {
550       AbstractState* that = new (zone) AbstractState(*this);
551       that->maps_ = that_maps;
552       return that;
553     }
554   }
555   return this;
556 }
557 
KillMaps(Node * object,Zone * zone) const558 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
559     Node* object, Zone* zone) const {
560   AliasStateInfo alias_info(this, object);
561   return KillMaps(alias_info, zone);
562 }
563 
LookupElement(Node * object,Node * index,MachineRepresentation representation) const564 Node* LoadElimination::AbstractState::LookupElement(
565     Node* object, Node* index, MachineRepresentation representation) const {
566   if (this->elements_) {
567     return this->elements_->Lookup(object, index, representation);
568   }
569   return nullptr;
570 }
571 
572 LoadElimination::AbstractState const*
AddElement(Node * object,Node * index,Node * value,MachineRepresentation representation,Zone * zone) const573 LoadElimination::AbstractState::AddElement(Node* object, Node* index,
574                                            Node* value,
575                                            MachineRepresentation representation,
576                                            Zone* zone) const {
577   AbstractState* that = new (zone) AbstractState(*this);
578   if (that->elements_) {
579     that->elements_ =
580         that->elements_->Extend(object, index, value, representation, zone);
581   } else {
582     that->elements_ =
583         new (zone) AbstractElements(object, index, value, representation, zone);
584   }
585   return that;
586 }
587 
588 LoadElimination::AbstractState const*
KillElement(Node * object,Node * index,Zone * zone) const589 LoadElimination::AbstractState::KillElement(Node* object, Node* index,
590                                             Zone* zone) const {
591   if (this->elements_) {
592     AbstractElements const* that_elements =
593         this->elements_->Kill(object, index, zone);
594     if (this->elements_ != that_elements) {
595       AbstractState* that = new (zone) AbstractState(*this);
596       that->elements_ = that_elements;
597       return that;
598     }
599   }
600   return this;
601 }
602 
AddField(Node * object,size_t index,Node * value,MaybeHandle<Name> name,Zone * zone) const603 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
604     Node* object, size_t index, Node* value, MaybeHandle<Name> name,
605     Zone* zone) const {
606   AbstractState* that = new (zone) AbstractState(*this);
607   if (that->fields_[index]) {
608     that->fields_[index] =
609         that->fields_[index]->Extend(object, value, name, zone);
610   } else {
611     that->fields_[index] = new (zone) AbstractField(object, value, name, zone);
612   }
613   return that;
614 }
615 
KillField(Node * object,size_t index,MaybeHandle<Name> name,Zone * zone) const616 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
617     Node* object, size_t index, MaybeHandle<Name> name, Zone* zone) const {
618   AliasStateInfo alias_info(this, object);
619   return KillField(alias_info, index, name, zone);
620 }
621 
KillField(const AliasStateInfo & alias_info,size_t index,MaybeHandle<Name> name,Zone * zone) const622 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
623     const AliasStateInfo& alias_info, size_t index, MaybeHandle<Name> name,
624     Zone* zone) const {
625   if (AbstractField const* this_field = this->fields_[index]) {
626     this_field = this_field->Kill(alias_info, name, zone);
627     if (this->fields_[index] != this_field) {
628       AbstractState* that = new (zone) AbstractState(*this);
629       that->fields_[index] = this_field;
630       return that;
631     }
632   }
633   return this;
634 }
635 
636 LoadElimination::AbstractState const*
KillFields(Node * object,MaybeHandle<Name> name,Zone * zone) const637 LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
638                                            Zone* zone) const {
639   AliasStateInfo alias_info(this, object);
640   for (size_t i = 0;; ++i) {
641     if (i == arraysize(fields_)) return this;
642     if (AbstractField const* this_field = this->fields_[i]) {
643       AbstractField const* that_field =
644           this_field->Kill(alias_info, name, zone);
645       if (that_field != this_field) {
646         AbstractState* that = new (zone) AbstractState(*this);
647         that->fields_[i] = that_field;
648         while (++i < arraysize(fields_)) {
649           if (this->fields_[i] != nullptr) {
650             that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone);
651           }
652         }
653         return that;
654       }
655     }
656   }
657 }
658 
LookupField(Node * object,size_t index) const659 Node* LoadElimination::AbstractState::LookupField(Node* object,
660                                                   size_t index) const {
661   if (AbstractField const* this_field = this->fields_[index]) {
662     return this_field->Lookup(object);
663   }
664   return nullptr;
665 }
666 
MayAlias(Node * other) const667 bool LoadElimination::AliasStateInfo::MayAlias(Node* other) const {
668   // If {object} is being initialized right here (indicated by {object} being
669   // an Allocate node instead of a FinishRegion node), we know that {other}
670   // can only alias with {object} if they refer to exactly the same node.
671   if (object_->opcode() == IrOpcode::kAllocate) {
672     return object_ == other;
673   }
674   // Decide aliasing based on the node kinds.
675   if (!compiler::MayAlias(object_, other)) {
676     return false;
677   }
678   // Decide aliasing based on maps (if available).
679   Handle<Map> map;
680   if (map_.ToHandle(&map)) {
681     ZoneHandleSet<Map> other_maps;
682     if (state_->LookupMaps(other, &other_maps) && other_maps.size() == 1) {
683       if (map.address() != other_maps.at(0).address()) {
684         return false;
685       }
686     }
687   }
688   return true;
689 }
690 
Print() const691 void LoadElimination::AbstractState::Print() const {
692   if (checks_) {
693     PrintF("   checks:\n");
694     checks_->Print();
695   }
696   if (maps_) {
697     PrintF("   maps:\n");
698     maps_->Print();
699   }
700   if (elements_) {
701     PrintF("   elements:\n");
702     elements_->Print();
703   }
704   for (size_t i = 0; i < arraysize(fields_); ++i) {
705     if (AbstractField const* const field = fields_[i]) {
706       PrintF("   field %zu:\n", i);
707       field->Print();
708     }
709   }
710 }
711 
712 LoadElimination::AbstractState const*
Get(Node * node) const713 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
714   size_t const id = node->id();
715   if (id < info_for_node_.size()) return info_for_node_[id];
716   return nullptr;
717 }
718 
Set(Node * node,AbstractState const * state)719 void LoadElimination::AbstractStateForEffectNodes::Set(
720     Node* node, AbstractState const* state) {
721   size_t const id = node->id();
722   if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
723   info_for_node_[id] = state;
724 }
725 
ReduceArrayBufferWasNeutered(Node * node)726 Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
727   Node* const effect = NodeProperties::GetEffectInput(node);
728   AbstractState const* state = node_states_.Get(effect);
729   if (state == nullptr) return NoChange();
730   if (Node* const check = state->LookupCheck(node)) {
731     ReplaceWithValue(node, check, effect);
732     return Replace(check);
733   }
734   state = state->AddCheck(node, zone());
735   return UpdateState(node, state);
736 }
737 
ReduceMapGuard(Node * node)738 Reduction LoadElimination::ReduceMapGuard(Node* node) {
739   ZoneHandleSet<Map> const maps = MapGuardMapsOf(node->op()).maps();
740   Node* const object = NodeProperties::GetValueInput(node, 0);
741   Node* const effect = NodeProperties::GetEffectInput(node);
742   AbstractState const* state = node_states_.Get(effect);
743   if (state == nullptr) return NoChange();
744   ZoneHandleSet<Map> object_maps;
745   if (state->LookupMaps(object, &object_maps)) {
746     if (maps.contains(object_maps)) return Replace(effect);
747     // TODO(turbofan): Compute the intersection.
748   }
749   state = state->SetMaps(object, maps, zone());
750   return UpdateState(node, state);
751 }
752 
ReduceCheckMaps(Node * node)753 Reduction LoadElimination::ReduceCheckMaps(Node* node) {
754   ZoneHandleSet<Map> const maps = CheckMapsParametersOf(node->op()).maps();
755   Node* const object = NodeProperties::GetValueInput(node, 0);
756   Node* const effect = NodeProperties::GetEffectInput(node);
757   AbstractState const* state = node_states_.Get(effect);
758   if (state == nullptr) return NoChange();
759   ZoneHandleSet<Map> object_maps;
760   if (state->LookupMaps(object, &object_maps)) {
761     if (maps.contains(object_maps)) return Replace(effect);
762     // TODO(turbofan): Compute the intersection.
763   }
764   state = state->SetMaps(object, maps, zone());
765   return UpdateState(node, state);
766 }
767 
ReduceCompareMaps(Node * node)768 Reduction LoadElimination::ReduceCompareMaps(Node* node) {
769   ZoneHandleSet<Map> const maps = CompareMapsParametersOf(node->op()).maps();
770   Node* const object = NodeProperties::GetValueInput(node, 0);
771   Node* const effect = NodeProperties::GetEffectInput(node);
772   AbstractState const* state = node_states_.Get(effect);
773   if (state == nullptr) return NoChange();
774   ZoneHandleSet<Map> object_maps;
775   if (state->LookupMaps(object, &object_maps)) {
776     if (maps.contains(object_maps)) {
777       Node* value = jsgraph()->TrueConstant();
778       ReplaceWithValue(node, value, effect);
779       return Replace(value);
780     }
781     // TODO(turbofan): Compute the intersection.
782   }
783   return UpdateState(node, state);
784 }
785 
ReduceEnsureWritableFastElements(Node * node)786 Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
787   Node* const object = NodeProperties::GetValueInput(node, 0);
788   Node* const elements = NodeProperties::GetValueInput(node, 1);
789   Node* const effect = NodeProperties::GetEffectInput(node);
790   AbstractState const* state = node_states_.Get(effect);
791   if (state == nullptr) return NoChange();
792     // Check if the {elements} already have the fixed array map.
793   ZoneHandleSet<Map> elements_maps;
794   ZoneHandleSet<Map> fixed_array_maps(factory()->fixed_array_map());
795   if (state->LookupMaps(elements, &elements_maps) &&
796       fixed_array_maps.contains(elements_maps)) {
797     ReplaceWithValue(node, elements, effect);
798     return Replace(elements);
799   }
800   // We know that the resulting elements have the fixed array map.
801   state = state->SetMaps(node, fixed_array_maps, zone());
802   // Kill the previous elements on {object}.
803   state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
804                            MaybeHandle<Name>(), zone());
805   // Add the new elements on {object}.
806   state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
807                           MaybeHandle<Name>(), zone());
808   return UpdateState(node, state);
809 }
810 
ReduceMaybeGrowFastElements(Node * node)811 Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
812   GrowFastElementsParameters params = GrowFastElementsParametersOf(node->op());
813   Node* const object = NodeProperties::GetValueInput(node, 0);
814   Node* const effect = NodeProperties::GetEffectInput(node);
815   AbstractState const* state = node_states_.Get(effect);
816   if (state == nullptr) return NoChange();
817   if (params.mode() == GrowFastElementsMode::kDoubleElements) {
818     // We know that the resulting elements have the fixed double array map.
819     state = state->SetMaps(
820         node, ZoneHandleSet<Map>(factory()->fixed_double_array_map()), zone());
821   } else {
822     // We know that the resulting elements have the fixed array map or the COW
823     // version thereof (if we didn't grow and it was already COW before).
824     ZoneHandleSet<Map> fixed_array_maps(factory()->fixed_array_map());
825     fixed_array_maps.insert(factory()->fixed_cow_array_map(), zone());
826     state = state->SetMaps(node, fixed_array_maps, zone());
827   }
828   // Kill the previous elements on {object}.
829   state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
830                            MaybeHandle<Name>(), zone());
831   // Add the new elements on {object}.
832   state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
833                           MaybeHandle<Name>(), zone());
834   return UpdateState(node, state);
835 }
836 
ReduceTransitionElementsKind(Node * node)837 Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
838   ElementsTransition transition = ElementsTransitionOf(node->op());
839   Node* const object = NodeProperties::GetValueInput(node, 0);
840   Handle<Map> source_map(transition.source());
841   Handle<Map> target_map(transition.target());
842   Node* const effect = NodeProperties::GetEffectInput(node);
843   AbstractState const* state = node_states_.Get(effect);
844   if (state == nullptr) return NoChange();
845   switch (transition.mode()) {
846     case ElementsTransition::kFastTransition:
847       break;
848     case ElementsTransition::kSlowTransition:
849       // Kill the elements as well.
850       AliasStateInfo alias_info(state, object, source_map);
851       state =
852           state->KillField(alias_info, FieldIndexOf(JSObject::kElementsOffset),
853                            MaybeHandle<Name>(), zone());
854       break;
855   }
856   ZoneHandleSet<Map> object_maps;
857   if (state->LookupMaps(object, &object_maps)) {
858     if (ZoneHandleSet<Map>(target_map).contains(object_maps)) {
859       // The {object} already has the {target_map}, so this TransitionElements
860       // {node} is fully redundant (independent of what {source_map} is).
861       return Replace(effect);
862     }
863     if (object_maps.contains(ZoneHandleSet<Map>(source_map))) {
864       object_maps.remove(source_map, zone());
865       object_maps.insert(target_map, zone());
866       AliasStateInfo alias_info(state, object, source_map);
867       state = state->KillMaps(alias_info, zone());
868       state = state->SetMaps(object, object_maps, zone());
869     }
870   } else {
871     AliasStateInfo alias_info(state, object, source_map);
872     state = state->KillMaps(alias_info, zone());
873   }
874   return UpdateState(node, state);
875 }
876 
ReduceTransitionAndStoreElement(Node * node)877 Reduction LoadElimination::ReduceTransitionAndStoreElement(Node* node) {
878   Node* const object = NodeProperties::GetValueInput(node, 0);
879   Handle<Map> double_map(DoubleMapParameterOf(node->op()));
880   Handle<Map> fast_map(FastMapParameterOf(node->op()));
881   Node* const effect = NodeProperties::GetEffectInput(node);
882   AbstractState const* state = node_states_.Get(effect);
883   if (state == nullptr) return NoChange();
884 
885   // We need to add the double and fast maps to the set of possible maps for
886   // this object, because we don't know which of those we'll transition to.
887   // Additionally, we should kill all alias information.
888   ZoneHandleSet<Map> object_maps;
889   if (state->LookupMaps(object, &object_maps)) {
890     object_maps.insert(double_map, zone());
891     object_maps.insert(fast_map, zone());
892     state = state->KillMaps(object, zone());
893     state = state->SetMaps(object, object_maps, zone());
894   }
895   // Kill the elements as well.
896   state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
897                            MaybeHandle<Name>(), zone());
898   return UpdateState(node, state);
899 }
900 
ReduceLoadField(Node * node)901 Reduction LoadElimination::ReduceLoadField(Node* node) {
902   FieldAccess const& access = FieldAccessOf(node->op());
903   Node* object = NodeProperties::GetValueInput(node, 0);
904   Node* effect = NodeProperties::GetEffectInput(node);
905   Node* control = NodeProperties::GetControlInput(node);
906   AbstractState const* state = node_states_.Get(effect);
907   if (state == nullptr) return NoChange();
908   if (access.offset == HeapObject::kMapOffset &&
909       access.base_is_tagged == kTaggedBase) {
910     DCHECK(IsAnyTagged(access.machine_type.representation()));
911     ZoneHandleSet<Map> object_maps;
912     if (state->LookupMaps(object, &object_maps) && object_maps.size() == 1) {
913       Node* value = jsgraph()->HeapConstant(object_maps[0]);
914       NodeProperties::SetType(value, Type::OtherInternal());
915       ReplaceWithValue(node, value, effect);
916       return Replace(value);
917     }
918   } else {
919     int field_index = FieldIndexOf(access);
920     if (field_index >= 0) {
921       if (Node* replacement = state->LookupField(object, field_index)) {
922         // Make sure we don't resurrect dead {replacement} nodes.
923         if (!replacement->IsDead()) {
924           // Introduce a TypeGuard if the type of the {replacement} node is not
925           // a subtype of the original {node}'s type.
926           if (!NodeProperties::GetType(replacement)
927                    .Is(NodeProperties::GetType(node))) {
928             Type replacement_type = Type::Intersect(
929                 NodeProperties::GetType(node),
930                 NodeProperties::GetType(replacement), graph()->zone());
931             replacement = effect =
932                 graph()->NewNode(common()->TypeGuard(replacement_type),
933                                  replacement, effect, control);
934             NodeProperties::SetType(replacement, replacement_type);
935           }
936           ReplaceWithValue(node, replacement, effect);
937           return Replace(replacement);
938         }
939       }
940       state = state->AddField(object, field_index, node, access.name, zone());
941     }
942   }
943   Handle<Map> field_map;
944   if (access.map.ToHandle(&field_map)) {
945     state = state->SetMaps(node, ZoneHandleSet<Map>(field_map), zone());
946   }
947   return UpdateState(node, state);
948 }
949 
ReduceStoreField(Node * node)950 Reduction LoadElimination::ReduceStoreField(Node* node) {
951   FieldAccess const& access = FieldAccessOf(node->op());
952   Node* const object = NodeProperties::GetValueInput(node, 0);
953   Node* const new_value = NodeProperties::GetValueInput(node, 1);
954   Node* const effect = NodeProperties::GetEffectInput(node);
955   AbstractState const* state = node_states_.Get(effect);
956   if (state == nullptr) return NoChange();
957   if (access.offset == HeapObject::kMapOffset &&
958       access.base_is_tagged == kTaggedBase) {
959     DCHECK(IsAnyTagged(access.machine_type.representation()));
960     // Kill all potential knowledge about the {object}s map.
961     state = state->KillMaps(object, zone());
962     Type const new_value_type = NodeProperties::GetType(new_value);
963     if (new_value_type.IsHeapConstant()) {
964       // Record the new {object} map information.
965       ZoneHandleSet<Map> object_maps(
966           bit_cast<Handle<Map>>(new_value_type.AsHeapConstant()->Value()));
967       state = state->SetMaps(object, object_maps, zone());
968     }
969   } else {
970     int field_index = FieldIndexOf(access);
971     if (field_index >= 0) {
972       Node* const old_value = state->LookupField(object, field_index);
973       if (old_value == new_value) {
974         // This store is fully redundant.
975         return Replace(effect);
976       }
977       // Kill all potentially aliasing fields and record the new value.
978       state = state->KillField(object, field_index, access.name, zone());
979       state =
980           state->AddField(object, field_index, new_value, access.name, zone());
981     } else {
982       // Unsupported StoreField operator.
983       state = state->KillFields(object, access.name, zone());
984     }
985   }
986   return UpdateState(node, state);
987 }
988 
ReduceLoadElement(Node * node)989 Reduction LoadElimination::ReduceLoadElement(Node* node) {
990   Node* const object = NodeProperties::GetValueInput(node, 0);
991   Node* const index = NodeProperties::GetValueInput(node, 1);
992   Node* const effect = NodeProperties::GetEffectInput(node);
993   AbstractState const* state = node_states_.Get(effect);
994   if (state == nullptr) return NoChange();
995 
996   // Only handle loads that do not require truncations.
997   ElementAccess const& access = ElementAccessOf(node->op());
998   switch (access.machine_type.representation()) {
999     case MachineRepresentation::kNone:
1000     case MachineRepresentation::kBit:
1001       UNREACHABLE();
1002       break;
1003     case MachineRepresentation::kWord8:
1004     case MachineRepresentation::kWord16:
1005     case MachineRepresentation::kWord32:
1006     case MachineRepresentation::kWord64:
1007     case MachineRepresentation::kFloat32:
1008       // TODO(turbofan): Add support for doing the truncations.
1009       break;
1010     case MachineRepresentation::kFloat64:
1011     case MachineRepresentation::kSimd128:
1012     case MachineRepresentation::kTaggedSigned:
1013     case MachineRepresentation::kTaggedPointer:
1014     case MachineRepresentation::kTagged:
1015       if (Node* replacement = state->LookupElement(
1016               object, index, access.machine_type.representation())) {
1017         // Make sure we don't resurrect dead {replacement} nodes.
1018         // Skip lowering if the type of the {replacement} node is not a subtype
1019         // of the original {node}'s type.
1020         // TODO(tebbi): We should insert a {TypeGuard} for the intersection of
1021         // these two types here once we properly handle {Type::None} everywhere.
1022         if (!replacement->IsDead() && NodeProperties::GetType(replacement)
1023                                           .Is(NodeProperties::GetType(node))) {
1024           ReplaceWithValue(node, replacement, effect);
1025           return Replace(replacement);
1026         }
1027       }
1028       state = state->AddElement(object, index, node,
1029                                 access.machine_type.representation(), zone());
1030       return UpdateState(node, state);
1031   }
1032   return NoChange();
1033 }
1034 
ReduceStoreElement(Node * node)1035 Reduction LoadElimination::ReduceStoreElement(Node* node) {
1036   ElementAccess const& access = ElementAccessOf(node->op());
1037   Node* const object = NodeProperties::GetValueInput(node, 0);
1038   Node* const index = NodeProperties::GetValueInput(node, 1);
1039   Node* const new_value = NodeProperties::GetValueInput(node, 2);
1040   Node* const effect = NodeProperties::GetEffectInput(node);
1041   AbstractState const* state = node_states_.Get(effect);
1042   if (state == nullptr) return NoChange();
1043   Node* const old_value =
1044       state->LookupElement(object, index, access.machine_type.representation());
1045   if (old_value == new_value) {
1046     // This store is fully redundant.
1047     return Replace(effect);
1048   }
1049   // Kill all potentially aliasing elements.
1050   state = state->KillElement(object, index, zone());
1051   // Only record the new value if the store doesn't have an implicit truncation.
1052   switch (access.machine_type.representation()) {
1053     case MachineRepresentation::kNone:
1054     case MachineRepresentation::kBit:
1055       UNREACHABLE();
1056       break;
1057     case MachineRepresentation::kWord8:
1058     case MachineRepresentation::kWord16:
1059     case MachineRepresentation::kWord32:
1060     case MachineRepresentation::kWord64:
1061     case MachineRepresentation::kFloat32:
1062       // TODO(turbofan): Add support for doing the truncations.
1063       break;
1064     case MachineRepresentation::kFloat64:
1065     case MachineRepresentation::kSimd128:
1066     case MachineRepresentation::kTaggedSigned:
1067     case MachineRepresentation::kTaggedPointer:
1068     case MachineRepresentation::kTagged:
1069       state = state->AddElement(object, index, new_value,
1070                                 access.machine_type.representation(), zone());
1071       break;
1072   }
1073   return UpdateState(node, state);
1074 }
1075 
ReduceStoreTypedElement(Node * node)1076 Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
1077   Node* const effect = NodeProperties::GetEffectInput(node);
1078   AbstractState const* state = node_states_.Get(effect);
1079   if (state == nullptr) return NoChange();
1080   return UpdateState(node, state);
1081 }
1082 
UpdateStateForPhi(AbstractState const * state,Node * effect_phi,Node * phi)1083 LoadElimination::AbstractState const* LoadElimination::UpdateStateForPhi(
1084     AbstractState const* state, Node* effect_phi, Node* phi) {
1085   int predecessor_count = phi->InputCount() - 1;
1086   // TODO(jarin) Consider doing a union here. At the moment, we just keep this
1087   // consistent with AbstractState::Merge.
1088 
1089   // Check if all the inputs have the same maps.
1090   AbstractState const* input_state =
1091       node_states_.Get(NodeProperties::GetEffectInput(effect_phi, 0));
1092   ZoneHandleSet<Map> object_maps;
1093   if (!input_state->LookupMaps(phi->InputAt(0), &object_maps)) return state;
1094   for (int i = 1; i < predecessor_count; i++) {
1095     input_state =
1096         node_states_.Get(NodeProperties::GetEffectInput(effect_phi, i));
1097     ZoneHandleSet<Map> input_maps;
1098     if (!input_state->LookupMaps(phi->InputAt(i), &input_maps)) return state;
1099     if (input_maps != object_maps) return state;
1100   }
1101   return state->SetMaps(phi, object_maps, zone());
1102 }
1103 
ReduceEffectPhi(Node * node)1104 Reduction LoadElimination::ReduceEffectPhi(Node* node) {
1105   Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
1106   Node* const control = NodeProperties::GetControlInput(node);
1107   AbstractState const* state0 = node_states_.Get(effect0);
1108   if (state0 == nullptr) return NoChange();
1109   if (control->opcode() == IrOpcode::kLoop) {
1110     // Here we rely on having only reducible loops:
1111     // The loop entry edge always dominates the header, so we can just take
1112     // the state from the first input, and compute the loop state based on it.
1113     AbstractState const* state = ComputeLoopState(node, state0);
1114     return UpdateState(node, state);
1115   }
1116   DCHECK_EQ(IrOpcode::kMerge, control->opcode());
1117 
1118   // Shortcut for the case when we do not know anything about some input.
1119   int const input_count = node->op()->EffectInputCount();
1120   for (int i = 1; i < input_count; ++i) {
1121     Node* const effect = NodeProperties::GetEffectInput(node, i);
1122     if (node_states_.Get(effect) == nullptr) return NoChange();
1123   }
1124 
1125   // Make a copy of the first input's state and merge with the state
1126   // from other inputs.
1127   AbstractState* state = new (zone()) AbstractState(*state0);
1128   for (int i = 1; i < input_count; ++i) {
1129     Node* const input = NodeProperties::GetEffectInput(node, i);
1130     state->Merge(node_states_.Get(input), zone());
1131   }
1132 
1133   // For each phi, try to compute the new state for the phi from
1134   // the inputs.
1135   AbstractState const* state_with_phis = state;
1136   for (Node* use : control->uses()) {
1137     if (use->opcode() == IrOpcode::kPhi) {
1138       state_with_phis = UpdateStateForPhi(state_with_phis, node, use);
1139     }
1140   }
1141 
1142   return UpdateState(node, state_with_phis);
1143 }
1144 
ReduceStart(Node * node)1145 Reduction LoadElimination::ReduceStart(Node* node) {
1146   return UpdateState(node, empty_state());
1147 }
1148 
ReduceOtherNode(Node * node)1149 Reduction LoadElimination::ReduceOtherNode(Node* node) {
1150   if (node->op()->EffectInputCount() == 1) {
1151     if (node->op()->EffectOutputCount() == 1) {
1152       Node* const effect = NodeProperties::GetEffectInput(node);
1153       AbstractState const* state = node_states_.Get(effect);
1154       // If we do not know anything about the predecessor, do not propagate
1155       // just yet because we will have to recompute anyway once we compute
1156       // the predecessor.
1157       if (state == nullptr) return NoChange();
1158       // Check if this {node} has some uncontrolled side effects.
1159       if (!node->op()->HasProperty(Operator::kNoWrite)) {
1160         state = empty_state();
1161       }
1162       return UpdateState(node, state);
1163     } else {
1164       // Effect terminators should be handled specially.
1165       return NoChange();
1166     }
1167   }
1168   DCHECK_EQ(0, node->op()->EffectInputCount());
1169   DCHECK_EQ(0, node->op()->EffectOutputCount());
1170   return NoChange();
1171 }
1172 
UpdateState(Node * node,AbstractState const * state)1173 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
1174   AbstractState const* original = node_states_.Get(node);
1175   // Only signal that the {node} has Changed, if the information about {state}
1176   // has changed wrt. the {original}.
1177   if (state != original) {
1178     if (original == nullptr || !state->Equals(original)) {
1179       node_states_.Set(node, state);
1180       return Changed(node);
1181     }
1182   }
1183   return NoChange();
1184 }
1185 
ComputeLoopState(Node * node,AbstractState const * state) const1186 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
1187     Node* node, AbstractState const* state) const {
1188   Node* const control = NodeProperties::GetControlInput(node);
1189   struct TransitionElementsKindInfo {
1190     ElementsTransition transition;
1191     Node* object;
1192   };
1193   ZoneVector<TransitionElementsKindInfo> element_transitions_(zone());
1194   ZoneQueue<Node*> queue(zone());
1195   ZoneSet<Node*> visited(zone());
1196   visited.insert(node);
1197   for (int i = 1; i < control->InputCount(); ++i) {
1198     queue.push(node->InputAt(i));
1199   }
1200   while (!queue.empty()) {
1201     Node* const current = queue.front();
1202     queue.pop();
1203     if (visited.find(current) == visited.end()) {
1204       visited.insert(current);
1205       if (!current->op()->HasProperty(Operator::kNoWrite)) {
1206         switch (current->opcode()) {
1207           case IrOpcode::kEnsureWritableFastElements: {
1208             Node* const object = NodeProperties::GetValueInput(current, 0);
1209             state = state->KillField(object,
1210                                      FieldIndexOf(JSObject::kElementsOffset),
1211                                      MaybeHandle<Name>(), zone());
1212             break;
1213           }
1214           case IrOpcode::kMaybeGrowFastElements: {
1215             Node* const object = NodeProperties::GetValueInput(current, 0);
1216             state = state->KillField(object,
1217                                      FieldIndexOf(JSObject::kElementsOffset),
1218                                      MaybeHandle<Name>(), zone());
1219             break;
1220           }
1221           case IrOpcode::kTransitionElementsKind: {
1222             ElementsTransition transition = ElementsTransitionOf(current->op());
1223             Node* const object = NodeProperties::GetValueInput(current, 0);
1224             ZoneHandleSet<Map> object_maps;
1225             if (!state->LookupMaps(object, &object_maps) ||
1226                 !ZoneHandleSet<Map>(transition.target())
1227                      .contains(object_maps)) {
1228               element_transitions_.push_back({transition, object});
1229             }
1230             break;
1231           }
1232           case IrOpcode::kTransitionAndStoreElement: {
1233             Node* const object = NodeProperties::GetValueInput(current, 0);
1234             // Invalidate what we know about the {object}s map.
1235             state = state->KillMaps(object, zone());
1236             // Kill the elements as well.
1237             state = state->KillField(object,
1238                                      FieldIndexOf(JSObject::kElementsOffset),
1239                                      MaybeHandle<Name>(), zone());
1240             break;
1241           }
1242           case IrOpcode::kStoreField: {
1243             FieldAccess const& access = FieldAccessOf(current->op());
1244             Node* const object = NodeProperties::GetValueInput(current, 0);
1245             if (access.offset == HeapObject::kMapOffset) {
1246               // Invalidate what we know about the {object}s map.
1247               state = state->KillMaps(object, zone());
1248             } else {
1249               int field_index = FieldIndexOf(access);
1250               if (field_index < 0) {
1251                 state = state->KillFields(object, access.name, zone());
1252               } else {
1253                 state =
1254                     state->KillField(object, field_index, access.name, zone());
1255               }
1256             }
1257             break;
1258           }
1259           case IrOpcode::kStoreElement: {
1260             Node* const object = NodeProperties::GetValueInput(current, 0);
1261             Node* const index = NodeProperties::GetValueInput(current, 1);
1262             state = state->KillElement(object, index, zone());
1263             break;
1264           }
1265           case IrOpcode::kStoreTypedElement: {
1266             // Doesn't affect anything we track with the state currently.
1267             break;
1268           }
1269           default:
1270             return empty_state();
1271         }
1272       }
1273       for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
1274         queue.push(NodeProperties::GetEffectInput(current, i));
1275       }
1276     }
1277   }
1278 
1279   // Finally, we apply the element transitions. For each transition, we will try
1280   // to only invalidate information about nodes that can have the transition's
1281   // source map. The trouble is that an object can be transitioned by some other
1282   // transition to the source map. In that case, the other transition will
1283   // invalidate the information, so we are mostly fine.
1284   //
1285   // The only bad case is
1286   //
1287   //    mapA   ---fast--->   mapB   ---slow--->   mapC
1288   //
1289   // If we process the slow transition first on an object that has mapA, we will
1290   // ignore the transition because the object does not have its source map
1291   // (mapB). When we later process the fast transition, we invalidate the
1292   // object's map, but we keep the information about the object's elements. This
1293   // is wrong because the elements will be overwritten by the slow transition.
1294   //
1295   // Note that the slow-slow case is fine because either of the slow transition
1296   // will invalidate the elements field, so the processing order does not
1297   // matter.
1298   //
1299   // To handle the bad case properly, we first kill the maps using all
1300   // transitions. We kill the the fields later when all the transitions are
1301   // already reflected in the map information.
1302 
1303   for (const TransitionElementsKindInfo& t : element_transitions_) {
1304     AliasStateInfo alias_info(state, t.object, t.transition.source());
1305     state = state->KillMaps(alias_info, zone());
1306   }
1307   for (const TransitionElementsKindInfo& t : element_transitions_) {
1308     switch (t.transition.mode()) {
1309       case ElementsTransition::kFastTransition:
1310         break;
1311       case ElementsTransition::kSlowTransition: {
1312         AliasStateInfo alias_info(state, t.object, t.transition.source());
1313         state = state->KillField(alias_info,
1314                                  FieldIndexOf(JSObject::kElementsOffset),
1315                                  MaybeHandle<Name>(), zone());
1316         break;
1317       }
1318     }
1319   }
1320   return state;
1321 }
1322 
1323 // static
FieldIndexOf(int offset)1324 int LoadElimination::FieldIndexOf(int offset) {
1325   DCHECK_EQ(0, offset % kPointerSize);
1326   int field_index = offset / kPointerSize;
1327   if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
1328   DCHECK_LT(0, field_index);
1329   return field_index - 1;
1330 }
1331 
1332 // static
FieldIndexOf(FieldAccess const & access)1333 int LoadElimination::FieldIndexOf(FieldAccess const& access) {
1334   MachineRepresentation rep = access.machine_type.representation();
1335   switch (rep) {
1336     case MachineRepresentation::kNone:
1337     case MachineRepresentation::kBit:
1338     case MachineRepresentation::kSimd128:
1339       UNREACHABLE();
1340       break;
1341     case MachineRepresentation::kWord32:
1342     case MachineRepresentation::kWord64:
1343       if (rep != MachineType::PointerRepresentation()) {
1344         return -1;  // We currently only track pointer size fields.
1345       }
1346       break;
1347     case MachineRepresentation::kWord8:
1348     case MachineRepresentation::kWord16:
1349     case MachineRepresentation::kFloat32:
1350       return -1;  // Currently untracked.
1351     case MachineRepresentation::kFloat64:
1352       if (kDoubleSize != kPointerSize) {
1353         return -1;  // We currently only track pointer size fields.
1354       }
1355       break;
1356     case MachineRepresentation::kTaggedSigned:
1357     case MachineRepresentation::kTaggedPointer:
1358     case MachineRepresentation::kTagged:
1359       // TODO(bmeurer): Check that we never do overlapping load/stores of
1360       // individual parts of Float64 values.
1361       break;
1362   }
1363   if (access.base_is_tagged != kTaggedBase) {
1364     return -1;  // We currently only track tagged objects.
1365   }
1366   return FieldIndexOf(access.offset);
1367 }
1368 
common() const1369 CommonOperatorBuilder* LoadElimination::common() const {
1370   return jsgraph()->common();
1371 }
1372 
graph() const1373 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
1374 
isolate() const1375 Isolate* LoadElimination::isolate() const { return jsgraph()->isolate(); }
1376 
factory() const1377 Factory* LoadElimination::factory() const { return jsgraph()->factory(); }
1378 
1379 }  // namespace compiler
1380 }  // namespace internal
1381 }  // namespace v8
1382