• 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/access-builder.h"
8 #include "src/compiler/common-operator.h"
9 #include "src/compiler/js-graph.h"
10 #include "src/compiler/node-properties.h"
11 #include "src/heap/factory.h"
12 #include "src/objects/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 !node->IsDead();
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::kMapGuard:
103       return ReduceMapGuard(node);
104     case IrOpcode::kCheckMaps:
105       return ReduceCheckMaps(node);
106     case IrOpcode::kCompareMaps:
107       return ReduceCompareMaps(node);
108     case IrOpcode::kEnsureWritableFastElements:
109       return ReduceEnsureWritableFastElements(node);
110     case IrOpcode::kMaybeGrowFastElements:
111       return ReduceMaybeGrowFastElements(node);
112     case IrOpcode::kTransitionElementsKind:
113       return ReduceTransitionElementsKind(node);
114     case IrOpcode::kLoadField:
115       return ReduceLoadField(node, FieldAccessOf(node->op()));
116     case IrOpcode::kStoreField:
117       return ReduceStoreField(node, FieldAccessOf(node->op()));
118     case IrOpcode::kLoadElement:
119       return ReduceLoadElement(node);
120     case IrOpcode::kStoreElement:
121       return ReduceStoreElement(node);
122     case IrOpcode::kTransitionAndStoreElement:
123       return ReduceTransitionAndStoreElement(node);
124     case IrOpcode::kStoreTypedElement:
125       return ReduceStoreTypedElement(node);
126     case IrOpcode::kEffectPhi:
127       return ReduceEffectPhi(node);
128     case IrOpcode::kDead:
129       break;
130     case IrOpcode::kStart:
131       return ReduceStart(node);
132     default:
133       return ReduceOtherNode(node);
134   }
135   return NoChange();
136 }
137 
138 namespace {
139 
IsCompatible(MachineRepresentation r1,MachineRepresentation r2)140 bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
141   if (r1 == r2) return true;
142   return IsAnyTagged(r1) && IsAnyTagged(r2);
143 }
144 
145 }  // namespace
146 
147 LoadElimination::AbstractState const
148     LoadElimination::AbstractState::empty_state_;
149 
Lookup(Node * object,Node * index,MachineRepresentation representation) const150 Node* LoadElimination::AbstractElements::Lookup(
151     Node* object, Node* index, MachineRepresentation representation) const {
152   for (Element const element : elements_) {
153     if (element.object == nullptr) continue;
154     DCHECK_NOT_NULL(element.index);
155     DCHECK_NOT_NULL(element.value);
156     if (MustAlias(object, element.object) && MustAlias(index, element.index) &&
157         IsCompatible(representation, element.representation)) {
158       return element.value;
159     }
160   }
161   return nullptr;
162 }
163 
164 LoadElimination::AbstractElements const*
Kill(Node * object,Node * index,Zone * zone) const165 LoadElimination::AbstractElements::Kill(Node* object, Node* index,
166                                         Zone* zone) const {
167   for (Element const element : this->elements_) {
168     if (element.object == nullptr) continue;
169     if (MayAlias(object, element.object)) {
170       AbstractElements* that = zone->New<AbstractElements>(zone);
171       for (Element const element2 : this->elements_) {
172         if (element2.object == nullptr) continue;
173         DCHECK_NOT_NULL(element2.index);
174         DCHECK_NOT_NULL(element2.value);
175         if (!MayAlias(object, element2.object) ||
176             !NodeProperties::GetType(index).Maybe(
177                 NodeProperties::GetType(element2.index))) {
178           that->elements_[that->next_index_++] = element2;
179         }
180       }
181       that->next_index_ %= arraysize(elements_);
182       return that;
183     }
184   }
185   return this;
186 }
187 
Equals(AbstractElements const * that) const188 bool LoadElimination::AbstractElements::Equals(
189     AbstractElements const* that) const {
190   if (this == that) return true;
191   for (size_t i = 0; i < arraysize(elements_); ++i) {
192     Element this_element = this->elements_[i];
193     if (this_element.object == nullptr) continue;
194     for (size_t j = 0;; ++j) {
195       if (j == arraysize(elements_)) return false;
196       Element that_element = that->elements_[j];
197       if (this_element.object == that_element.object &&
198           this_element.index == that_element.index &&
199           this_element.value == that_element.value) {
200         break;
201       }
202     }
203   }
204   for (size_t i = 0; i < arraysize(elements_); ++i) {
205     Element that_element = that->elements_[i];
206     if (that_element.object == nullptr) continue;
207     for (size_t j = 0;; ++j) {
208       if (j == arraysize(elements_)) return false;
209       Element this_element = this->elements_[j];
210       if (that_element.object == this_element.object &&
211           that_element.index == this_element.index &&
212           that_element.value == this_element.value) {
213         break;
214       }
215     }
216   }
217   return true;
218 }
219 
220 LoadElimination::AbstractElements const*
Merge(AbstractElements const * that,Zone * zone) const221 LoadElimination::AbstractElements::Merge(AbstractElements const* that,
222                                          Zone* zone) const {
223   if (this->Equals(that)) return this;
224   AbstractElements* copy = zone->New<AbstractElements>(zone);
225   for (Element const this_element : this->elements_) {
226     if (this_element.object == nullptr) continue;
227     for (Element const that_element : that->elements_) {
228       if (this_element.object == that_element.object &&
229           this_element.index == that_element.index &&
230           this_element.value == that_element.value) {
231         copy->elements_[copy->next_index_++] = this_element;
232         break;
233       }
234     }
235   }
236   copy->next_index_ %= arraysize(elements_);
237   return copy;
238 }
239 
Print() const240 void LoadElimination::AbstractElements::Print() const {
241   for (Element const& element : elements_) {
242     if (element.object) {
243       PrintF("    #%d:%s @ #%d:%s -> #%d:%s\n", element.object->id(),
244              element.object->op()->mnemonic(), element.index->id(),
245              element.index->op()->mnemonic(), element.value->id(),
246              element.value->op()->mnemonic());
247     }
248   }
249 }
250 
Lookup(Node * object) const251 LoadElimination::FieldInfo const* LoadElimination::AbstractField::Lookup(
252     Node* object) const {
253   for (auto& pair : info_for_node_) {
254     if (pair.first->IsDead()) continue;
255     if (MustAlias(object, pair.first)) return &pair.second;
256   }
257   return nullptr;
258 }
259 
260 namespace {
261 
MayAlias(MaybeHandle<Name> x,MaybeHandle<Name> y)262 bool MayAlias(MaybeHandle<Name> x, MaybeHandle<Name> y) {
263   if (!x.address()) return true;
264   if (!y.address()) return true;
265   if (x.address() != y.address()) return false;
266   return true;
267 }
268 
269 }  // namespace
270 
271 class LoadElimination::AliasStateInfo {
272  public:
AliasStateInfo(const AbstractState * state,Node * object,Handle<Map> map)273   AliasStateInfo(const AbstractState* state, Node* object, Handle<Map> map)
274       : state_(state), object_(object), map_(map) {}
AliasStateInfo(const AbstractState * state,Node * object)275   AliasStateInfo(const AbstractState* state, Node* object)
276       : state_(state), object_(object) {}
277 
278   bool MayAlias(Node* other) const;
279 
280  private:
281   const AbstractState* state_;
282   Node* object_;
283   MaybeHandle<Map> map_;
284 };
285 
KillConst(Node * object,Zone * zone) const286 LoadElimination::AbstractField const* LoadElimination::AbstractField::KillConst(
287     Node* object, Zone* zone) const {
288   for (auto info1 : this->info_for_node_) {
289     if (info1.first->IsDead()) continue;
290     // If we previously recorded information about a const store on the given
291     // 'object', we might not have done it on the same node; e.g. we might now
292     // identify the object by a FinishRegion node, whereas the initial const
293     // store was performed on the Allocate node. We therefore remove information
294     // on all nodes that must alias with 'object'.
295     if (MustAlias(object, info1.first)) {
296       AbstractField* that = zone->New<AbstractField>(zone);
297       for (auto info2 : this->info_for_node_) {
298         if (!MustAlias(object, info2.first)) {
299           that->info_for_node_.insert(info2);
300         }
301       }
302       return that;
303     }
304   }
305   return this;
306 }
307 
Kill(const AliasStateInfo & alias_info,MaybeHandle<Name> name,Zone * zone) const308 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill(
309     const AliasStateInfo& alias_info, MaybeHandle<Name> name,
310     Zone* zone) const {
311   for (auto info1 : this->info_for_node_) {
312     if (info1.first->IsDead()) continue;
313     if (alias_info.MayAlias(info1.first)) {
314       AbstractField* that = zone->New<AbstractField>(zone);
315       for (auto info2 : this->info_for_node_) {
316         if (!alias_info.MayAlias(info2.first) ||
317             !MayAlias(name, info2.second.name)) {
318           that->info_for_node_.insert(info2);
319         }
320       }
321       return that;
322     }
323   }
324   return this;
325 }
326 
Print() const327 void LoadElimination::AbstractField::Print() const {
328   for (auto pair : info_for_node_) {
329     PrintF("    #%d:%s -> #%d:%s [repr=%s]\n", pair.first->id(),
330            pair.first->op()->mnemonic(), pair.second.value->id(),
331            pair.second.value->op()->mnemonic(),
332            MachineReprToString(pair.second.representation));
333   }
334 }
335 
AbstractMaps(Zone * zone)336 LoadElimination::AbstractMaps::AbstractMaps(Zone* zone)
337     : info_for_node_(zone) {}
338 
AbstractMaps(Node * object,ZoneHandleSet<Map> maps,Zone * zone)339 LoadElimination::AbstractMaps::AbstractMaps(Node* object,
340                                             ZoneHandleSet<Map> maps, Zone* zone)
341     : info_for_node_(zone) {
342   object = ResolveRenames(object);
343   info_for_node_.insert(std::make_pair(object, maps));
344 }
345 
Lookup(Node * object,ZoneHandleSet<Map> * object_maps) const346 bool LoadElimination::AbstractMaps::Lookup(
347     Node* object, ZoneHandleSet<Map>* object_maps) const {
348   auto it = info_for_node_.find(ResolveRenames(object));
349   if (it == info_for_node_.end()) return false;
350   *object_maps = it->second;
351   return true;
352 }
353 
Kill(const AliasStateInfo & alias_info,Zone * zone) const354 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Kill(
355     const AliasStateInfo& alias_info, Zone* zone) const {
356   for (auto info1 : this->info_for_node_) {
357     if (alias_info.MayAlias(info1.first)) {
358       AbstractMaps* that = zone->New<AbstractMaps>(zone);
359       for (auto info2 : this->info_for_node_) {
360         if (!alias_info.MayAlias(info2.first))
361           that->info_for_node_.insert(info2);
362       }
363       return that;
364     }
365   }
366   return this;
367 }
368 
Merge(AbstractMaps const * that,Zone * zone) const369 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Merge(
370     AbstractMaps const* that, Zone* zone) const {
371   if (this->Equals(that)) return this;
372   AbstractMaps* copy = zone->New<AbstractMaps>(zone);
373   for (auto this_it : this->info_for_node_) {
374     Node* this_object = this_it.first;
375     ZoneHandleSet<Map> this_maps = this_it.second;
376     auto that_it = that->info_for_node_.find(this_object);
377     if (that_it != that->info_for_node_.end() && that_it->second == this_maps) {
378       copy->info_for_node_.insert(this_it);
379     }
380   }
381   return copy;
382 }
383 
Extend(Node * object,ZoneHandleSet<Map> maps,Zone * zone) const384 LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Extend(
385     Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
386   AbstractMaps* that = zone->New<AbstractMaps>(zone);
387   that->info_for_node_ = this->info_for_node_;
388   object = ResolveRenames(object);
389   that->info_for_node_[object] = maps;
390   return that;
391 }
392 
Print() const393 void LoadElimination::AbstractMaps::Print() const {
394   AllowHandleDereference allow_handle_dereference;
395   StdoutStream os;
396   for (auto pair : info_for_node_) {
397     os << "    #" << pair.first->id() << ":" << pair.first->op()->mnemonic()
398        << std::endl;
399     ZoneHandleSet<Map> const& maps = pair.second;
400     for (size_t i = 0; i < maps.size(); ++i) {
401       os << "     - " << Brief(*maps[i]) << std::endl;
402     }
403   }
404 }
405 
FieldsEquals(AbstractFields const & this_fields,AbstractFields const & that_fields) const406 bool LoadElimination::AbstractState::FieldsEquals(
407     AbstractFields const& this_fields,
408     AbstractFields const& that_fields) const {
409   for (size_t i = 0u; i < this_fields.size(); ++i) {
410     AbstractField const* this_field = this_fields[i];
411     AbstractField const* that_field = that_fields[i];
412     if (this_field) {
413       if (!that_field || !that_field->Equals(this_field)) return false;
414     } else if (that_field) {
415       return false;
416     }
417   }
418   return true;
419 }
420 
Equals(AbstractState const * that) const421 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
422   if (this->elements_) {
423     if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
424       return false;
425     }
426   } else if (that->elements_) {
427     return false;
428   }
429   if (!FieldsEquals(this->fields_, that->fields_) ||
430       !FieldsEquals(this->const_fields_, that->const_fields_)) {
431     return false;
432   }
433   if (this->maps_) {
434     if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
435       return false;
436     }
437   } else if (that->maps_) {
438     return false;
439   }
440   return true;
441 }
442 
FieldsMerge(AbstractFields * this_fields,AbstractFields const & that_fields,Zone * zone)443 void LoadElimination::AbstractState::FieldsMerge(
444     AbstractFields* this_fields, AbstractFields const& that_fields,
445     Zone* zone) {
446   for (size_t i = 0; i < this_fields->size(); ++i) {
447     AbstractField const*& this_field = (*this_fields)[i];
448     if (this_field) {
449       if (that_fields[i]) {
450         this_field = this_field->Merge(that_fields[i], zone);
451       } else {
452         this_field = nullptr;
453       }
454     }
455   }
456 }
457 
Merge(AbstractState const * that,Zone * zone)458 void LoadElimination::AbstractState::Merge(AbstractState const* that,
459                                            Zone* zone) {
460   // Merge the information we have about the elements.
461   if (this->elements_) {
462     this->elements_ = that->elements_
463                           ? that->elements_->Merge(this->elements_, zone)
464                           : nullptr;
465   }
466 
467   // Merge the information we have about the fields.
468   FieldsMerge(&this->fields_, that->fields_, zone);
469   FieldsMerge(&this->const_fields_, that->const_fields_, zone);
470 
471   // Merge the information we have about the maps.
472   if (this->maps_) {
473     this->maps_ = that->maps_ ? that->maps_->Merge(this->maps_, zone) : nullptr;
474   }
475 }
476 
LookupMaps(Node * object,ZoneHandleSet<Map> * object_map) const477 bool LoadElimination::AbstractState::LookupMaps(
478     Node* object, ZoneHandleSet<Map>* object_map) const {
479   return this->maps_ && this->maps_->Lookup(object, object_map);
480 }
481 
SetMaps(Node * object,ZoneHandleSet<Map> maps,Zone * zone) const482 LoadElimination::AbstractState const* LoadElimination::AbstractState::SetMaps(
483     Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
484   AbstractState* that = zone->New<AbstractState>(*this);
485   if (that->maps_) {
486     that->maps_ = that->maps_->Extend(object, maps, zone);
487   } else {
488     that->maps_ = zone->New<AbstractMaps>(object, maps, zone);
489   }
490   return that;
491 }
492 
KillMaps(const AliasStateInfo & alias_info,Zone * zone) const493 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
494     const AliasStateInfo& alias_info, Zone* zone) const {
495   if (this->maps_) {
496     AbstractMaps const* that_maps = this->maps_->Kill(alias_info, zone);
497     if (this->maps_ != that_maps) {
498       AbstractState* that = zone->New<AbstractState>(*this);
499       that->maps_ = that_maps;
500       return that;
501     }
502   }
503   return this;
504 }
505 
KillMaps(Node * object,Zone * zone) const506 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
507     Node* object, Zone* zone) const {
508   AliasStateInfo alias_info(this, object);
509   return KillMaps(alias_info, zone);
510 }
511 
LookupElement(Node * object,Node * index,MachineRepresentation representation) const512 Node* LoadElimination::AbstractState::LookupElement(
513     Node* object, Node* index, MachineRepresentation representation) const {
514   if (this->elements_) {
515     return this->elements_->Lookup(object, index, representation);
516   }
517   return nullptr;
518 }
519 
520 LoadElimination::AbstractState const*
AddElement(Node * object,Node * index,Node * value,MachineRepresentation representation,Zone * zone) const521 LoadElimination::AbstractState::AddElement(Node* object, Node* index,
522                                            Node* value,
523                                            MachineRepresentation representation,
524                                            Zone* zone) const {
525   AbstractState* that = zone->New<AbstractState>(*this);
526   if (that->elements_) {
527     that->elements_ =
528         that->elements_->Extend(object, index, value, representation, zone);
529   } else {
530     that->elements_ =
531         zone->New<AbstractElements>(object, index, value, representation, zone);
532   }
533   return that;
534 }
535 
536 LoadElimination::AbstractState const*
KillElement(Node * object,Node * index,Zone * zone) const537 LoadElimination::AbstractState::KillElement(Node* object, Node* index,
538                                             Zone* zone) const {
539   if (this->elements_) {
540     AbstractElements const* that_elements =
541         this->elements_->Kill(object, index, zone);
542     if (this->elements_ != that_elements) {
543       AbstractState* that = zone->New<AbstractState>(*this);
544       that->elements_ = that_elements;
545       return that;
546     }
547   }
548   return this;
549 }
550 
AddField(Node * object,IndexRange index_range,LoadElimination::FieldInfo info,Zone * zone) const551 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
552     Node* object, IndexRange index_range, LoadElimination::FieldInfo info,
553     Zone* zone) const {
554   AbstractState* that = zone->New<AbstractState>(*this);
555   AbstractFields& fields =
556       info.const_field_info.IsConst() ? that->const_fields_ : that->fields_;
557   for (int index : index_range) {
558     if (fields[index]) {
559       fields[index] = fields[index]->Extend(object, info, zone);
560     } else {
561       fields[index] = zone->New<AbstractField>(object, info, zone);
562     }
563   }
564   return that;
565 }
566 
567 LoadElimination::AbstractState const*
KillConstField(Node * object,IndexRange index_range,Zone * zone) const568 LoadElimination::AbstractState::KillConstField(Node* object,
569                                                IndexRange index_range,
570                                                Zone* zone) const {
571   AliasStateInfo alias_info(this, object);
572   AbstractState* that = nullptr;
573   for (int index : index_range) {
574     if (AbstractField const* this_field = this->const_fields_[index]) {
575       this_field = this_field->KillConst(object, zone);
576       if (this->const_fields_[index] != this_field) {
577         if (!that) that = zone->New<AbstractState>(*this);
578         that->const_fields_[index] = this_field;
579       }
580     }
581   }
582   return that ? that : this;
583 }
584 
KillField(Node * object,IndexRange index_range,MaybeHandle<Name> name,Zone * zone) const585 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
586     Node* object, IndexRange index_range, MaybeHandle<Name> name,
587     Zone* zone) const {
588   AliasStateInfo alias_info(this, object);
589   return KillField(alias_info, index_range, name, zone);
590 }
591 
KillField(const AliasStateInfo & alias_info,IndexRange index_range,MaybeHandle<Name> name,Zone * zone) const592 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
593     const AliasStateInfo& alias_info, IndexRange index_range,
594     MaybeHandle<Name> name, Zone* zone) const {
595   AbstractState* that = nullptr;
596   for (int index : index_range) {
597     if (AbstractField const* this_field = this->fields_[index]) {
598       this_field = this_field->Kill(alias_info, name, zone);
599       if (this->fields_[index] != this_field) {
600         if (!that) that = zone->New<AbstractState>(*this);
601         that->fields_[index] = this_field;
602       }
603     }
604   }
605   return that ? that : this;
606 }
607 
608 LoadElimination::AbstractState const*
KillFields(Node * object,MaybeHandle<Name> name,Zone * zone) const609 LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
610                                            Zone* zone) const {
611   AliasStateInfo alias_info(this, object);
612   for (size_t i = 0;; ++i) {
613     if (i == fields_.size()) return this;
614     if (AbstractField const* this_field = this->fields_[i]) {
615       AbstractField const* that_field =
616           this_field->Kill(alias_info, name, zone);
617       if (that_field != this_field) {
618         AbstractState* that = zone->New<AbstractState>(*this);
619         that->fields_[i] = that_field;
620         while (++i < fields_.size()) {
621           if (this->fields_[i] != nullptr) {
622             that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone);
623           }
624         }
625         return that;
626       }
627     }
628   }
629 }
630 
KillAll(Zone * zone) const631 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillAll(
632     Zone* zone) const {
633   // Kill everything except for const fields
634   for (size_t i = 0; i < const_fields_.size(); ++i) {
635     if (const_fields_[i]) {
636       AbstractState* that = zone->New<AbstractState>();
637       that->const_fields_ = const_fields_;
638       return that;
639     }
640   }
641   return LoadElimination::empty_state();
642 }
643 
LookupField(Node * object,IndexRange index_range,ConstFieldInfo const_field_info) const644 LoadElimination::FieldInfo const* LoadElimination::AbstractState::LookupField(
645     Node* object, IndexRange index_range,
646     ConstFieldInfo const_field_info) const {
647   // Check if all the indices in {index_range} contain identical information.
648   // If not, a partially overlapping access has invalidated part of the value.
649   base::Optional<LoadElimination::FieldInfo const*> result;
650   for (int index : index_range) {
651     LoadElimination::FieldInfo const* info = nullptr;
652     if (const_field_info.IsConst()) {
653       if (AbstractField const* this_field = const_fields_[index]) {
654         info = this_field->Lookup(object);
655       }
656       if (!(info && info->const_field_info == const_field_info)) return nullptr;
657     } else {
658       if (AbstractField const* this_field = fields_[index]) {
659         info = this_field->Lookup(object);
660       }
661       if (!info) return nullptr;
662     }
663     if (!result.has_value()) {
664       result = info;
665     } else if (**result != *info) {
666       // We detected inconsistent information for a field here.
667       // This can happen when incomplete alias information makes an unrelated
668       // write invalidate part of a field and then we re-combine this partial
669       // information.
670       // This is probably OK, but since it's rare, we better bail out here.
671       return nullptr;
672     }
673   }
674   return *result;
675 }
676 
MayAlias(Node * other) const677 bool LoadElimination::AliasStateInfo::MayAlias(Node* other) const {
678   // If {object} is being initialized right here (indicated by {object} being
679   // an Allocate node instead of a FinishRegion node), we know that {other}
680   // can only alias with {object} if they refer to exactly the same node.
681   if (object_->opcode() == IrOpcode::kAllocate) {
682     return object_ == other;
683   }
684   // Decide aliasing based on the node kinds.
685   if (!compiler::MayAlias(object_, other)) {
686     return false;
687   }
688   // Decide aliasing based on maps (if available).
689   Handle<Map> map;
690   if (map_.ToHandle(&map)) {
691     ZoneHandleSet<Map> other_maps;
692     if (state_->LookupMaps(other, &other_maps) && other_maps.size() == 1) {
693       if (map.address() != other_maps.at(0).address()) {
694         return false;
695       }
696     }
697   }
698   return true;
699 }
700 
Print() const701 void LoadElimination::AbstractState::Print() const {
702   if (maps_) {
703     PrintF("   maps:\n");
704     maps_->Print();
705   }
706   if (elements_) {
707     PrintF("   elements:\n");
708     elements_->Print();
709   }
710   for (size_t i = 0; i < fields_.size(); ++i) {
711     if (AbstractField const* const field = fields_[i]) {
712       PrintF("   field %zu:\n", i);
713       field->Print();
714     }
715   }
716   for (size_t i = 0; i < const_fields_.size(); ++i) {
717     if (AbstractField const* const const_field = const_fields_[i]) {
718       PrintF("   const field %zu:\n", i);
719       const_field->Print();
720     }
721   }
722 }
723 
724 LoadElimination::AbstractState const*
Get(Node * node) const725 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
726   size_t const id = node->id();
727   if (id < info_for_node_.size()) return info_for_node_[id];
728   return nullptr;
729 }
730 
Set(Node * node,AbstractState const * state)731 void LoadElimination::AbstractStateForEffectNodes::Set(
732     Node* node, AbstractState const* state) {
733   size_t const id = node->id();
734   if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
735   info_for_node_[id] = state;
736 }
737 
ReduceMapGuard(Node * node)738 Reduction LoadElimination::ReduceMapGuard(Node* node) {
739   ZoneHandleSet<Map> const& maps = MapGuardMapsOf(node->op());
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());
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,
804                            FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
805                            MaybeHandle<Name>(), zone());
806   // Add the new elements on {object}.
807   state = state->AddField(
808       object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
809       {node, MachineRepresentation::kTaggedPointer}, zone());
810   return UpdateState(node, state);
811 }
812 
ReduceMaybeGrowFastElements(Node * node)813 Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
814   GrowFastElementsParameters params = GrowFastElementsParametersOf(node->op());
815   Node* const object = NodeProperties::GetValueInput(node, 0);
816   Node* const effect = NodeProperties::GetEffectInput(node);
817   AbstractState const* state = node_states_.Get(effect);
818   if (state == nullptr) return NoChange();
819   if (params.mode() == GrowFastElementsMode::kDoubleElements) {
820     // We know that the resulting elements have the fixed double array map.
821     state = state->SetMaps(
822         node, ZoneHandleSet<Map>(factory()->fixed_double_array_map()), zone());
823   } else {
824     // We know that the resulting elements have the fixed array map or the COW
825     // version thereof (if we didn't grow and it was already COW before).
826     ZoneHandleSet<Map> fixed_array_maps(factory()->fixed_array_map());
827     fixed_array_maps.insert(factory()->fixed_cow_array_map(), zone());
828     state = state->SetMaps(node, fixed_array_maps, zone());
829   }
830   // Kill the previous elements on {object}.
831   state = state->KillField(object,
832                            FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
833                            MaybeHandle<Name>(), zone());
834   // Add the new elements on {object}.
835   state = state->AddField(
836       object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
837       {node, MachineRepresentation::kTaggedPointer}, zone());
838   return UpdateState(node, state);
839 }
840 
ReduceTransitionElementsKind(Node * node)841 Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
842   ElementsTransition transition = ElementsTransitionOf(node->op());
843   Node* const object = NodeProperties::GetValueInput(node, 0);
844   Handle<Map> source_map(transition.source());
845   Handle<Map> target_map(transition.target());
846   Node* const effect = NodeProperties::GetEffectInput(node);
847   AbstractState const* state = node_states_.Get(effect);
848   if (state == nullptr) return NoChange();
849   switch (transition.mode()) {
850     case ElementsTransition::kFastTransition:
851       break;
852     case ElementsTransition::kSlowTransition:
853       // Kill the elements as well.
854       AliasStateInfo alias_info(state, object, source_map);
855       state = state->KillField(
856           alias_info, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
857           MaybeHandle<Name>(), zone());
858       break;
859   }
860   ZoneHandleSet<Map> object_maps;
861   if (state->LookupMaps(object, &object_maps)) {
862     if (ZoneHandleSet<Map>(target_map).contains(object_maps)) {
863       // The {object} already has the {target_map}, so this TransitionElements
864       // {node} is fully redundant (independent of what {source_map} is).
865       return Replace(effect);
866     }
867     if (object_maps.contains(ZoneHandleSet<Map>(source_map))) {
868       object_maps.remove(source_map, zone());
869       object_maps.insert(target_map, zone());
870       AliasStateInfo alias_info(state, object, source_map);
871       state = state->KillMaps(alias_info, zone());
872       state = state->SetMaps(object, object_maps, zone());
873     }
874   } else {
875     AliasStateInfo alias_info(state, object, source_map);
876     state = state->KillMaps(alias_info, zone());
877   }
878   return UpdateState(node, state);
879 }
880 
ReduceTransitionAndStoreElement(Node * node)881 Reduction LoadElimination::ReduceTransitionAndStoreElement(Node* node) {
882   Node* const object = NodeProperties::GetValueInput(node, 0);
883   Handle<Map> double_map(DoubleMapParameterOf(node->op()));
884   Handle<Map> fast_map(FastMapParameterOf(node->op()));
885   Node* const effect = NodeProperties::GetEffectInput(node);
886   AbstractState const* state = node_states_.Get(effect);
887   if (state == nullptr) return NoChange();
888 
889   // We need to add the double and fast maps to the set of possible maps for
890   // this object, because we don't know which of those we'll transition to.
891   // Additionally, we should kill all alias information.
892   ZoneHandleSet<Map> object_maps;
893   if (state->LookupMaps(object, &object_maps)) {
894     object_maps.insert(double_map, zone());
895     object_maps.insert(fast_map, zone());
896     state = state->KillMaps(object, zone());
897     state = state->SetMaps(object, object_maps, zone());
898   }
899   // Kill the elements as well.
900   state = state->KillField(object,
901                            FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
902                            MaybeHandle<Name>(), zone());
903   return UpdateState(node, state);
904 }
905 
ReduceLoadField(Node * node,FieldAccess const & access)906 Reduction LoadElimination::ReduceLoadField(Node* node,
907                                            FieldAccess const& access) {
908   Node* object = NodeProperties::GetValueInput(node, 0);
909   Node* effect = NodeProperties::GetEffectInput(node);
910   Node* control = NodeProperties::GetControlInput(node);
911   AbstractState const* state = node_states_.Get(effect);
912   if (state == nullptr) return NoChange();
913   if (access.offset == HeapObject::kMapOffset &&
914       access.base_is_tagged == kTaggedBase) {
915     DCHECK(IsAnyTagged(access.machine_type.representation()));
916     ZoneHandleSet<Map> object_maps;
917     if (state->LookupMaps(object, &object_maps) && object_maps.size() == 1) {
918       Node* value = jsgraph()->HeapConstant(object_maps[0]);
919       NodeProperties::SetType(value, Type::OtherInternal());
920       ReplaceWithValue(node, value, effect);
921       return Replace(value);
922     }
923   } else {
924     IndexRange field_index = FieldIndexOf(access);
925     if (field_index != IndexRange::Invalid()) {
926       MachineRepresentation representation =
927           access.machine_type.representation();
928       FieldInfo const* lookup_result =
929           state->LookupField(object, field_index, access.const_field_info);
930       if (!lookup_result && access.const_field_info.IsConst()) {
931         // If the access is const and we didn't find anything, also try to look
932         // up information from mutable stores
933         lookup_result =
934             state->LookupField(object, field_index, ConstFieldInfo::None());
935       }
936       if (lookup_result) {
937         // Make sure we don't reuse values that were recorded with a different
938         // representation or resurrect dead {replacement} nodes.
939         Node* replacement = lookup_result->value;
940         if (IsCompatible(representation, lookup_result->representation) &&
941             !replacement->IsDead()) {
942           // Introduce a TypeGuard if the type of the {replacement} node is not
943           // a subtype of the original {node}'s type.
944           if (!NodeProperties::GetType(replacement)
945                    .Is(NodeProperties::GetType(node))) {
946             Type replacement_type = Type::Intersect(
947                 NodeProperties::GetType(node),
948                 NodeProperties::GetType(replacement), graph()->zone());
949             replacement = effect =
950                 graph()->NewNode(common()->TypeGuard(replacement_type),
951                                  replacement, effect, control);
952             NodeProperties::SetType(replacement, replacement_type);
953           }
954           ReplaceWithValue(node, replacement, effect);
955           return Replace(replacement);
956         }
957       }
958       FieldInfo info(node, representation, access.name,
959                      access.const_field_info);
960       state = state->AddField(object, field_index, info, zone());
961     }
962   }
963   Handle<Map> field_map;
964   if (access.map.ToHandle(&field_map)) {
965     state = state->SetMaps(node, ZoneHandleSet<Map>(field_map), zone());
966   }
967   return UpdateState(node, state);
968 }
969 
ReduceStoreField(Node * node,FieldAccess const & access)970 Reduction LoadElimination::ReduceStoreField(Node* node,
971                                             FieldAccess const& access) {
972   Node* const object = NodeProperties::GetValueInput(node, 0);
973   Node* const new_value = NodeProperties::GetValueInput(node, 1);
974   Node* const effect = NodeProperties::GetEffectInput(node);
975   AbstractState const* state = node_states_.Get(effect);
976   if (state == nullptr) return NoChange();
977   if (access.offset == HeapObject::kMapOffset &&
978       access.base_is_tagged == kTaggedBase) {
979     DCHECK(IsAnyTagged(access.machine_type.representation()));
980     // Kill all potential knowledge about the {object}s map.
981     state = state->KillMaps(object, zone());
982     Type const new_value_type = NodeProperties::GetType(new_value);
983     if (new_value_type.IsHeapConstant()) {
984       // Record the new {object} map information.
985       ZoneHandleSet<Map> object_maps(
986           new_value_type.AsHeapConstant()->Ref().AsMap().object());
987       state = state->SetMaps(object, object_maps, zone());
988     }
989   } else {
990     IndexRange field_index = FieldIndexOf(access);
991     if (field_index != IndexRange::Invalid()) {
992       bool is_const_store = access.const_field_info.IsConst();
993       MachineRepresentation representation =
994           access.machine_type.representation();
995       FieldInfo const* lookup_result =
996           state->LookupField(object, field_index, access.const_field_info);
997 
998       if (lookup_result &&
999           (!is_const_store || V8_ENABLE_DOUBLE_CONST_STORE_CHECK_BOOL)) {
1000         // At runtime, we should never encounter
1001         // - any store replacing existing info with a different, incompatible
1002         //   representation, nor
1003         // - two consecutive const stores, unless the latter is a store into
1004         //   a literal.
1005         // However, we may see such code statically, so we guard against
1006         // executing it by emitting Unreachable.
1007         // TODO(gsps): Re-enable the double const store check even for
1008         //   non-debug builds once we have identified other FieldAccesses
1009         //   that should be marked mutable instead of const
1010         //   (cf. JSCreateLowering::AllocateFastLiteral).
1011         bool incompatible_representation =
1012             !lookup_result->name.is_null() &&
1013             !IsCompatible(representation, lookup_result->representation);
1014         bool illegal_double_const_store =
1015             is_const_store && !access.is_store_in_literal;
1016         if (incompatible_representation || illegal_double_const_store) {
1017           Node* control = NodeProperties::GetControlInput(node);
1018           Node* unreachable =
1019               graph()->NewNode(common()->Unreachable(), effect, control);
1020           return Replace(unreachable);
1021         }
1022         if (lookup_result->value == new_value) {
1023           // This store is fully redundant.
1024           return Replace(effect);
1025         }
1026       }
1027 
1028       // Kill all potentially aliasing fields and record the new value.
1029       FieldInfo new_info(new_value, representation, access.name,
1030                          access.const_field_info);
1031       if (is_const_store && access.is_store_in_literal) {
1032         // We only kill const information when there is a chance that we
1033         // previously stored information about the given const field (namely,
1034         // when we observe const stores to literals).
1035         state = state->KillConstField(object, field_index, zone());
1036       }
1037       state = state->KillField(object, field_index, access.name, zone());
1038       state = state->AddField(object, field_index, new_info, zone());
1039       if (is_const_store) {
1040         // For const stores, we track information in both the const and the
1041         // mutable world to guard against field accesses that should have
1042         // been marked const, but were not.
1043         new_info.const_field_info = ConstFieldInfo::None();
1044         state = state->AddField(object, field_index, new_info, zone());
1045       }
1046     } else {
1047       // Unsupported StoreField operator.
1048       state = state->KillFields(object, access.name, zone());
1049     }
1050   }
1051   return UpdateState(node, state);
1052 }
1053 
ReduceLoadElement(Node * node)1054 Reduction LoadElimination::ReduceLoadElement(Node* node) {
1055   Node* const object = NodeProperties::GetValueInput(node, 0);
1056   Node* const index = NodeProperties::GetValueInput(node, 1);
1057   Node* const effect = NodeProperties::GetEffectInput(node);
1058   AbstractState const* state = node_states_.Get(effect);
1059   if (state == nullptr) return NoChange();
1060 
1061   // Only handle loads that do not require truncations.
1062   ElementAccess const& access = ElementAccessOf(node->op());
1063   switch (access.machine_type.representation()) {
1064     case MachineRepresentation::kNone:
1065     case MachineRepresentation::kBit:
1066     case MachineRepresentation::kWord8:
1067     case MachineRepresentation::kWord16:
1068     case MachineRepresentation::kWord32:
1069     case MachineRepresentation::kWord64:
1070     case MachineRepresentation::kFloat32:
1071     case MachineRepresentation::kCompressedPointer:
1072     case MachineRepresentation::kCompressed:
1073     case MachineRepresentation::kSandboxedPointer:
1074       // TODO(turbofan): Add support for doing the truncations.
1075       break;
1076     case MachineRepresentation::kFloat64:
1077     case MachineRepresentation::kSimd128:
1078     case MachineRepresentation::kTaggedSigned:
1079     case MachineRepresentation::kTaggedPointer:
1080     case MachineRepresentation::kTagged:
1081     case MachineRepresentation::kMapWord:
1082       if (Node* replacement = state->LookupElement(
1083               object, index, access.machine_type.representation())) {
1084         // Make sure we don't resurrect dead {replacement} nodes.
1085         // Skip lowering if the type of the {replacement} node is not a subtype
1086         // of the original {node}'s type.
1087         // TODO(turbofan): We should insert a {TypeGuard} for the intersection
1088         // of these two types here once we properly handle {Type::None}
1089         // everywhere.
1090         if (!replacement->IsDead() && NodeProperties::GetType(replacement)
1091                                           .Is(NodeProperties::GetType(node))) {
1092           ReplaceWithValue(node, replacement, effect);
1093           return Replace(replacement);
1094         }
1095       }
1096       state = state->AddElement(object, index, node,
1097                                 access.machine_type.representation(), zone());
1098       return UpdateState(node, state);
1099   }
1100   return NoChange();
1101 }
1102 
ReduceStoreElement(Node * node)1103 Reduction LoadElimination::ReduceStoreElement(Node* node) {
1104   ElementAccess const& access = ElementAccessOf(node->op());
1105   Node* const object = NodeProperties::GetValueInput(node, 0);
1106   Node* const index = NodeProperties::GetValueInput(node, 1);
1107   Node* const new_value = NodeProperties::GetValueInput(node, 2);
1108   Node* const effect = NodeProperties::GetEffectInput(node);
1109   AbstractState const* state = node_states_.Get(effect);
1110   if (state == nullptr) return NoChange();
1111   Node* const old_value =
1112       state->LookupElement(object, index, access.machine_type.representation());
1113   if (old_value == new_value) {
1114     // This store is fully redundant.
1115     return Replace(effect);
1116   }
1117   // Kill all potentially aliasing elements.
1118   state = state->KillElement(object, index, zone());
1119   // Only record the new value if the store doesn't have an implicit truncation.
1120   switch (access.machine_type.representation()) {
1121     case MachineRepresentation::kNone:
1122     case MachineRepresentation::kBit:
1123     case MachineRepresentation::kWord8:
1124     case MachineRepresentation::kWord16:
1125     case MachineRepresentation::kWord32:
1126     case MachineRepresentation::kWord64:
1127     case MachineRepresentation::kFloat32:
1128     case MachineRepresentation::kCompressedPointer:
1129     case MachineRepresentation::kCompressed:
1130     case MachineRepresentation::kSandboxedPointer:
1131       // TODO(turbofan): Add support for doing the truncations.
1132       break;
1133     case MachineRepresentation::kFloat64:
1134     case MachineRepresentation::kSimd128:
1135     case MachineRepresentation::kTaggedSigned:
1136     case MachineRepresentation::kTaggedPointer:
1137     case MachineRepresentation::kTagged:
1138     case MachineRepresentation::kMapWord:
1139       state = state->AddElement(object, index, new_value,
1140                                 access.machine_type.representation(), zone());
1141       break;
1142   }
1143   return UpdateState(node, state);
1144 }
1145 
ReduceStoreTypedElement(Node * node)1146 Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
1147   Node* const effect = NodeProperties::GetEffectInput(node);
1148   AbstractState const* state = node_states_.Get(effect);
1149   if (state == nullptr) return NoChange();
1150   return UpdateState(node, state);
1151 }
1152 
UpdateStateForPhi(AbstractState const * state,Node * effect_phi,Node * phi)1153 LoadElimination::AbstractState const* LoadElimination::UpdateStateForPhi(
1154     AbstractState const* state, Node* effect_phi, Node* phi) {
1155   int predecessor_count = phi->InputCount() - 1;
1156   // TODO(jarin) Consider doing a union here. At the moment, we just keep this
1157   // consistent with AbstractState::Merge.
1158 
1159   // Check if all the inputs have the same maps.
1160   AbstractState const* input_state =
1161       node_states_.Get(NodeProperties::GetEffectInput(effect_phi, 0));
1162   ZoneHandleSet<Map> object_maps;
1163   if (!input_state->LookupMaps(phi->InputAt(0), &object_maps)) return state;
1164   for (int i = 1; i < predecessor_count; i++) {
1165     input_state =
1166         node_states_.Get(NodeProperties::GetEffectInput(effect_phi, i));
1167     ZoneHandleSet<Map> input_maps;
1168     if (!input_state->LookupMaps(phi->InputAt(i), &input_maps)) return state;
1169     if (input_maps != object_maps) return state;
1170   }
1171   return state->SetMaps(phi, object_maps, zone());
1172 }
1173 
ReduceEffectPhi(Node * node)1174 Reduction LoadElimination::ReduceEffectPhi(Node* node) {
1175   Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
1176   Node* const control = NodeProperties::GetControlInput(node);
1177   AbstractState const* state0 = node_states_.Get(effect0);
1178   if (state0 == nullptr) return NoChange();
1179   if (control->opcode() == IrOpcode::kLoop) {
1180     // Here we rely on having only reducible loops:
1181     // The loop entry edge always dominates the header, so we can just take
1182     // the state from the first input, and compute the loop state based on it.
1183     AbstractState const* state = ComputeLoopState(node, state0);
1184     return UpdateState(node, state);
1185   }
1186   DCHECK_EQ(IrOpcode::kMerge, control->opcode());
1187 
1188   // Shortcut for the case when we do not know anything about some input.
1189   int const input_count = node->op()->EffectInputCount();
1190   for (int i = 1; i < input_count; ++i) {
1191     Node* const effect = NodeProperties::GetEffectInput(node, i);
1192     if (node_states_.Get(effect) == nullptr) return NoChange();
1193   }
1194 
1195   // Make a copy of the first input's state and merge with the state
1196   // from other inputs.
1197   AbstractState* state = zone()->New<AbstractState>(*state0);
1198   for (int i = 1; i < input_count; ++i) {
1199     Node* const input = NodeProperties::GetEffectInput(node, i);
1200     state->Merge(node_states_.Get(input), zone());
1201   }
1202 
1203   // For each phi, try to compute the new state for the phi from
1204   // the inputs.
1205   AbstractState const* state_with_phis = state;
1206   for (Node* use : control->uses()) {
1207     if (use->opcode() == IrOpcode::kPhi) {
1208       state_with_phis = UpdateStateForPhi(state_with_phis, node, use);
1209     }
1210   }
1211 
1212   return UpdateState(node, state_with_phis);
1213 }
1214 
ReduceStart(Node * node)1215 Reduction LoadElimination::ReduceStart(Node* node) {
1216   return UpdateState(node, empty_state());
1217 }
1218 
ReduceOtherNode(Node * node)1219 Reduction LoadElimination::ReduceOtherNode(Node* node) {
1220   if (node->op()->EffectInputCount() == 1) {
1221     if (node->op()->EffectOutputCount() == 1) {
1222       Node* const effect = NodeProperties::GetEffectInput(node);
1223       AbstractState const* state = node_states_.Get(effect);
1224       // If we do not know anything about the predecessor, do not propagate
1225       // just yet because we will have to recompute anyway once we compute
1226       // the predecessor.
1227       if (state == nullptr) return NoChange();
1228       // Check if this {node} has some uncontrolled side effects.
1229       if (!node->op()->HasProperty(Operator::kNoWrite)) {
1230         state = state->KillAll(zone());
1231       }
1232       return UpdateState(node, state);
1233     } else {
1234       // Effect terminators should be handled specially.
1235       return NoChange();
1236     }
1237   }
1238   DCHECK_EQ(0, node->op()->EffectInputCount());
1239   DCHECK_EQ(0, node->op()->EffectOutputCount());
1240   return NoChange();
1241 }
1242 
UpdateState(Node * node,AbstractState const * state)1243 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
1244   AbstractState const* original = node_states_.Get(node);
1245   // Only signal that the {node} has Changed, if the information about {state}
1246   // has changed wrt. the {original}.
1247   if (state != original) {
1248     if (original == nullptr || !state->Equals(original)) {
1249       node_states_.Set(node, state);
1250       return Changed(node);
1251     }
1252   }
1253   return NoChange();
1254 }
1255 
1256 LoadElimination::AbstractState const*
ComputeLoopStateForStoreField(Node * current,LoadElimination::AbstractState const * state,FieldAccess const & access) const1257 LoadElimination::ComputeLoopStateForStoreField(
1258     Node* current, LoadElimination::AbstractState const* state,
1259     FieldAccess const& access) const {
1260   Node* const object = NodeProperties::GetValueInput(current, 0);
1261   if (access.offset == HeapObject::kMapOffset) {
1262     // Invalidate what we know about the {object}s map.
1263     state = state->KillMaps(object, zone());
1264   } else {
1265     IndexRange field_index = FieldIndexOf(access);
1266     if (field_index == IndexRange::Invalid()) {
1267       state = state->KillFields(object, access.name, zone());
1268     } else {
1269       state = state->KillField(object, field_index, access.name, zone());
1270     }
1271   }
1272   return state;
1273 }
1274 
ComputeLoopState(Node * node,AbstractState const * state) const1275 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
1276     Node* node, AbstractState const* state) const {
1277   Node* const control = NodeProperties::GetControlInput(node);
1278   struct TransitionElementsKindInfo {
1279     ElementsTransition transition;
1280     Node* object;
1281   };
1282   // Allocate zone data structures in a temporary zone with a lifetime limited
1283   // to this function to avoid blowing up the size of the stage-global zone.
1284   Zone temp_zone(zone()->allocator(), "Temporary scoped zone");
1285   ZoneVector<TransitionElementsKindInfo> element_transitions_(&temp_zone);
1286   ZoneQueue<Node*> queue(&temp_zone);
1287   ZoneSet<Node*> visited(&temp_zone);
1288   visited.insert(node);
1289   for (int i = 1; i < control->InputCount(); ++i) {
1290     queue.push(node->InputAt(i));
1291   }
1292   while (!queue.empty()) {
1293     Node* const current = queue.front();
1294     queue.pop();
1295     if (visited.find(current) == visited.end()) {
1296       visited.insert(current);
1297       if (!current->op()->HasProperty(Operator::kNoWrite)) {
1298         switch (current->opcode()) {
1299           case IrOpcode::kEnsureWritableFastElements: {
1300             Node* const object = NodeProperties::GetValueInput(current, 0);
1301             state = state->KillField(
1302                 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1303                 MaybeHandle<Name>(), zone());
1304             break;
1305           }
1306           case IrOpcode::kMaybeGrowFastElements: {
1307             Node* const object = NodeProperties::GetValueInput(current, 0);
1308             state = state->KillField(
1309                 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1310                 MaybeHandle<Name>(), zone());
1311             break;
1312           }
1313           case IrOpcode::kTransitionElementsKind: {
1314             ElementsTransition transition = ElementsTransitionOf(current->op());
1315             Node* const object = NodeProperties::GetValueInput(current, 0);
1316             ZoneHandleSet<Map> object_maps;
1317             if (!state->LookupMaps(object, &object_maps) ||
1318                 !ZoneHandleSet<Map>(transition.target())
1319                      .contains(object_maps)) {
1320               element_transitions_.push_back({transition, object});
1321             }
1322             break;
1323           }
1324           case IrOpcode::kTransitionAndStoreElement: {
1325             Node* const object = NodeProperties::GetValueInput(current, 0);
1326             // Invalidate what we know about the {object}s map.
1327             state = state->KillMaps(object, zone());
1328             // Kill the elements as well.
1329             state = state->KillField(
1330                 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1331                 MaybeHandle<Name>(), zone());
1332             break;
1333           }
1334           case IrOpcode::kStoreField: {
1335             FieldAccess access = FieldAccessOf(current->op());
1336             state = ComputeLoopStateForStoreField(current, state, access);
1337             break;
1338           }
1339           case IrOpcode::kStoreElement: {
1340             Node* const object = NodeProperties::GetValueInput(current, 0);
1341             Node* const index = NodeProperties::GetValueInput(current, 1);
1342             state = state->KillElement(object, index, zone());
1343             break;
1344           }
1345           case IrOpcode::kStoreTypedElement: {
1346             // Doesn't affect anything we track with the state currently.
1347             break;
1348           }
1349           default:
1350             return state->KillAll(zone());
1351         }
1352       }
1353       for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
1354         queue.push(NodeProperties::GetEffectInput(current, i));
1355       }
1356     }
1357   }
1358 
1359   // Finally, we apply the element transitions. For each transition, we will try
1360   // to only invalidate information about nodes that can have the transition's
1361   // source map. The trouble is that an object can be transitioned by some other
1362   // transition to the source map. In that case, the other transition will
1363   // invalidate the information, so we are mostly fine.
1364   //
1365   // The only bad case is
1366   //
1367   //    mapA   ---fast--->   mapB   ---slow--->   mapC
1368   //
1369   // If we process the slow transition first on an object that has mapA, we will
1370   // ignore the transition because the object does not have its source map
1371   // (mapB). When we later process the fast transition, we invalidate the
1372   // object's map, but we keep the information about the object's elements. This
1373   // is wrong because the elements will be overwritten by the slow transition.
1374   //
1375   // Note that the slow-slow case is fine because either of the slow transition
1376   // will invalidate the elements field, so the processing order does not
1377   // matter.
1378   //
1379   // To handle the bad case properly, we first kill the maps using all
1380   // transitions. We kill the the fields later when all the transitions are
1381   // already reflected in the map information.
1382 
1383   for (const TransitionElementsKindInfo& t : element_transitions_) {
1384     AliasStateInfo alias_info(state, t.object, t.transition.source());
1385     state = state->KillMaps(alias_info, zone());
1386   }
1387   for (const TransitionElementsKindInfo& t : element_transitions_) {
1388     switch (t.transition.mode()) {
1389       case ElementsTransition::kFastTransition:
1390         break;
1391       case ElementsTransition::kSlowTransition: {
1392         AliasStateInfo alias_info(state, t.object, t.transition.source());
1393         state = state->KillField(
1394             alias_info, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1395             MaybeHandle<Name>(), zone());
1396         break;
1397       }
1398     }
1399   }
1400   return state;
1401 }
1402 
1403 // static
FieldIndexOf(int offset,int representation_size)1404 LoadElimination::IndexRange LoadElimination::FieldIndexOf(
1405     int offset, int representation_size) {
1406   DCHECK(IsAligned(offset, kTaggedSize));
1407   int field_index = offset / kTaggedSize - 1;
1408   DCHECK_EQ(0, representation_size % kTaggedSize);
1409   return IndexRange(field_index, representation_size / kTaggedSize);
1410 }
1411 
1412 // static
FieldIndexOf(FieldAccess const & access)1413 LoadElimination::IndexRange LoadElimination::FieldIndexOf(
1414     FieldAccess const& access) {
1415   MachineRepresentation rep = access.machine_type.representation();
1416   switch (rep) {
1417     case MachineRepresentation::kNone:
1418     case MachineRepresentation::kBit:
1419     case MachineRepresentation::kSimd128:
1420       UNREACHABLE();
1421     case MachineRepresentation::kWord8:
1422     case MachineRepresentation::kWord16:
1423     case MachineRepresentation::kFloat32:
1424       // Currently untracked.
1425       return IndexRange::Invalid();
1426     case MachineRepresentation::kFloat64:
1427     case MachineRepresentation::kWord32:
1428     case MachineRepresentation::kWord64:
1429     case MachineRepresentation::kTaggedSigned:
1430     case MachineRepresentation::kTaggedPointer:
1431     case MachineRepresentation::kTagged:
1432     case MachineRepresentation::kMapWord:
1433     case MachineRepresentation::kCompressedPointer:
1434     case MachineRepresentation::kCompressed:
1435     case MachineRepresentation::kSandboxedPointer:
1436       break;
1437   }
1438   int representation_size = ElementSizeInBytes(rep);
1439   // We currently only track fields that are at least tagged pointer sized.
1440   if (representation_size < kTaggedSize) return IndexRange::Invalid();
1441   DCHECK_EQ(0, representation_size % kTaggedSize);
1442 
1443   if (access.base_is_tagged != kTaggedBase) {
1444     // We currently only track tagged objects.
1445     return IndexRange::Invalid();
1446   }
1447   return FieldIndexOf(access.offset, representation_size);
1448 }
1449 
common() const1450 CommonOperatorBuilder* LoadElimination::common() const {
1451   return jsgraph()->common();
1452 }
1453 
graph() const1454 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
1455 
isolate() const1456 Isolate* LoadElimination::isolate() const { return jsgraph()->isolate(); }
1457 
factory() const1458 Factory* LoadElimination::factory() const { return jsgraph()->factory(); }
1459 
1460 }  // namespace compiler
1461 }  // namespace internal
1462 }  // namespace v8
1463