v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
load-elimination.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
14#include "src/heap/factory.h"
16
17namespace v8 {
18namespace internal {
19namespace compiler {
20
21namespace {
22
23bool IsRename(Node* node) {
24 switch (node->opcode()) {
25 case IrOpcode::kCheckHeapObject:
26 case IrOpcode::kFinishRegion:
27 case IrOpcode::kTypeGuard:
28 return !node->IsDead();
29 default:
30 return false;
31 }
32}
33
34Node* ResolveRenames(Node* node) {
35 while (IsRename(node)) {
36 node = node->InputAt(0);
37 }
38 return node;
39}
40
41bool MayAlias(Node* a, Node* b) {
42 if (a != b) {
44 return false;
45 } else if (IsRename(b)) {
46 return MayAlias(a, b->InputAt(0));
47 } else if (IsRename(a)) {
48 return MayAlias(a->InputAt(0), b);
49 } else if (b->opcode() == IrOpcode::kAllocate) {
50 switch (a->opcode()) {
51 case IrOpcode::kAllocate:
52 case IrOpcode::kHeapConstant:
53 case IrOpcode::kParameter:
54 return false;
55 default:
56 break;
57 }
58 } else if (a->opcode() == IrOpcode::kAllocate) {
59 switch (b->opcode()) {
60 case IrOpcode::kHeapConstant:
61 case IrOpcode::kParameter:
62 return false;
63 default:
64 break;
65 }
66 }
67 }
68 return true;
69}
70
71bool MustAlias(Node* a, Node* b) {
72 return ResolveRenames(a) == ResolveRenames(b);
73}
74
75} // namespace
76
78 if (v8_flags.trace_turbo_load_elimination) {
79 if (node->op()->EffectInputCount() > 0) {
80 PrintF(" visit #%d:%s", node->id(), node->op()->mnemonic());
81 if (node->op()->ValueInputCount() > 0) {
82 PrintF("(");
83 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
84 if (i > 0) PrintF(", ");
85 Node* const value = NodeProperties::GetValueInput(node, i);
86 PrintF("#%d:%s", value->id(), value->op()->mnemonic());
87 }
88 PrintF(")");
89 }
90 PrintF("\n");
91 for (int i = 0; i < node->op()->EffectInputCount(); ++i) {
92 Node* const effect = NodeProperties::GetEffectInput(node, i);
93 if (AbstractState const* const state = node_states_.Get(effect)) {
94 PrintF(" state[%i]: #%d:%s\n", i, effect->id(),
95 effect->op()->mnemonic());
96 state->Print();
97 } else {
98 PrintF(" no state[%i]: #%d:%s\n", i, effect->id(),
99 effect->op()->mnemonic());
100 }
101 }
102 }
103 }
104 switch (node->opcode()) {
105 case IrOpcode::kMapGuard:
106 return ReduceMapGuard(node);
107 case IrOpcode::kCheckMaps:
108 return ReduceCheckMaps(node);
109 case IrOpcode::kCompareMaps:
110 return ReduceCompareMaps(node);
111 case IrOpcode::kEnsureWritableFastElements:
113 case IrOpcode::kMaybeGrowFastElements:
114 return ReduceMaybeGrowFastElements(node);
115 case IrOpcode::kTransitionElementsKind:
116 return ReduceTransitionElementsKind(node);
117 case IrOpcode::kTransitionElementsKindOrCheckMap:
119 case IrOpcode::kLoadField:
120 return ReduceLoadField(node, FieldAccessOf(node->op()));
121 case IrOpcode::kStoreField:
122 return ReduceStoreField(node, FieldAccessOf(node->op()));
123 case IrOpcode::kLoadElement:
124 return ReduceLoadElement(node);
125 case IrOpcode::kStoreElement:
126 return ReduceStoreElement(node);
127 case IrOpcode::kTransitionAndStoreElement:
129 case IrOpcode::kStoreTypedElement:
130 return ReduceStoreTypedElement(node);
131 case IrOpcode::kEffectPhi:
132 return ReduceEffectPhi(node);
133 case IrOpcode::kDead:
134 break;
135 case IrOpcode::kStart:
136 return ReduceStart(node);
137 default:
138 return ReduceOtherNode(node);
139 }
140 return NoChange();
141}
142
143namespace {
144
145bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
146 if (r1 == r2) return true;
147 return IsAnyTagged(r1) && IsAnyTagged(r2);
148}
149
150} // namespace
151
152LoadElimination::AbstractState const
154
156 Node* object, Node* index, MachineRepresentation representation) const {
157 for (Element const element : elements_) {
158 if (element.object == nullptr) continue;
159 DCHECK_NOT_NULL(element.index);
160 DCHECK_NOT_NULL(element.value);
161 if (MustAlias(object, element.object) && MustAlias(index, element.index) &&
162 IsCompatible(representation, element.representation)) {
163 return element.value;
164 }
165 }
166 return nullptr;
167}
168
171 Zone* zone) const {
172 for (Element const element : this->elements_) {
173 if (element.object == nullptr) continue;
174 if (MayAlias(object, element.object)) {
176 for (Element const element2 : this->elements_) {
177 if (element2.object == nullptr) continue;
178 DCHECK_NOT_NULL(element2.index);
179 DCHECK_NOT_NULL(element2.value);
180 if (!MayAlias(object, element2.object) ||
182 NodeProperties::GetType(element2.index))) {
183 that->elements_[that->next_index_++] = element2;
184 }
185 }
187 return that;
188 }
189 }
190 return this;
191}
192
194 AbstractElements const* that) const {
195 if (this == that) return true;
196 for (size_t i = 0; i < arraysize(elements_); ++i) {
197 Element this_element = this->elements_[i];
198 if (this_element.object == nullptr) continue;
199 for (size_t j = 0;; ++j) {
200 if (j == arraysize(elements_)) return false;
201 Element that_element = that->elements_[j];
202 if (this_element.object == that_element.object &&
203 this_element.index == that_element.index &&
204 this_element.value == that_element.value) {
205 break;
206 }
207 }
208 }
209 for (size_t i = 0; i < arraysize(elements_); ++i) {
210 Element that_element = that->elements_[i];
211 if (that_element.object == nullptr) continue;
212 for (size_t j = 0;; ++j) {
213 if (j == arraysize(elements_)) return false;
214 Element this_element = this->elements_[j];
215 if (that_element.object == this_element.object &&
216 that_element.index == this_element.index &&
217 that_element.value == this_element.value) {
218 break;
219 }
220 }
221 }
222 return true;
223}
224
227 Zone* zone) const {
228 if (this->Equals(that)) return this;
230 for (Element const this_element : this->elements_) {
231 if (this_element.object == nullptr) continue;
232 for (Element const that_element : that->elements_) {
233 if (this_element.object == that_element.object &&
234 this_element.index == that_element.index &&
235 this_element.value == that_element.value) {
236 copy->elements_[copy->next_index_++] = this_element;
237 break;
238 }
239 }
240 }
242 return copy;
243}
244
246 for (Element const& element : elements_) {
247 if (element.object) {
248 PrintF(" #%d:%s @ #%d:%s -> #%d:%s\n", element.object->id(),
249 element.object->op()->mnemonic(), element.index->id(),
250 element.index->op()->mnemonic(), element.value->id(),
251 element.value->op()->mnemonic());
252 }
253 }
254}
255
257 Node* object) const {
258 for (auto& pair : info_for_node_) {
259 if (pair.first->IsDead()) continue;
260 if (MustAlias(object, pair.first)) return &pair.second;
261 }
262 return nullptr;
263}
264
265namespace {
266
267bool MayAlias(MaybeHandle<Name> x, MaybeHandle<Name> y) {
268 if (!x.address()) return true;
269 if (!y.address()) return true;
270 if (x.address() != y.address()) return false;
271 return true;
272}
273
274} // namespace
275
277 public:
278 AliasStateInfo(const AbstractState* state, Node* object, MapRef map)
279 : state_(state), object_(object), map_(map) {}
280 AliasStateInfo(const AbstractState* state, Node* object)
281 : state_(state), object_(object) {}
282
283 bool MayAlias(Node* other) const;
284
285 private:
288 OptionalMapRef map_;
289};
290
292 Node* object, Zone* zone) const {
293 for (auto info1 : this->info_for_node_) {
294 if (info1.first->IsDead()) continue;
295 // If we previously recorded information about a const store on the given
296 // 'object', we might not have done it on the same node; e.g. we might now
297 // identify the object by a FinishRegion node, whereas the initial const
298 // store was performed on the Allocate node. We therefore remove information
299 // on all nodes that must alias with 'object'.
300 if (MustAlias(object, info1.first)) {
302 for (auto info2 : this->info_for_node_) {
303 if (!MustAlias(object, info2.first)) {
304 that->info_for_node_.insert(info2);
305 }
306 }
307 return that;
308 }
309 }
310 return this;
311}
312
314 const AliasStateInfo& alias_info, MaybeHandle<Name> name,
315 Zone* zone) const {
316 for (auto info1 : this->info_for_node_) {
317 if (info1.first->IsDead()) continue;
318 if (alias_info.MayAlias(info1.first)) {
320 for (auto info2 : this->info_for_node_) {
321 if (!alias_info.MayAlias(info2.first) ||
322 !MayAlias(name, info2.second.name)) {
323 that->info_for_node_.insert(info2);
324 }
325 }
326 return that;
327 }
328 }
329 return this;
330}
331
333 for (auto pair : info_for_node_) {
334 PrintF(" #%d:%s -> #%d:%s [repr=%s]\n", pair.first->id(),
335 pair.first->op()->mnemonic(), pair.second.value->id(),
336 pair.second.value->op()->mnemonic(),
337 MachineReprToString(pair.second.representation));
338 }
339}
340
343
345 Zone* zone)
346 : info_for_node_(zone) {
347 object = ResolveRenames(object);
348 info_for_node_.insert(std::make_pair(object, maps));
349}
350
352 ZoneRefSet<Map>* object_maps) const {
353 auto it = info_for_node_.find(ResolveRenames(object));
354 if (it == info_for_node_.end()) return false;
355 *object_maps = it->second;
356 return true;
357}
358
360 const AliasStateInfo& alias_info, Zone* zone) const {
361 for (auto info1 : this->info_for_node_) {
362 if (alias_info.MayAlias(info1.first)) {
364 for (auto info2 : this->info_for_node_) {
365 if (!alias_info.MayAlias(info2.first))
366 that->info_for_node_.insert(info2);
367 }
368 return that;
369 }
370 }
371 return this;
372}
373
375 AbstractMaps const* that, Zone* zone) const {
376 if (this->Equals(that)) return this;
378 for (auto this_it : this->info_for_node_) {
379 Node* this_object = this_it.first;
380 ZoneRefSet<Map> this_maps = this_it.second;
381 auto that_it = that->info_for_node_.find(this_object);
382 if (that_it != that->info_for_node_.end() && that_it->second == this_maps) {
383 copy->info_for_node_.insert(this_it);
384 }
385 }
386 return copy;
387}
388
390 Node* object, ZoneRefSet<Map> maps, Zone* zone) const {
391 AbstractMaps* that = zone->New<AbstractMaps>(*this);
392 if (that->info_for_node_.size() >= kMaxTrackedObjects) {
393 // We are tracking too many objects, which leads to bad performance.
394 // Delete one to avoid the map from becoming bigger.
395 that->info_for_node_.erase(that->info_for_node_.begin());
396 }
397 object = ResolveRenames(object);
398 that->info_for_node_[object] = maps;
399 return that;
400}
401
403 AllowHandleDereference allow_handle_dereference;
404 StdoutStream os;
405 for (auto pair : info_for_node_) {
406 os << " #" << pair.first->id() << ":" << pair.first->op()->mnemonic()
407 << std::endl;
408 ZoneRefSet<Map> const& maps = pair.second;
409 for (size_t i = 0; i < maps.size(); ++i) {
410 os << " - " << Brief(*maps[i].object()) << std::endl;
411 }
412 }
413}
414
416 AbstractFields const& this_fields,
417 AbstractFields const& that_fields) const {
418 for (size_t i = 0u; i < this_fields.size(); ++i) {
419 AbstractField const* this_field = this_fields[i];
420 AbstractField const* that_field = that_fields[i];
421 if (this_field) {
422 if (!that_field || !that_field->Equals(this_field)) return false;
423 } else if (that_field) {
424 return false;
425 }
426 }
427 return true;
428}
429
431 if (this->elements_) {
432 if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
433 return false;
434 }
435 } else if (that->elements_) {
436 return false;
437 }
438 if (!FieldsEquals(this->fields_, that->fields_) ||
439 !FieldsEquals(this->const_fields_, that->const_fields_)) {
440 return false;
441 }
442 if (this->maps_) {
443 if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
444 return false;
445 }
446 } else if (that->maps_) {
447 return false;
448 }
449 return true;
450}
451
453 AbstractFields* this_fields, AbstractFields const& that_fields,
454 Zone* zone) {
455 for (size_t i = 0; i < this_fields->size(); ++i) {
456 AbstractField const*& this_field = (*this_fields)[i];
457 if (this_field) {
458 if (that_fields[i]) {
459 this_field = this_field->Merge(that_fields[i], zone, &fields_count_);
460 } else {
461 this_field = nullptr;
462 }
463 }
464 }
465}
466
468 Zone* zone) {
469 // Merge the information we have about the elements.
470 if (this->elements_) {
471 this->elements_ = that->elements_
472 ? that->elements_->Merge(this->elements_, zone)
473 : nullptr;
474 }
475
476 // Merge the information we have about the fields.
477 fields_count_ = 0;
478 FieldsMerge(&this->const_fields_, that->const_fields_, zone);
479 const_fields_count_ = fields_count_;
480 FieldsMerge(&this->fields_, that->fields_, zone);
481
482 // Merge the information we have about the maps.
483 if (this->maps_) {
484 this->maps_ = that->maps_ ? that->maps_->Merge(this->maps_, zone) : nullptr;
485 }
486}
487
489 Node* object, ZoneRefSet<Map>* object_map) const {
490 return this->maps_ && this->maps_->Lookup(object, object_map);
491}
492
494 Node* object, ZoneRefSet<Map> maps, Zone* zone) const {
495 AbstractState* that = zone->New<AbstractState>(*this);
496 if (that->maps_) {
497 that->maps_ = that->maps_->Extend(object, maps, zone);
498 } else {
499 that->maps_ = zone->New<AbstractMaps>(object, maps, zone);
500 }
501 return that;
502}
503
505 const AliasStateInfo& alias_info, Zone* zone) const {
506 if (this->maps_) {
507 AbstractMaps const* that_maps = this->maps_->Kill(alias_info, zone);
508 if (this->maps_ != that_maps) {
509 AbstractState* that = zone->New<AbstractState>(*this);
510 that->maps_ = that_maps;
511 return that;
512 }
513 }
514 return this;
515}
516
518 Node* object, Zone* zone) const {
519 AliasStateInfo alias_info(this, object);
520 return KillMaps(alias_info, zone);
521}
522
524 Node* object, Node* index, MachineRepresentation representation) const {
525 if (this->elements_) {
526 return this->elements_->Lookup(object, index, representation);
527 }
528 return nullptr;
529}
530
533 Node* value,
534 MachineRepresentation representation,
535 Zone* zone) const {
536 AbstractState* that = zone->New<AbstractState>(*this);
537 if (that->elements_) {
538 that->elements_ =
539 that->elements_->Extend(object, index, value, representation, zone);
540 } else {
541 that->elements_ =
542 zone->New<AbstractElements>(object, index, value, representation, zone);
543 }
544 return that;
545}
546
549 Zone* zone) const {
550 if (this->elements_) {
551 AbstractElements const* that_elements =
552 this->elements_->Kill(object, index, zone);
553 if (this->elements_ != that_elements) {
554 AbstractState* that = zone->New<AbstractState>(*this);
555 that->elements_ = that_elements;
556 return that;
557 }
558 }
559 return this;
560}
561
563 Node* object, IndexRange index_range, LoadElimination::FieldInfo info,
564 Zone* zone) const {
565 AbstractState* that = zone->New<AbstractState>(*this);
566 bool is_const = info.const_field_info.IsConst();
567 AbstractFields& fields = is_const ? that->const_fields_ : that->fields_;
568 for (int index : index_range) {
569 int count_before = fields[index] ? fields[index]->count() : 0;
570 if (fields[index]) {
571 fields[index] =
572 fields[index]->Extend(object, info, zone, that->fields_count_);
573 } else {
574 fields[index] = zone->New<AbstractField>(object, info, zone);
575 }
576 int added = fields[index]->count() - count_before;
577 if (is_const) that->const_fields_count_ += added;
578 that->fields_count_ += added;
579 }
580 return that;
581}
582
585 IndexRange index_range,
586 Zone* zone) const {
587 AliasStateInfo alias_info(this, object);
588 AbstractState* that = nullptr;
589 for (int index : index_range) {
590 if (AbstractField const* this_field = this->const_fields_[index]) {
591 this_field = this_field->KillConst(object, zone);
592 if (this->const_fields_[index] != this_field) {
593 if (!that) that = zone->New<AbstractState>(*this);
594 that->const_fields_[index] = this_field;
595 int removed = this->const_fields_[index]->count() -
596 that->const_fields_[index]->count();
597 that->const_fields_count_ -= removed;
598 that->fields_count_ -= removed;
599 }
600 }
601 }
602 return that ? that : this;
603}
604
606 Node* object, IndexRange index_range, MaybeHandle<Name> name,
607 Zone* zone) const {
608 AliasStateInfo alias_info(this, object);
609 return KillField(alias_info, index_range, name, zone);
610}
611
613 const AliasStateInfo& alias_info, IndexRange index_range,
614 MaybeHandle<Name> name, Zone* zone) const {
615 AbstractState* that = nullptr;
616 for (int index : index_range) {
617 if (AbstractField const* this_field = this->fields_[index]) {
618 this_field = this_field->Kill(alias_info, name, zone);
619 if (this->fields_[index] != this_field) {
620 if (!that) that = zone->New<AbstractState>(*this);
621 that->fields_[index] = this_field;
622 int removed =
623 this->fields_[index]->count() - that->fields_[index]->count();
624 that->fields_count_ -= removed;
625 }
626 }
627 }
628 return that ? that : this;
629}
630
633 Zone* zone) const {
634 AliasStateInfo alias_info(this, object);
635 for (size_t i = 0;; ++i) {
636 if (i == fields_.size()) {
637 return this;
638 }
639 if (AbstractField const* this_field = this->fields_[i]) {
640 AbstractField const* that_field =
641 this_field->Kill(alias_info, name, zone);
642 if (that_field != this_field) {
643 AbstractState* that = zone->New<AbstractState>(*this);
644 that->fields_[i] = that_field;
645 while (++i < fields_.size()) {
646 if (this->fields_[i] != nullptr) {
647 that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone);
648 int removed = this->fields_[i]->count() - that->fields_[i]->count();
649 that->fields_count_ -= removed;
650 }
651 }
652 return that;
653 }
654 }
655 }
656}
657
659 Zone* zone) const {
660 // Kill everything except for const fields
661 for (size_t i = 0; i < const_fields_.size(); ++i) {
662 if (const_fields_[i]) {
664 that->const_fields_ = const_fields_;
665 that->const_fields_count_ = const_fields_count_;
666 that->fields_count_ = const_fields_count_;
667 return that;
668 }
669 }
671}
672
674 Node* object, IndexRange index_range,
675 ConstFieldInfo const_field_info) const {
676 // Check if all the indices in {index_range} contain identical information.
677 // If not, a partially overlapping access has invalidated part of the value.
678 std::optional<LoadElimination::FieldInfo const*> result;
679 for (int index : index_range) {
680 LoadElimination::FieldInfo const* info = nullptr;
681 if (const_field_info.IsConst()) {
682 if (AbstractField const* this_field = const_fields_[index]) {
683 info = this_field->Lookup(object);
684 }
685 if (!(info && info->const_field_info == const_field_info)) return nullptr;
686 } else {
687 if (AbstractField const* this_field = fields_[index]) {
688 info = this_field->Lookup(object);
689 }
690 if (!info) return nullptr;
691 }
692 if (!result.has_value()) {
693 result = info;
694 } else if (**result != *info) {
695 // We detected inconsistent information for a field here.
696 // This can happen when incomplete alias information makes an unrelated
697 // write invalidate part of a field and then we re-combine this partial
698 // information.
699 // This is probably OK, but since it's rare, we better bail out here.
700 return nullptr;
701 }
702 }
703 return *result;
704}
705
707 // If {object} is being initialized right here (indicated by {object} being
708 // an Allocate node instead of a FinishRegion node), we know that {other}
709 // can only alias with {object} if they refer to exactly the same node.
710 if (object_->opcode() == IrOpcode::kAllocate) {
711 return object_ == other;
712 }
713 // Decide aliasing based on the node kinds.
714 if (!compiler::MayAlias(object_, other)) {
715 return false;
716 }
717 // Decide aliasing based on maps (if available).
718 if (map_.has_value()) {
719 MapRef map = *map_;
720 ZoneRefSet<Map> other_maps;
721 if (state_->LookupMaps(other, &other_maps) && other_maps.size() == 1) {
722 if (map != other_maps.at(0)) {
723 return false;
724 }
725 }
726 }
727 return true;
728}
729
731 if (maps_) {
732 PrintF(" maps:\n");
733 maps_->Print();
734 }
735 if (elements_) {
736 PrintF(" elements:\n");
737 elements_->Print();
738 }
739 for (size_t i = 0; i < fields_.size(); ++i) {
740 if (AbstractField const* const field = fields_[i]) {
741 PrintF(" field %zu:\n", i);
742 field->Print();
743 }
744 }
745 for (size_t i = 0; i < const_fields_.size(); ++i) {
746 if (AbstractField const* const const_field = const_fields_[i]) {
747 PrintF(" const field %zu:\n", i);
748 const_field->Print();
749 }
750 }
751}
752
755 size_t const id = node->id();
756 if (id < info_for_node_.size()) return info_for_node_[id];
757 return nullptr;
758}
759
761 Node* node, AbstractState const* state) {
762 size_t const id = node->id();
763 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
764 info_for_node_[id] = state;
765}
766
768 ZoneRefSet<Map> const& maps = MapGuardMapsOf(node->op());
769 Node* const object = NodeProperties::GetValueInput(node, 0);
770 Node* const effect = NodeProperties::GetEffectInput(node);
771 AbstractState const* state = node_states_.Get(effect);
772 if (state == nullptr) return NoChange();
773 ZoneRefSet<Map> object_maps;
774 if (state->LookupMaps(object, &object_maps)) {
775 if (maps.contains(object_maps)) return Replace(effect);
776 // TODO(turbofan): Compute the intersection.
777 }
778 state = state->SetMaps(object, maps, zone());
779 return UpdateState(node, state);
780}
781
783 ZoneRefSet<Map> const& maps = CheckMapsParametersOf(node->op()).maps();
784 Node* const object = NodeProperties::GetValueInput(node, 0);
785 Node* const effect = NodeProperties::GetEffectInput(node);
786 AbstractState const* state = node_states_.Get(effect);
787 if (state == nullptr) return NoChange();
788 ZoneRefSet<Map> object_maps;
789 if (state->LookupMaps(object, &object_maps)) {
790 if (maps.contains(object_maps)) return Replace(effect);
791 // TODO(turbofan): Compute the intersection.
792 }
793 state = state->SetMaps(object, maps, zone());
794 return UpdateState(node, state);
795}
796
798 ZoneRefSet<Map> const& maps = CompareMapsParametersOf(node->op());
799 Node* const object = NodeProperties::GetValueInput(node, 0);
800 Node* const effect = NodeProperties::GetEffectInput(node);
801 AbstractState const* state = node_states_.Get(effect);
802 if (state == nullptr) return NoChange();
803 ZoneRefSet<Map> object_maps;
804 if (state->LookupMaps(object, &object_maps)) {
805 if (maps.contains(object_maps)) {
806 Node* value = jsgraph()->TrueConstant();
807 ReplaceWithValue(node, value, effect);
808 return Replace(value);
809 }
810 // TODO(turbofan): Compute the intersection.
811 }
812 return UpdateState(node, state);
813}
814
816 Node* const object = NodeProperties::GetValueInput(node, 0);
817 Node* const elements = NodeProperties::GetValueInput(node, 1);
818 Node* const effect = NodeProperties::GetEffectInput(node);
819 AbstractState const* state = node_states_.Get(effect);
820 if (state == nullptr) return NoChange();
821 // Check if the {elements} already have the fixed array map.
822 ZoneRefSet<Map> elements_maps;
823 ZoneRefSet<Map> fixed_array_maps(broker()->fixed_array_map());
824 if (state->LookupMaps(elements, &elements_maps) &&
825 fixed_array_maps.contains(elements_maps)) {
826 ReplaceWithValue(node, elements, effect);
827 return Replace(elements);
828 }
829 // We know that the resulting elements have the fixed array map.
830 state = state->SetMaps(node, fixed_array_maps, zone());
831 // Kill the previous elements on {object}.
832 state = state->KillField(object,
833 FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
835 // Add the new elements on {object}.
836 state = state->AddField(
837 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
839 return UpdateState(node, state);
840}
841
844 Node* const object = NodeProperties::GetValueInput(node, 0);
845 Node* const effect = NodeProperties::GetEffectInput(node);
846 AbstractState const* state = node_states_.Get(effect);
847 if (state == nullptr) return NoChange();
848 if (params.mode() == GrowFastElementsMode::kDoubleElements) {
849 // We know that the resulting elements have the fixed double array map.
850 state = state->SetMaps(
851 node, ZoneRefSet<Map>(broker()->fixed_double_array_map()), zone());
852 } else {
853 // We know that the resulting elements have the fixed array map or the COW
854 // version thereof (if we didn't grow and it was already COW before).
855 ZoneRefSet<Map> fixed_array_maps(
856 {broker()->fixed_array_map(), broker()->fixed_cow_array_map()}, zone());
857 state = state->SetMaps(node, fixed_array_maps, zone());
858 }
859 // Kill the previous elements on {object}.
860 state = state->KillField(object,
861 FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
863 // Add the new elements on {object}.
864 state = state->AddField(
865 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
867 return UpdateState(node, state);
868}
869
871 ElementsTransition transition = ElementsTransitionOf(node->op());
872 Node* const object = NodeProperties::GetValueInput(node, 0);
873 MapRef source_map(transition.source());
874 MapRef target_map(transition.target());
875 Node* const effect = NodeProperties::GetEffectInput(node);
876 AbstractState const* state = node_states_.Get(effect);
877 if (state == nullptr) return NoChange();
878 switch (transition.mode()) {
880 break;
882 // Kill the elements as well.
883 AliasStateInfo alias_info(state, object, source_map);
884 state = state->KillField(
885 alias_info, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
887 break;
888 }
889 ZoneRefSet<Map> object_maps;
890 if (state->LookupMaps(object, &object_maps)) {
891 if (ZoneRefSet<Map>(target_map).contains(object_maps)) {
892 // The {object} already has the {target_map}, so this TransitionElements
893 // {node} is fully redundant (independent of what {source_map} is).
894 return Replace(effect);
895 }
896 if (object_maps.contains(ZoneRefSet<Map>(source_map))) {
897 object_maps.remove(source_map, zone());
898 object_maps.insert(target_map, zone());
899 AliasStateInfo alias_info(state, object, source_map);
900 state = state->KillMaps(alias_info, zone());
901 state = state->SetMaps(object, object_maps, zone());
902 }
903 } else {
904 AliasStateInfo alias_info(state, object, source_map);
905 state = state->KillMaps(alias_info, zone());
906 }
907 return UpdateState(node, state);
908}
909
913 Node* const object = NodeProperties::GetValueInput(node, 0);
914 const ZoneRefSet<Map>& source_maps = transition.sources();
915 MapRef target_map(transition.target());
916 Node* const effect = NodeProperties::GetEffectInput(node);
917 AbstractState const* state = node_states_.Get(effect);
918 if (state == nullptr) return NoChange();
919 for (MapRef source_map : source_maps) {
920 if (!IsSimpleMapChangeTransition(source_map.elements_kind(),
921 target_map.elements_kind())) {
922 // Kill the elements as well.
923 AliasStateInfo alias_info(state, object, source_map);
924 state = state->KillField(
925 alias_info, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
927 }
928 }
929
930 ZoneRefSet<Map> object_maps;
931 if (state->LookupMaps(object, &object_maps)) {
932 if (ZoneRefSet<Map>(target_map).contains(object_maps)) {
933 // The {object} already has the {target_map}, so this TransitionElements
934 // {node} is fully redundant (independent of what {source_map} is).
935 return Replace(effect);
936 }
937 for (MapRef source_map : source_maps) {
938 if (object_maps.contains(ZoneRefSet<Map>(source_map))) {
939 object_maps.remove(source_map, zone());
940 object_maps.insert(target_map, zone());
941 AliasStateInfo alias_info(state, object, source_map);
942 state = state->KillMaps(alias_info, zone());
943 state = state->SetMaps(object, object_maps, zone());
944 }
945 }
946 } else {
947 for (MapRef source_map : source_maps) {
948 AliasStateInfo alias_info(state, object, source_map);
949 state = state->KillMaps(alias_info, zone());
950 }
951 }
952 state = state->SetMaps(object, ZoneRefSet<Map>(target_map), zone());
953 return UpdateState(node, state);
954}
955
957 Node* const object = NodeProperties::GetValueInput(node, 0);
958 MapRef double_map(DoubleMapParameterOf(node->op()));
959 MapRef fast_map(FastMapParameterOf(node->op()));
960 Node* const effect = NodeProperties::GetEffectInput(node);
961 AbstractState const* state = node_states_.Get(effect);
962 if (state == nullptr) return NoChange();
963
964 // We need to add the double and fast maps to the set of possible maps for
965 // this object, because we don't know which of those we'll transition to.
966 // Additionally, we should kill all alias information.
967 ZoneRefSet<Map> object_maps;
968 if (state->LookupMaps(object, &object_maps)) {
969 object_maps.insert(double_map, zone());
970 object_maps.insert(fast_map, zone());
971 state = state->KillMaps(object, zone());
972 state = state->SetMaps(object, object_maps, zone());
973 }
974 // Kill the elements as well.
975 state = state->KillField(object,
976 FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
978 return UpdateState(node, state);
979}
980
982 FieldAccess const& access) {
983 Node* object = NodeProperties::GetValueInput(node, 0);
984 Node* effect = NodeProperties::GetEffectInput(node);
985 Node* control = NodeProperties::GetControlInput(node);
986 AbstractState const* state = node_states_.Get(effect);
987 if (state == nullptr) return NoChange();
988 if (access.offset == HeapObject::kMapOffset &&
989 access.base_is_tagged == kTaggedBase) {
990 DCHECK(IsAnyTagged(access.machine_type.representation()));
991 ZoneRefSet<Map> object_maps;
992 if (state->LookupMaps(object, &object_maps) && object_maps.size() == 1) {
993 Node* value = jsgraph()->HeapConstantNoHole(object_maps[0].object());
994 NodeProperties::SetType(value, Type::OtherInternal());
995 ReplaceWithValue(node, value, effect);
996 return Replace(value);
997 }
998 } else {
999 IndexRange field_index = FieldIndexOf(access);
1000 if (field_index != IndexRange::Invalid()) {
1001 MachineRepresentation representation =
1002 access.machine_type.representation();
1003 FieldInfo const* lookup_result =
1004 state->LookupField(object, field_index, access.const_field_info);
1005 if (!lookup_result && access.const_field_info.IsConst()) {
1006 // If the access is const and we didn't find anything, also try to look
1007 // up information from mutable stores
1008 lookup_result =
1009 state->LookupField(object, field_index, ConstFieldInfo::None());
1010 }
1011 if (lookup_result) {
1012 // Make sure we don't reuse values that were recorded with a different
1013 // representation or resurrect dead {replacement} nodes.
1014 Node* replacement = lookup_result->value;
1015 if (IsCompatible(representation, lookup_result->representation) &&
1016 !replacement->IsDead()) {
1017 // Introduce a TypeGuard if the type of the {replacement} node is not
1018 // a subtype of the original {node}'s type.
1019 if (!NodeProperties::GetType(replacement)
1020 .Is(NodeProperties::GetType(node))) {
1021 Type replacement_type = Type::Intersect(
1023 NodeProperties::GetType(replacement), graph()->zone());
1024 replacement = effect =
1025 graph()->NewNode(common()->TypeGuard(replacement_type),
1026 replacement, effect, control);
1027 NodeProperties::SetType(replacement, replacement_type);
1028 }
1029 ReplaceWithValue(node, replacement, effect);
1030 return Replace(replacement);
1031 }
1032 }
1033 FieldInfo info(node, representation, access.name,
1034 access.const_field_info);
1035 state = state->AddField(object, field_index, info, zone());
1036 }
1037 }
1038 if (access.map.has_value()) {
1039 state = state->SetMaps(node, ZoneRefSet<Map>(*access.map), zone());
1040 }
1041 return UpdateState(node, state);
1042}
1043
1045 FieldAccess const& access) {
1046 Node* const object = NodeProperties::GetValueInput(node, 0);
1047 Node* const new_value = NodeProperties::GetValueInput(node, 1);
1048 Node* const effect = NodeProperties::GetEffectInput(node);
1049 AbstractState const* state = node_states_.Get(effect);
1050 if (state == nullptr) return NoChange();
1051 if (access.offset == HeapObject::kMapOffset &&
1052 access.base_is_tagged == kTaggedBase) {
1053 DCHECK(IsAnyTagged(access.machine_type.representation()));
1054 // Kill all potential knowledge about the {object}s map.
1055 state = state->KillMaps(object, zone());
1056 Type const new_value_type = NodeProperties::GetType(new_value);
1057 if (new_value_type.IsHeapConstant()) {
1058 // Record the new {object} map information.
1059 ZoneRefSet<Map> object_maps(
1060 new_value_type.AsHeapConstant()->Ref().AsMap());
1061 state = state->SetMaps(object, object_maps, zone());
1062 }
1063 } else {
1064 IndexRange field_index = FieldIndexOf(access);
1065 if (field_index != IndexRange::Invalid()) {
1066 bool is_const_store = access.const_field_info.IsConst();
1067 MachineRepresentation representation =
1068 access.machine_type.representation();
1069 FieldInfo const* lookup_result =
1070 state->LookupField(object, field_index, access.const_field_info);
1071
1072 if (lookup_result &&
1073 (!is_const_store || V8_ENABLE_DOUBLE_CONST_STORE_CHECK_BOOL)) {
1074 // At runtime, we should never encounter
1075 // - any store replacing existing info with a different, incompatible
1076 // representation, nor
1077 // - two consecutive const stores, unless the latter is a store into
1078 // a literal.
1079 // However, we may see such code statically, so we guard against
1080 // executing it by emitting Unreachable.
1081 // TODO(gsps): Re-enable the double const store check even for
1082 // non-debug builds once we have identified other FieldAccesses
1083 // that should be marked mutable instead of const
1084 // (cf. JSCreateLowering::AllocateFastLiteral).
1085 bool incompatible_representation =
1086 !lookup_result->name.is_null() &&
1087 !IsCompatible(representation, lookup_result->representation);
1088 bool illegal_double_const_store =
1089 is_const_store && !access.is_store_in_literal;
1090 if (incompatible_representation || illegal_double_const_store) {
1091 Node* control = NodeProperties::GetControlInput(node);
1092 Node* unreachable =
1093 graph()->NewNode(common()->Unreachable(), effect, control);
1094 return Replace(unreachable);
1095 }
1096 if (lookup_result->value == new_value) {
1097 // This store is fully redundant.
1098 return Replace(effect);
1099 }
1100 }
1101
1102 // Kill all potentially aliasing fields and record the new value.
1103 FieldInfo new_info(new_value, representation, access.name,
1104 access.const_field_info);
1105 if (is_const_store && access.is_store_in_literal) {
1106 // We only kill const information when there is a chance that we
1107 // previously stored information about the given const field (namely,
1108 // when we observe const stores to literals).
1109 state = state->KillConstField(object, field_index, zone());
1110 }
1111 state = state->KillField(object, field_index, access.name, zone());
1112 state = state->AddField(object, field_index, new_info, zone());
1113 if (is_const_store) {
1114 // For const stores, we track information in both the const and the
1115 // mutable world to guard against field accesses that should have
1116 // been marked const, but were not.
1118 state = state->AddField(object, field_index, new_info, zone());
1119 }
1120 } else {
1121 // Unsupported StoreField operator.
1122 state = state->KillFields(object, access.name, zone());
1123 }
1124 }
1125 return UpdateState(node, state);
1126}
1127
1129 Node* const object = NodeProperties::GetValueInput(node, 0);
1130 Node* const index = NodeProperties::GetValueInput(node, 1);
1131 Node* const effect = NodeProperties::GetEffectInput(node);
1132 AbstractState const* state = node_states_.Get(effect);
1133 if (state == nullptr) return NoChange();
1134
1135 // Only handle loads that do not require truncations.
1136 ElementAccess const& access = ElementAccessOf(node->op());
1137 switch (access.machine_type.representation()) {
1152 // TODO(turbofan): Add support for doing the truncations.
1153 break;
1161 if (Node* replacement = state->LookupElement(
1162 object, index, access.machine_type.representation())) {
1163 // Make sure we don't resurrect dead {replacement} nodes.
1164 // Skip lowering if the type of the {replacement} node is not a subtype
1165 // of the original {node}'s type.
1166 // TODO(turbofan): We should insert a {TypeGuard} for the intersection
1167 // of these two types here once we properly handle {Type::None}
1168 // everywhere.
1169 if (!replacement->IsDead() && NodeProperties::GetType(replacement)
1170 .Is(NodeProperties::GetType(node))) {
1171 ReplaceWithValue(node, replacement, effect);
1172 return Replace(replacement);
1173 }
1174 }
1175 state = state->AddElement(object, index, node,
1176 access.machine_type.representation(), zone());
1177 return UpdateState(node, state);
1178 }
1179 return NoChange();
1180}
1181
1183 ElementAccess const& access = ElementAccessOf(node->op());
1184 Node* const object = NodeProperties::GetValueInput(node, 0);
1185 Node* const index = NodeProperties::GetValueInput(node, 1);
1186 Node* const new_value = NodeProperties::GetValueInput(node, 2);
1187 Node* const effect = NodeProperties::GetEffectInput(node);
1188 AbstractState const* state = node_states_.Get(effect);
1189 if (state == nullptr) return NoChange();
1190 Node* const old_value =
1191 state->LookupElement(object, index, access.machine_type.representation());
1192 if (old_value == new_value) {
1193 // This store is fully redundant.
1194 return Replace(effect);
1195 }
1196 // Kill all potentially aliasing elements.
1197 state = state->KillElement(object, index, zone());
1198 // Only record the new value if the store doesn't have an implicit truncation.
1199 switch (access.machine_type.representation()) {
1214 // TODO(turbofan): Add support for doing the truncations.
1215 break;
1223 state = state->AddElement(object, index, new_value,
1224 access.machine_type.representation(), zone());
1225 break;
1226 }
1227 return UpdateState(node, state);
1228}
1229
1231 Node* const effect = NodeProperties::GetEffectInput(node);
1232 AbstractState const* state = node_states_.Get(effect);
1233 if (state == nullptr) return NoChange();
1234 return UpdateState(node, state);
1235}
1236
1238 AbstractState const* state, Node* effect_phi, Node* phi) {
1239 int predecessor_count = phi->InputCount() - 1;
1240 // TODO(jarin) Consider doing a union here. At the moment, we just keep this
1241 // consistent with AbstractState::Merge.
1242
1243 // Check if all the inputs have the same maps.
1244 AbstractState const* input_state =
1246 ZoneRefSet<Map> object_maps;
1247 if (!input_state->LookupMaps(phi->InputAt(0), &object_maps)) return state;
1248 for (int i = 1; i < predecessor_count; i++) {
1249 input_state =
1251 ZoneRefSet<Map> input_maps;
1252 if (!input_state->LookupMaps(phi->InputAt(i), &input_maps)) return state;
1253 if (input_maps != object_maps) return state;
1254 }
1255 return state->SetMaps(phi, object_maps, zone());
1256}
1257
1259 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
1260 Node* const control = NodeProperties::GetControlInput(node);
1261 AbstractState const* state0 = node_states_.Get(effect0);
1262 if (state0 == nullptr) return NoChange();
1263 if (control->opcode() == IrOpcode::kLoop) {
1264 // Here we rely on having only reducible loops:
1265 // The loop entry edge always dominates the header, so we can just take
1266 // the state from the first input, and compute the loop state based on it.
1267 AbstractState const* state = ComputeLoopState(node, state0);
1268 return UpdateState(node, state);
1269 }
1270 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
1271
1272 // Shortcut for the case when we do not know anything about some input.
1273 int const input_count = node->op()->EffectInputCount();
1274 for (int i = 1; i < input_count; ++i) {
1275 Node* const effect = NodeProperties::GetEffectInput(node, i);
1276 if (node_states_.Get(effect) == nullptr) return NoChange();
1277 }
1278
1279 // Make a copy of the first input's state and merge with the state
1280 // from other inputs.
1281 AbstractState* state = zone()->New<AbstractState>(*state0);
1282 for (int i = 1; i < input_count; ++i) {
1283 Node* const input = NodeProperties::GetEffectInput(node, i);
1284 state->Merge(node_states_.Get(input), zone());
1285 }
1286
1287 // For each phi, try to compute the new state for the phi from
1288 // the inputs.
1289 AbstractState const* state_with_phis = state;
1290 for (Node* use : control->uses()) {
1291 if (use->opcode() == IrOpcode::kPhi) {
1292 state_with_phis = UpdateStateForPhi(state_with_phis, node, use);
1293 }
1294 }
1295
1296 return UpdateState(node, state_with_phis);
1297}
1298
1302
1304 if (node->op()->EffectInputCount() == 1) {
1305 if (node->op()->EffectOutputCount() == 1) {
1306 Node* const effect = NodeProperties::GetEffectInput(node);
1307 AbstractState const* state = node_states_.Get(effect);
1308 // If we do not know anything about the predecessor, do not propagate
1309 // just yet because we will have to recompute anyway once we compute
1310 // the predecessor.
1311 if (state == nullptr) return NoChange();
1312 // Check if this {node} has some uncontrolled side effects.
1313 if (!node->op()->HasProperty(Operator::kNoWrite)) {
1314 state = state->KillAll(zone());
1315 }
1316 return UpdateState(node, state);
1317 } else {
1318 // Effect terminators should be handled specially.
1319 return NoChange();
1320 }
1321 }
1322 DCHECK_EQ(0, node->op()->EffectInputCount());
1323 DCHECK_EQ(0, node->op()->EffectOutputCount());
1324 return NoChange();
1325}
1326
1328 AbstractState const* original = node_states_.Get(node);
1329 // Only signal that the {node} has Changed, if the information about {state}
1330 // has changed wrt. the {original}.
1331 if (state != original) {
1332 if (original == nullptr || !state->Equals(original)) {
1333 node_states_.Set(node, state);
1334 return Changed(node);
1335 }
1336 }
1337 return NoChange();
1338}
1339
1342 Node* current, LoadElimination::AbstractState const* state,
1343 FieldAccess const& access) const {
1344 Node* const object = NodeProperties::GetValueInput(current, 0);
1345 if (access.offset == HeapObject::kMapOffset) {
1346 // Invalidate what we know about the {object}s map.
1347 state = state->KillMaps(object, zone());
1348 } else {
1349 IndexRange field_index = FieldIndexOf(access);
1350 if (field_index == IndexRange::Invalid()) {
1351 state = state->KillFields(object, access.name, zone());
1352 } else {
1353 state = state->KillField(object, field_index, access.name, zone());
1354 }
1355 }
1356 return state;
1357}
1358
1360 Node* node, AbstractState const* state) const {
1361 Node* const control = NodeProperties::GetControlInput(node);
1362 struct TransitionElementsKindInfo {
1363 ElementsTransition transition;
1364 Node* object;
1365 };
1366 // Allocate zone data structures in a temporary zone with a lifetime limited
1367 // to this function to avoid blowing up the size of the stage-global zone.
1368 Zone temp_zone(zone()->allocator(), "Temporary scoped zone");
1369 ZoneVector<TransitionElementsKindInfo> element_transitions_(&temp_zone);
1370 ZoneQueue<Node*> queue(&temp_zone);
1371 ZoneSet<Node*> visited(&temp_zone);
1372 visited.insert(node);
1373 for (int i = 1; i < control->InputCount(); ++i) {
1374 queue.push(node->InputAt(i));
1375 }
1376 while (!queue.empty()) {
1377 Node* const current = queue.front();
1378 queue.pop();
1379 if (visited.find(current) == visited.end()) {
1380 visited.insert(current);
1381 if (!current->op()->HasProperty(Operator::kNoWrite)) {
1382 switch (current->opcode()) {
1383 case IrOpcode::kEnsureWritableFastElements: {
1384 Node* const object = NodeProperties::GetValueInput(current, 0);
1385 state = state->KillField(
1386 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1387 MaybeHandle<Name>(), zone());
1388 break;
1389 }
1390 case IrOpcode::kMaybeGrowFastElements: {
1391 Node* const object = NodeProperties::GetValueInput(current, 0);
1392 state = state->KillField(
1393 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1394 MaybeHandle<Name>(), zone());
1395 break;
1396 }
1397 case IrOpcode::kTransitionElementsKind: {
1398 ElementsTransition transition = ElementsTransitionOf(current->op());
1399 Node* const object = NodeProperties::GetValueInput(current, 0);
1400 ZoneRefSet<Map> object_maps;
1401 if (!state->LookupMaps(object, &object_maps) ||
1402 !ZoneRefSet<Map>(transition.target()).contains(object_maps)) {
1403 element_transitions_.push_back({transition, object});
1404 }
1405 break;
1406 }
1407 case IrOpcode::kTransitionElementsKindOrCheckMap: {
1410 Node* const object = NodeProperties::GetValueInput(current, 0);
1411 ZoneRefSet<Map> object_maps;
1412 MapRef target = transition.target();
1413 if (!state->LookupMaps(object, &object_maps) ||
1414 !ZoneRefSet<Map>(target).contains(object_maps)) {
1415 for (MapRef source : transition.sources()) {
1417 IsSimpleMapChangeTransition(source.elements_kind(),
1418 target.elements_kind())
1421 element_transitions_.push_back(
1422 {ElementsTransition(mode, source, target), object});
1423 }
1424 }
1425 break;
1426 }
1427 case IrOpcode::kTransitionAndStoreElement: {
1428 Node* const object = NodeProperties::GetValueInput(current, 0);
1429 // Invalidate what we know about the {object}s map.
1430 state = state->KillMaps(object, zone());
1431 // Kill the elements as well.
1432 state = state->KillField(
1433 object, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1434 MaybeHandle<Name>(), zone());
1435 break;
1436 }
1437 case IrOpcode::kStoreField: {
1438 FieldAccess access = FieldAccessOf(current->op());
1439 state = ComputeLoopStateForStoreField(current, state, access);
1440 break;
1441 }
1442 case IrOpcode::kStoreElement: {
1443 Node* const object = NodeProperties::GetValueInput(current, 0);
1444 Node* const index = NodeProperties::GetValueInput(current, 1);
1445 state = state->KillElement(object, index, zone());
1446 break;
1447 }
1448 case IrOpcode::kCheckMaps:
1449 case IrOpcode::kStoreTypedElement: {
1450 // Doesn't affect anything we track with the state currently.
1451 break;
1452 }
1453 default:
1454 return state->KillAll(zone());
1455 }
1456 }
1457 for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
1458 queue.push(NodeProperties::GetEffectInput(current, i));
1459 }
1460 }
1461 }
1462
1463 // Finally, we apply the element transitions. For each transition, we will try
1464 // to only invalidate information about nodes that can have the transition's
1465 // source map. The trouble is that an object can be transitioned by some other
1466 // transition to the source map. In that case, the other transition will
1467 // invalidate the information, so we are mostly fine.
1468 //
1469 // The only bad case is
1470 //
1471 // mapA ---fast---> mapB ---slow---> mapC
1472 //
1473 // If we process the slow transition first on an object that has mapA, we will
1474 // ignore the transition because the object does not have its source map
1475 // (mapB). When we later process the fast transition, we invalidate the
1476 // object's map, but we keep the information about the object's elements. This
1477 // is wrong because the elements will be overwritten by the slow transition.
1478 //
1479 // Note that the slow-slow case is fine because either of the slow transition
1480 // will invalidate the elements field, so the processing order does not
1481 // matter.
1482 //
1483 // To handle the bad case properly, we first kill the maps using all
1484 // transitions. We kill the the fields later when all the transitions are
1485 // already reflected in the map information.
1486
1487 for (const TransitionElementsKindInfo& t : element_transitions_) {
1488 AliasStateInfo alias_info(state, t.object, t.transition.source());
1489 state = state->KillMaps(alias_info, zone());
1490 }
1491 for (const TransitionElementsKindInfo& t : element_transitions_) {
1492 switch (t.transition.mode()) {
1494 break;
1496 AliasStateInfo alias_info(state, t.object, t.transition.source());
1497 state = state->KillField(
1498 alias_info, FieldIndexOf(JSObject::kElementsOffset, kTaggedSize),
1499 MaybeHandle<Name>(), zone());
1500 break;
1501 }
1502 }
1503 }
1504 return state;
1505}
1506
1507// static
1509 int offset, int representation_size) {
1511 int field_index = offset / kTaggedSize - 1;
1512 DCHECK_EQ(0, representation_size % kTaggedSize);
1513 return IndexRange(field_index, representation_size / kTaggedSize);
1514}
1515
1516// static
1518 FieldAccess const& access) {
1519 MachineRepresentation rep = access.machine_type.representation();
1520 switch (rep) {
1526 UNREACHABLE();
1531 // Currently untracked.
1532 return IndexRange::Invalid();
1545 break;
1546 }
1547 int representation_size = ElementSizeInBytes(rep);
1548 // We currently only track fields that are at least tagged pointer sized.
1549 // We assume that indirect pointers are tagged pointer sized if we see them
1550 // here since they should only ever be used in pointer compression
1551 // configurations.
1553 representation_size == kTaggedSize);
1554 if (representation_size < kTaggedSize) return IndexRange::Invalid();
1555 DCHECK_EQ(0, representation_size % kTaggedSize);
1556
1557 if (access.base_is_tagged != kTaggedBase) {
1558 // We currently only track tagged objects.
1559 return IndexRange::Invalid();
1560 }
1561 return FieldIndexOf(access.offset, representation_size);
1562}
1563
1567
1569
1571
1573
1574} // namespace compiler
1575} // namespace internal
1576} // namespace v8
static constexpr int kMapOffset
void remove(T handle, Zone *zone)
void insert(T handle, Zone *zone)
bool contains(ZoneCompactSet< T > const &other) const
void push_back(const T &value)
T * New(Args &&... args)
Definition zone.h:114
Node * HeapConstantNoHole(Handle< HeapObject > value)
Definition js-graph.cc:146
Isolate * isolate() const
Definition js-graph.h:106
Factory * factory() const
Definition js-graph.h:107
Node * Lookup(Node *object, Node *index, MachineRepresentation representation) const
AbstractElements const * Merge(AbstractElements const *that, Zone *zone) const
AbstractElements const * Kill(Node *object, Node *index, Zone *zone) const
AbstractField const * KillConst(Node *object, Zone *zone) const
AbstractField const * Kill(const AliasStateInfo &alias_info, MaybeHandle< Name > name, Zone *zone) const
AbstractField const * Merge(AbstractField const *that, Zone *zone, int *count) const
bool Lookup(Node *object, ZoneRefSet< Map > *object_maps) const
AbstractMaps const * Merge(AbstractMaps const *that, Zone *zone) const
AbstractMaps const * Kill(const AliasStateInfo &alias_info, Zone *zone) const
ZoneMap< Node *, ZoneRefSet< Map > > info_for_node_
AbstractMaps const * Extend(Node *object, ZoneRefSet< Map > maps, Zone *zone) const
AbstractState const * KillMaps(Node *object, Zone *zone) const
AbstractState const * KillAll(Zone *zone) const
FieldInfo const * LookupField(Node *object, IndexRange index, ConstFieldInfo const_field_info) const
void FieldsMerge(AbstractFields *this_fields, AbstractFields const &that_fields, Zone *zone)
bool LookupMaps(Node *object, ZoneRefSet< Map > *object_maps) const
AbstractState const * KillField(const AliasStateInfo &alias_info, IndexRange index, MaybeHandle< Name > name, Zone *zone) const
AbstractState const * AddField(Node *object, IndexRange index, FieldInfo info, Zone *zone) const
std::array< AbstractField const *, kMaxTrackedFieldsPerObject > AbstractFields
AbstractState const * KillFields(Node *object, MaybeHandle< Name > name, Zone *zone) const
AbstractState const * KillConstField(Node *object, IndexRange index_range, Zone *zone) const
void Merge(AbstractState const *that, Zone *zone)
AbstractState const * KillElement(Node *object, Node *index, Zone *zone) const
AbstractState const * AddElement(Node *object, Node *index, Node *value, MachineRepresentation representation, Zone *zone) const
AbstractState const * SetMaps(Node *object, ZoneRefSet< Map > maps, Zone *zone) const
Node * LookupElement(Node *object, Node *index, MachineRepresentation representation) const
bool FieldsEquals(AbstractFields const &this_fields, AbstractFields const &that_fields) const
AliasStateInfo(const AbstractState *state, Node *object)
AliasStateInfo(const AbstractState *state, Node *object, MapRef map)
AbstractStateForEffectNodes node_states_
AbstractState const * ComputeLoopStateForStoreField(Node *current, LoadElimination::AbstractState const *state, FieldAccess const &access) const
static IndexRange FieldIndexOf(int offset, int representation_size)
Reduction ReduceTransitionElementsKindOrCheckMap(Node *node)
CommonOperatorBuilder * common() const
Reduction UpdateState(Node *node, AbstractState const *state)
AbstractState const * UpdateStateForPhi(AbstractState const *state, Node *effect_phi, Node *phi)
Reduction ReduceStoreField(Node *node, FieldAccess const &access)
Reduction ReduceTransitionAndStoreElement(Node *node)
AbstractState const * ComputeLoopState(Node *node, AbstractState const *state) const
Reduction ReduceEnsureWritableFastElements(Node *node)
static AbstractState const * empty_state()
Reduction ReduceLoadField(Node *node, FieldAccess const &access)
CommonOperatorBuilder * common() const
ElementsKind elements_kind() const
static Type GetType(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static void SetType(Node *node, Type type)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
int InputCount() const
Definition node.h:59
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
bool Maybe(Type that) const
const HeapConstantType * AsHeapConstant() const
static Type Intersect(Type type1, Type type2, Zone *zone)
bool Is(Type that) const
Register const object_
const MapRef map_
Handle< SharedFunctionInfo > info
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
#define V8_ENABLE_DOUBLE_CONST_STORE_CHECK_BOOL
int32_t offset
Handle< FixedArray > elements_
Definition isolate.cc:1119
Node * node
ZoneVector< RpoNumber > & result
LiftoffAssembler::CacheState state
int x
const GrowFastElementsParameters & GrowFastElementsParametersOf(const Operator *op)
CheckMapsParameters const & CheckMapsParametersOf(Operator const *op)
ZoneRefSet< Map > const & MapGuardMapsOf(Operator const *op)
const FieldAccess & FieldAccessOf(const Operator *op)
const ElementAccess & ElementAccessOf(const Operator *op)
ZoneRefSet< Map > const & CompareMapsParametersOf(Operator const *op)
MapRef FastMapParameterOf(const Operator *op)
ElementsTransitionWithMultipleSources const & ElementsTransitionWithMultipleSourcesOf(const Operator *op)
ElementsTransition const & ElementsTransitionOf(const Operator *op)
MapRef DoubleMapParameterOf(const Operator *op)
constexpr int kTaggedSize
Definition globals.h:542
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
void PrintF(const char *format,...)
Definition utils.cc:39
constexpr bool IsAnyTagged(MachineRepresentation rep)
bool IsSimpleMapChangeTransition(ElementsKind from_kind, ElementsKind to_kind)
const char * MachineReprToString(MachineRepresentation rep)
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
V8_EXPORT_PRIVATE constexpr int ElementSizeInBytes(MachineRepresentation)
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
#define arraysize(array)
Definition macros.h:67