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