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