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
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15
16 namespace {
17
18 enum Aliasing { kNoAlias, kMayAlias, kMustAlias };
19
QueryAlias(Node * a,Node * b)20 Aliasing QueryAlias(Node* a, Node* b) {
21 if (a == b) return kMustAlias;
22 if (!NodeProperties::GetType(a)->Maybe(NodeProperties::GetType(b))) {
23 return kNoAlias;
24 }
25 switch (b->opcode()) {
26 case IrOpcode::kAllocate: {
27 switch (a->opcode()) {
28 case IrOpcode::kAllocate:
29 case IrOpcode::kHeapConstant:
30 case IrOpcode::kParameter:
31 return kNoAlias;
32 default:
33 break;
34 }
35 break;
36 }
37 case IrOpcode::kFinishRegion:
38 return QueryAlias(a, b->InputAt(0));
39 default:
40 break;
41 }
42 switch (a->opcode()) {
43 case IrOpcode::kAllocate: {
44 switch (b->opcode()) {
45 case IrOpcode::kHeapConstant:
46 case IrOpcode::kParameter:
47 return kNoAlias;
48 default:
49 break;
50 }
51 break;
52 }
53 case IrOpcode::kFinishRegion:
54 return QueryAlias(a->InputAt(0), b);
55 default:
56 break;
57 }
58 return kMayAlias;
59 }
60
MayAlias(Node * a,Node * b)61 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; }
62
MustAlias(Node * a,Node * b)63 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
64
65 } // namespace
66
Reduce(Node * node)67 Reduction LoadElimination::Reduce(Node* node) {
68 if (FLAG_trace_turbo_load_elimination) {
69 if (node->op()->EffectInputCount() > 0) {
70 PrintF(" visit #%d:%s", node->id(), node->op()->mnemonic());
71 if (node->op()->ValueInputCount() > 0) {
72 PrintF("(");
73 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
74 if (i > 0) PrintF(", ");
75 Node* const value = NodeProperties::GetValueInput(node, i);
76 PrintF("#%d:%s", value->id(), value->op()->mnemonic());
77 }
78 PrintF(")");
79 }
80 PrintF("\n");
81 for (int i = 0; i < node->op()->EffectInputCount(); ++i) {
82 Node* const effect = NodeProperties::GetEffectInput(node, i);
83 if (AbstractState const* const state = node_states_.Get(effect)) {
84 PrintF(" state[%i]: #%d:%s\n", i, effect->id(),
85 effect->op()->mnemonic());
86 state->Print();
87 } else {
88 PrintF(" no state[%i]: #%d:%s\n", i, effect->id(),
89 effect->op()->mnemonic());
90 }
91 }
92 }
93 }
94 switch (node->opcode()) {
95 case IrOpcode::kArrayBufferWasNeutered:
96 return ReduceArrayBufferWasNeutered(node);
97 case IrOpcode::kCheckMaps:
98 return ReduceCheckMaps(node);
99 case IrOpcode::kEnsureWritableFastElements:
100 return ReduceEnsureWritableFastElements(node);
101 case IrOpcode::kMaybeGrowFastElements:
102 return ReduceMaybeGrowFastElements(node);
103 case IrOpcode::kTransitionElementsKind:
104 return ReduceTransitionElementsKind(node);
105 case IrOpcode::kLoadField:
106 return ReduceLoadField(node);
107 case IrOpcode::kStoreField:
108 return ReduceStoreField(node);
109 case IrOpcode::kLoadElement:
110 return ReduceLoadElement(node);
111 case IrOpcode::kStoreElement:
112 return ReduceStoreElement(node);
113 case IrOpcode::kStoreTypedElement:
114 return ReduceStoreTypedElement(node);
115 case IrOpcode::kEffectPhi:
116 return ReduceEffectPhi(node);
117 case IrOpcode::kDead:
118 break;
119 case IrOpcode::kStart:
120 return ReduceStart(node);
121 default:
122 return ReduceOtherNode(node);
123 }
124 return NoChange();
125 }
126
127 namespace {
128
IsCompatibleCheck(Node const * a,Node const * b)129 bool IsCompatibleCheck(Node const* a, Node const* b) {
130 if (a->op() != b->op()) return false;
131 for (int i = a->op()->ValueInputCount(); --i >= 0;) {
132 if (!MustAlias(a->InputAt(i), b->InputAt(i))) return false;
133 }
134 return true;
135 }
136
137 } // namespace
138
Lookup(Node * node) const139 Node* LoadElimination::AbstractChecks::Lookup(Node* node) const {
140 for (Node* const check : nodes_) {
141 if (check && IsCompatibleCheck(check, node)) {
142 return check;
143 }
144 }
145 return nullptr;
146 }
147
Equals(AbstractChecks const * that) const148 bool LoadElimination::AbstractChecks::Equals(AbstractChecks const* that) const {
149 if (this == that) return true;
150 for (size_t i = 0; i < arraysize(nodes_); ++i) {
151 if (Node* this_node = this->nodes_[i]) {
152 for (size_t j = 0;; ++j) {
153 if (j == arraysize(nodes_)) return false;
154 if (that->nodes_[j] == this_node) break;
155 }
156 }
157 }
158 for (size_t i = 0; i < arraysize(nodes_); ++i) {
159 if (Node* that_node = that->nodes_[i]) {
160 for (size_t j = 0;; ++j) {
161 if (j == arraysize(nodes_)) return false;
162 if (this->nodes_[j] == that_node) break;
163 }
164 }
165 }
166 return true;
167 }
168
Merge(AbstractChecks const * that,Zone * zone) const169 LoadElimination::AbstractChecks const* LoadElimination::AbstractChecks::Merge(
170 AbstractChecks const* that, Zone* zone) const {
171 if (this->Equals(that)) return this;
172 AbstractChecks* copy = new (zone) AbstractChecks(zone);
173 for (Node* const this_node : this->nodes_) {
174 if (this_node == nullptr) continue;
175 for (Node* const that_node : that->nodes_) {
176 if (this_node == that_node) {
177 copy->nodes_[copy->next_index_++] = this_node;
178 break;
179 }
180 }
181 }
182 copy->next_index_ %= arraysize(nodes_);
183 return copy;
184 }
185
Print() const186 void LoadElimination::AbstractChecks::Print() const {
187 for (Node* const node : nodes_) {
188 if (node != nullptr) {
189 PrintF(" #%d:%s\n", node->id(), node->op()->mnemonic());
190 }
191 }
192 }
193
Lookup(Node * object,Node * index) const194 Node* LoadElimination::AbstractElements::Lookup(Node* object,
195 Node* index) const {
196 for (Element const element : elements_) {
197 if (element.object == nullptr) continue;
198 DCHECK_NOT_NULL(element.index);
199 DCHECK_NOT_NULL(element.value);
200 if (MustAlias(object, element.object) && MustAlias(index, element.index)) {
201 return element.value;
202 }
203 }
204 return nullptr;
205 }
206
207 LoadElimination::AbstractElements const*
Kill(Node * object,Node * index,Zone * zone) const208 LoadElimination::AbstractElements::Kill(Node* object, Node* index,
209 Zone* zone) const {
210 for (Element const element : this->elements_) {
211 if (element.object == nullptr) continue;
212 if (MayAlias(object, element.object)) {
213 AbstractElements* that = new (zone) AbstractElements(zone);
214 for (Element const element : this->elements_) {
215 if (element.object == nullptr) continue;
216 DCHECK_NOT_NULL(element.index);
217 DCHECK_NOT_NULL(element.value);
218 if (!MayAlias(object, element.object) ||
219 !NodeProperties::GetType(index)->Maybe(
220 NodeProperties::GetType(element.index))) {
221 that->elements_[that->next_index_++] = element;
222 }
223 }
224 that->next_index_ %= arraysize(elements_);
225 return that;
226 }
227 }
228 return this;
229 }
230
Equals(AbstractElements const * that) const231 bool LoadElimination::AbstractElements::Equals(
232 AbstractElements const* that) const {
233 if (this == that) return true;
234 for (size_t i = 0; i < arraysize(elements_); ++i) {
235 Element this_element = this->elements_[i];
236 if (this_element.object == nullptr) continue;
237 for (size_t j = 0;; ++j) {
238 if (j == arraysize(elements_)) return false;
239 Element that_element = that->elements_[j];
240 if (this_element.object == that_element.object &&
241 this_element.index == that_element.index &&
242 this_element.value == that_element.value) {
243 break;
244 }
245 }
246 }
247 for (size_t i = 0; i < arraysize(elements_); ++i) {
248 Element that_element = that->elements_[i];
249 if (that_element.object == nullptr) continue;
250 for (size_t j = 0;; ++j) {
251 if (j == arraysize(elements_)) return false;
252 Element this_element = this->elements_[j];
253 if (that_element.object == this_element.object &&
254 that_element.index == this_element.index &&
255 that_element.value == this_element.value) {
256 break;
257 }
258 }
259 }
260 return true;
261 }
262
263 LoadElimination::AbstractElements const*
Merge(AbstractElements const * that,Zone * zone) const264 LoadElimination::AbstractElements::Merge(AbstractElements const* that,
265 Zone* zone) const {
266 if (this->Equals(that)) return this;
267 AbstractElements* copy = new (zone) AbstractElements(zone);
268 for (Element const this_element : this->elements_) {
269 if (this_element.object == nullptr) continue;
270 for (Element const that_element : that->elements_) {
271 if (this_element.object == that_element.object &&
272 this_element.index == that_element.index &&
273 this_element.value == that_element.value) {
274 copy->elements_[copy->next_index_++] = this_element;
275 break;
276 }
277 }
278 }
279 copy->next_index_ %= arraysize(elements_);
280 return copy;
281 }
282
Print() const283 void LoadElimination::AbstractElements::Print() const {
284 for (Element const& element : elements_) {
285 if (element.object) {
286 PrintF(" #%d:%s @ #%d:%s -> #%d:%s\n", element.object->id(),
287 element.object->op()->mnemonic(), element.index->id(),
288 element.index->op()->mnemonic(), element.value->id(),
289 element.value->op()->mnemonic());
290 }
291 }
292 }
293
Lookup(Node * object) const294 Node* LoadElimination::AbstractField::Lookup(Node* object) const {
295 for (auto pair : info_for_node_) {
296 if (MustAlias(object, pair.first)) return pair.second;
297 }
298 return nullptr;
299 }
300
Kill(Node * object,Zone * zone) const301 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill(
302 Node* object, Zone* zone) const {
303 for (auto pair : this->info_for_node_) {
304 if (MayAlias(object, pair.first)) {
305 AbstractField* that = new (zone) AbstractField(zone);
306 for (auto pair : this->info_for_node_) {
307 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
308 }
309 return that;
310 }
311 }
312 return this;
313 }
314
Print() const315 void LoadElimination::AbstractField::Print() const {
316 for (auto pair : info_for_node_) {
317 PrintF(" #%d:%s -> #%d:%s\n", pair.first->id(),
318 pair.first->op()->mnemonic(), pair.second->id(),
319 pair.second->op()->mnemonic());
320 }
321 }
322
Equals(AbstractState const * that) const323 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
324 if (this->checks_) {
325 if (!that->checks_ || !that->checks_->Equals(this->checks_)) {
326 return false;
327 }
328 } else if (that->checks_) {
329 return false;
330 }
331 if (this->elements_) {
332 if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
333 return false;
334 }
335 } else if (that->elements_) {
336 return false;
337 }
338 for (size_t i = 0u; i < arraysize(fields_); ++i) {
339 AbstractField const* this_field = this->fields_[i];
340 AbstractField const* that_field = that->fields_[i];
341 if (this_field) {
342 if (!that_field || !that_field->Equals(this_field)) return false;
343 } else if (that_field) {
344 return false;
345 }
346 }
347 return true;
348 }
349
Merge(AbstractState const * that,Zone * zone)350 void LoadElimination::AbstractState::Merge(AbstractState const* that,
351 Zone* zone) {
352 // Merge the information we have about the checks.
353 if (this->checks_) {
354 this->checks_ =
355 that->checks_ ? that->checks_->Merge(this->checks_, zone) : nullptr;
356 }
357
358 // Merge the information we have about the elements.
359 if (this->elements_) {
360 this->elements_ = that->elements_
361 ? that->elements_->Merge(this->elements_, zone)
362 : nullptr;
363 }
364
365 // Merge the information we have about the fields.
366 for (size_t i = 0; i < arraysize(fields_); ++i) {
367 if (this->fields_[i]) {
368 if (that->fields_[i]) {
369 this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone);
370 } else {
371 this->fields_[i] = nullptr;
372 }
373 }
374 }
375 }
376
LookupCheck(Node * node) const377 Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
378 return this->checks_ ? this->checks_->Lookup(node) : nullptr;
379 }
380
AddCheck(Node * node,Zone * zone) const381 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck(
382 Node* node, Zone* zone) const {
383 AbstractState* that = new (zone) AbstractState(*this);
384 if (that->checks_) {
385 that->checks_ = that->checks_->Extend(node, zone);
386 } else {
387 that->checks_ = new (zone) AbstractChecks(node, zone);
388 }
389 return that;
390 }
391
LookupElement(Node * object,Node * index) const392 Node* LoadElimination::AbstractState::LookupElement(Node* object,
393 Node* index) const {
394 if (this->elements_) {
395 return this->elements_->Lookup(object, index);
396 }
397 return nullptr;
398 }
399
400 LoadElimination::AbstractState const*
AddElement(Node * object,Node * index,Node * value,Zone * zone) const401 LoadElimination::AbstractState::AddElement(Node* object, Node* index,
402 Node* value, Zone* zone) const {
403 AbstractState* that = new (zone) AbstractState(*this);
404 if (that->elements_) {
405 that->elements_ = that->elements_->Extend(object, index, value, zone);
406 } else {
407 that->elements_ = new (zone) AbstractElements(object, index, value, zone);
408 }
409 return that;
410 }
411
412 LoadElimination::AbstractState const*
KillElement(Node * object,Node * index,Zone * zone) const413 LoadElimination::AbstractState::KillElement(Node* object, Node* index,
414 Zone* zone) const {
415 if (this->elements_) {
416 AbstractElements const* that_elements =
417 this->elements_->Kill(object, index, zone);
418 if (this->elements_ != that_elements) {
419 AbstractState* that = new (zone) AbstractState(*this);
420 that->elements_ = that_elements;
421 return that;
422 }
423 }
424 return this;
425 }
426
AddField(Node * object,size_t index,Node * value,Zone * zone) const427 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
428 Node* object, size_t index, Node* value, Zone* zone) const {
429 AbstractState* that = new (zone) AbstractState(*this);
430 if (that->fields_[index]) {
431 that->fields_[index] = that->fields_[index]->Extend(object, value, zone);
432 } else {
433 that->fields_[index] = new (zone) AbstractField(object, value, zone);
434 }
435 return that;
436 }
437
KillField(Node * object,size_t index,Zone * zone) const438 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField(
439 Node* object, size_t index, Zone* zone) const {
440 if (AbstractField const* this_field = this->fields_[index]) {
441 this_field = this_field->Kill(object, zone);
442 if (this->fields_[index] != this_field) {
443 AbstractState* that = new (zone) AbstractState(*this);
444 that->fields_[index] = this_field;
445 return that;
446 }
447 }
448 return this;
449 }
450
451 LoadElimination::AbstractState const*
KillFields(Node * object,Zone * zone) const452 LoadElimination::AbstractState::KillFields(Node* object, Zone* zone) const {
453 for (size_t i = 0;; ++i) {
454 if (i == arraysize(fields_)) return this;
455 if (AbstractField const* this_field = this->fields_[i]) {
456 AbstractField const* that_field = this_field->Kill(object, zone);
457 if (that_field != this_field) {
458 AbstractState* that = new (zone) AbstractState(*this);
459 that->fields_[i] = this_field;
460 while (++i < arraysize(fields_)) {
461 if (this->fields_[i] != nullptr) {
462 that->fields_[i] = this->fields_[i]->Kill(object, zone);
463 }
464 }
465 return that;
466 }
467 }
468 }
469 }
470
LookupField(Node * object,size_t index) const471 Node* LoadElimination::AbstractState::LookupField(Node* object,
472 size_t index) const {
473 if (AbstractField const* this_field = this->fields_[index]) {
474 return this_field->Lookup(object);
475 }
476 return nullptr;
477 }
478
Print() const479 void LoadElimination::AbstractState::Print() const {
480 if (checks_) {
481 PrintF(" checks:\n");
482 checks_->Print();
483 }
484 if (elements_) {
485 PrintF(" elements:\n");
486 elements_->Print();
487 }
488 for (size_t i = 0; i < arraysize(fields_); ++i) {
489 if (AbstractField const* const field = fields_[i]) {
490 PrintF(" field %zu:\n", i);
491 field->Print();
492 }
493 }
494 }
495
496 LoadElimination::AbstractState const*
Get(Node * node) const497 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
498 size_t const id = node->id();
499 if (id < info_for_node_.size()) return info_for_node_[id];
500 return nullptr;
501 }
502
Set(Node * node,AbstractState const * state)503 void LoadElimination::AbstractStateForEffectNodes::Set(
504 Node* node, AbstractState const* state) {
505 size_t const id = node->id();
506 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
507 info_for_node_[id] = state;
508 }
509
ReduceArrayBufferWasNeutered(Node * node)510 Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
511 Node* const effect = NodeProperties::GetEffectInput(node);
512 AbstractState const* state = node_states_.Get(effect);
513 if (state == nullptr) return NoChange();
514 if (Node* const check = state->LookupCheck(node)) {
515 ReplaceWithValue(node, check, effect);
516 return Replace(check);
517 }
518 state = state->AddCheck(node, zone());
519 return UpdateState(node, state);
520 }
521
ReduceCheckMaps(Node * node)522 Reduction LoadElimination::ReduceCheckMaps(Node* node) {
523 Node* const object = NodeProperties::GetValueInput(node, 0);
524 Node* const effect = NodeProperties::GetEffectInput(node);
525 AbstractState const* state = node_states_.Get(effect);
526 if (state == nullptr) return NoChange();
527 int const map_input_count = node->op()->ValueInputCount() - 1;
528 if (Node* const object_map =
529 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
530 for (int i = 0; i < map_input_count; ++i) {
531 Node* map = NodeProperties::GetValueInput(node, 1 + i);
532 if (map == object_map) return Replace(effect);
533 }
534 }
535 if (map_input_count == 1) {
536 Node* const map0 = NodeProperties::GetValueInput(node, 1);
537 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), map0,
538 zone());
539 }
540 return UpdateState(node, state);
541 }
542
ReduceEnsureWritableFastElements(Node * node)543 Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
544 Node* const object = NodeProperties::GetValueInput(node, 0);
545 Node* const elements = NodeProperties::GetValueInput(node, 1);
546 Node* const effect = NodeProperties::GetEffectInput(node);
547 AbstractState const* state = node_states_.Get(effect);
548 if (state == nullptr) return NoChange();
549 Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
550 if (Node* const elements_map =
551 state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) {
552 // Check if the {elements} already have the fixed array map.
553 if (elements_map == fixed_array_map) {
554 ReplaceWithValue(node, elements, effect);
555 return Replace(elements);
556 }
557 }
558 // We know that the resulting elements have the fixed array map.
559 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
560 fixed_array_map, zone());
561 // Kill the previous elements on {object}.
562 state =
563 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
564 // Add the new elements on {object}.
565 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
566 zone());
567 return UpdateState(node, state);
568 }
569
ReduceMaybeGrowFastElements(Node * node)570 Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
571 GrowFastElementsFlags flags = GrowFastElementsFlagsOf(node->op());
572 Node* const object = NodeProperties::GetValueInput(node, 0);
573 Node* const effect = NodeProperties::GetEffectInput(node);
574 AbstractState const* state = node_states_.Get(effect);
575 if (state == nullptr) return NoChange();
576 if (flags & GrowFastElementsFlag::kDoubleElements) {
577 // We know that the resulting elements have the fixed double array map.
578 Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant();
579 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
580 fixed_double_array_map, zone());
581 } else {
582 // We know that the resulting elements have the fixed array map.
583 Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
584 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
585 fixed_array_map, zone());
586 }
587 if (flags & GrowFastElementsFlag::kArrayObject) {
588 // Kill the previous Array::length on {object}.
589 state =
590 state->KillField(object, FieldIndexOf(JSArray::kLengthOffset), zone());
591 }
592 // Kill the previous elements on {object}.
593 state =
594 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
595 // Add the new elements on {object}.
596 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
597 zone());
598 return UpdateState(node, state);
599 }
600
ReduceTransitionElementsKind(Node * node)601 Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
602 Node* const object = NodeProperties::GetValueInput(node, 0);
603 Node* const source_map = NodeProperties::GetValueInput(node, 1);
604 Node* const target_map = NodeProperties::GetValueInput(node, 2);
605 Node* const effect = NodeProperties::GetEffectInput(node);
606 AbstractState const* state = node_states_.Get(effect);
607 if (state == nullptr) return NoChange();
608 if (Node* const object_map =
609 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
610 if (target_map == object_map) {
611 // The {object} already has the {target_map}, so this TransitionElements
612 // {node} is fully redundant (independent of what {source_map} is).
613 return Replace(effect);
614 }
615 state =
616 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
617 if (source_map == object_map) {
618 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset),
619 target_map, zone());
620 }
621 } else {
622 state =
623 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
624 }
625 ElementsTransition transition = ElementsTransitionOf(node->op());
626 switch (transition) {
627 case ElementsTransition::kFastTransition:
628 break;
629 case ElementsTransition::kSlowTransition:
630 // Kill the elements as well.
631 state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
632 zone());
633 break;
634 }
635 return UpdateState(node, state);
636 }
637
ReduceLoadField(Node * node)638 Reduction LoadElimination::ReduceLoadField(Node* node) {
639 FieldAccess const& access = FieldAccessOf(node->op());
640 Node* const object = NodeProperties::GetValueInput(node, 0);
641 Node* const effect = NodeProperties::GetEffectInput(node);
642 Node* const control = NodeProperties::GetControlInput(node);
643 AbstractState const* state = node_states_.Get(effect);
644 if (state == nullptr) return NoChange();
645 int field_index = FieldIndexOf(access);
646 if (field_index >= 0) {
647 if (Node* replacement = state->LookupField(object, field_index)) {
648 // Make sure we don't resurrect dead {replacement} nodes.
649 if (!replacement->IsDead()) {
650 // We might need to guard the {replacement} if the type of the
651 // {node} is more precise than the type of the {replacement}.
652 Type* const node_type = NodeProperties::GetType(node);
653 if (!NodeProperties::GetType(replacement)->Is(node_type)) {
654 replacement = graph()->NewNode(common()->TypeGuard(node_type),
655 replacement, control);
656 }
657 ReplaceWithValue(node, replacement, effect);
658 return Replace(replacement);
659 }
660 }
661 state = state->AddField(object, field_index, node, zone());
662 }
663 return UpdateState(node, state);
664 }
665
ReduceStoreField(Node * node)666 Reduction LoadElimination::ReduceStoreField(Node* node) {
667 FieldAccess const& access = FieldAccessOf(node->op());
668 Node* const object = NodeProperties::GetValueInput(node, 0);
669 Node* const new_value = NodeProperties::GetValueInput(node, 1);
670 Node* const effect = NodeProperties::GetEffectInput(node);
671 AbstractState const* state = node_states_.Get(effect);
672 if (state == nullptr) return NoChange();
673 int field_index = FieldIndexOf(access);
674 if (field_index >= 0) {
675 Node* const old_value = state->LookupField(object, field_index);
676 if (old_value == new_value) {
677 // This store is fully redundant.
678 return Replace(effect);
679 }
680 // Kill all potentially aliasing fields and record the new value.
681 state = state->KillField(object, field_index, zone());
682 state = state->AddField(object, field_index, new_value, zone());
683 } else {
684 // Unsupported StoreField operator.
685 state = state->KillFields(object, zone());
686 }
687 return UpdateState(node, state);
688 }
689
ReduceLoadElement(Node * node)690 Reduction LoadElimination::ReduceLoadElement(Node* node) {
691 Node* const object = NodeProperties::GetValueInput(node, 0);
692 Node* const index = NodeProperties::GetValueInput(node, 1);
693 Node* const effect = NodeProperties::GetEffectInput(node);
694 Node* const control = NodeProperties::GetControlInput(node);
695 AbstractState const* state = node_states_.Get(effect);
696 if (state == nullptr) return NoChange();
697 if (Node* replacement = state->LookupElement(object, index)) {
698 // Make sure we don't resurrect dead {replacement} nodes.
699 if (!replacement->IsDead()) {
700 // We might need to guard the {replacement} if the type of the
701 // {node} is more precise than the type of the {replacement}.
702 Type* const node_type = NodeProperties::GetType(node);
703 if (!NodeProperties::GetType(replacement)->Is(node_type)) {
704 replacement = graph()->NewNode(common()->TypeGuard(node_type),
705 replacement, control);
706 }
707 ReplaceWithValue(node, replacement, effect);
708 return Replace(replacement);
709 }
710 }
711 state = state->AddElement(object, index, node, zone());
712 return UpdateState(node, state);
713 }
714
ReduceStoreElement(Node * node)715 Reduction LoadElimination::ReduceStoreElement(Node* node) {
716 ElementAccess const& access = ElementAccessOf(node->op());
717 Node* const object = NodeProperties::GetValueInput(node, 0);
718 Node* const index = NodeProperties::GetValueInput(node, 1);
719 Node* const new_value = NodeProperties::GetValueInput(node, 2);
720 Node* const effect = NodeProperties::GetEffectInput(node);
721 AbstractState const* state = node_states_.Get(effect);
722 if (state == nullptr) return NoChange();
723 Node* const old_value = state->LookupElement(object, index);
724 if (old_value == new_value) {
725 // This store is fully redundant.
726 return Replace(effect);
727 }
728 // Kill all potentially aliasing elements.
729 state = state->KillElement(object, index, zone());
730 // Only record the new value if the store doesn't have an implicit truncation.
731 switch (access.machine_type.representation()) {
732 case MachineRepresentation::kNone:
733 case MachineRepresentation::kBit:
734 UNREACHABLE();
735 break;
736 case MachineRepresentation::kWord8:
737 case MachineRepresentation::kWord16:
738 case MachineRepresentation::kWord32:
739 case MachineRepresentation::kWord64:
740 case MachineRepresentation::kFloat32:
741 // TODO(turbofan): Add support for doing the truncations.
742 break;
743 case MachineRepresentation::kFloat64:
744 case MachineRepresentation::kSimd128:
745 case MachineRepresentation::kTaggedSigned:
746 case MachineRepresentation::kTaggedPointer:
747 case MachineRepresentation::kTagged:
748 state = state->AddElement(object, index, new_value, zone());
749 break;
750 }
751 return UpdateState(node, state);
752 }
753
ReduceStoreTypedElement(Node * node)754 Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
755 Node* const effect = NodeProperties::GetEffectInput(node);
756 AbstractState const* state = node_states_.Get(effect);
757 if (state == nullptr) return NoChange();
758 return UpdateState(node, state);
759 }
760
ReduceEffectPhi(Node * node)761 Reduction LoadElimination::ReduceEffectPhi(Node* node) {
762 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
763 Node* const control = NodeProperties::GetControlInput(node);
764 AbstractState const* state0 = node_states_.Get(effect0);
765 if (state0 == nullptr) return NoChange();
766 if (control->opcode() == IrOpcode::kLoop) {
767 // Here we rely on having only reducible loops:
768 // The loop entry edge always dominates the header, so we can just take
769 // the state from the first input, and compute the loop state based on it.
770 AbstractState const* state = ComputeLoopState(node, state0);
771 return UpdateState(node, state);
772 }
773 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
774
775 // Shortcut for the case when we do not know anything about some input.
776 int const input_count = node->op()->EffectInputCount();
777 for (int i = 1; i < input_count; ++i) {
778 Node* const effect = NodeProperties::GetEffectInput(node, i);
779 if (node_states_.Get(effect) == nullptr) return NoChange();
780 }
781
782 // Make a copy of the first input's state and merge with the state
783 // from other inputs.
784 AbstractState* state = new (zone()) AbstractState(*state0);
785 for (int i = 1; i < input_count; ++i) {
786 Node* const input = NodeProperties::GetEffectInput(node, i);
787 state->Merge(node_states_.Get(input), zone());
788 }
789 return UpdateState(node, state);
790 }
791
ReduceStart(Node * node)792 Reduction LoadElimination::ReduceStart(Node* node) {
793 return UpdateState(node, empty_state());
794 }
795
ReduceOtherNode(Node * node)796 Reduction LoadElimination::ReduceOtherNode(Node* node) {
797 if (node->op()->EffectInputCount() == 1) {
798 if (node->op()->EffectOutputCount() == 1) {
799 Node* const effect = NodeProperties::GetEffectInput(node);
800 AbstractState const* state = node_states_.Get(effect);
801 // If we do not know anything about the predecessor, do not propagate
802 // just yet because we will have to recompute anyway once we compute
803 // the predecessor.
804 if (state == nullptr) return NoChange();
805 // Check if this {node} has some uncontrolled side effects.
806 if (!node->op()->HasProperty(Operator::kNoWrite)) {
807 state = empty_state();
808 }
809 return UpdateState(node, state);
810 } else {
811 // Effect terminators should be handled specially.
812 return NoChange();
813 }
814 }
815 DCHECK_EQ(0, node->op()->EffectInputCount());
816 DCHECK_EQ(0, node->op()->EffectOutputCount());
817 return NoChange();
818 }
819
UpdateState(Node * node,AbstractState const * state)820 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
821 AbstractState const* original = node_states_.Get(node);
822 // Only signal that the {node} has Changed, if the information about {state}
823 // has changed wrt. the {original}.
824 if (state != original) {
825 if (original == nullptr || !state->Equals(original)) {
826 node_states_.Set(node, state);
827 return Changed(node);
828 }
829 }
830 return NoChange();
831 }
832
ComputeLoopState(Node * node,AbstractState const * state) const833 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
834 Node* node, AbstractState const* state) const {
835 Node* const control = NodeProperties::GetControlInput(node);
836 ZoneQueue<Node*> queue(zone());
837 ZoneSet<Node*> visited(zone());
838 visited.insert(node);
839 for (int i = 1; i < control->InputCount(); ++i) {
840 queue.push(node->InputAt(i));
841 }
842 while (!queue.empty()) {
843 Node* const current = queue.front();
844 queue.pop();
845 if (visited.find(current) == visited.end()) {
846 visited.insert(current);
847 if (!current->op()->HasProperty(Operator::kNoWrite)) {
848 switch (current->opcode()) {
849 case IrOpcode::kEnsureWritableFastElements: {
850 Node* const object = NodeProperties::GetValueInput(current, 0);
851 state = state->KillField(
852 object, FieldIndexOf(JSObject::kElementsOffset), zone());
853 break;
854 }
855 case IrOpcode::kMaybeGrowFastElements: {
856 GrowFastElementsFlags flags =
857 GrowFastElementsFlagsOf(current->op());
858 Node* const object = NodeProperties::GetValueInput(current, 0);
859 state = state->KillField(
860 object, FieldIndexOf(JSObject::kElementsOffset), zone());
861 if (flags & GrowFastElementsFlag::kArrayObject) {
862 state = state->KillField(
863 object, FieldIndexOf(JSArray::kLengthOffset), zone());
864 }
865 break;
866 }
867 case IrOpcode::kTransitionElementsKind: {
868 Node* const object = NodeProperties::GetValueInput(current, 0);
869 state = state->KillField(
870 object, FieldIndexOf(HeapObject::kMapOffset), zone());
871 state = state->KillField(
872 object, FieldIndexOf(JSObject::kElementsOffset), zone());
873 break;
874 }
875 case IrOpcode::kStoreField: {
876 FieldAccess const& access = FieldAccessOf(current->op());
877 Node* const object = NodeProperties::GetValueInput(current, 0);
878 int field_index = FieldIndexOf(access);
879 if (field_index < 0) {
880 state = state->KillFields(object, zone());
881 } else {
882 state = state->KillField(object, field_index, zone());
883 }
884 break;
885 }
886 case IrOpcode::kStoreElement: {
887 Node* const object = NodeProperties::GetValueInput(current, 0);
888 Node* const index = NodeProperties::GetValueInput(current, 1);
889 state = state->KillElement(object, index, zone());
890 break;
891 }
892 case IrOpcode::kStoreBuffer:
893 case IrOpcode::kStoreTypedElement: {
894 // Doesn't affect anything we track with the state currently.
895 break;
896 }
897 default:
898 return empty_state();
899 }
900 }
901 for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
902 queue.push(NodeProperties::GetEffectInput(current, i));
903 }
904 }
905 }
906 return state;
907 }
908
909 // static
FieldIndexOf(int offset)910 int LoadElimination::FieldIndexOf(int offset) {
911 DCHECK_EQ(0, offset % kPointerSize);
912 int field_index = offset / kPointerSize;
913 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
914 return field_index;
915 }
916
917 // static
FieldIndexOf(FieldAccess const & access)918 int LoadElimination::FieldIndexOf(FieldAccess const& access) {
919 MachineRepresentation rep = access.machine_type.representation();
920 switch (rep) {
921 case MachineRepresentation::kNone:
922 case MachineRepresentation::kBit:
923 case MachineRepresentation::kSimd128:
924 UNREACHABLE();
925 break;
926 case MachineRepresentation::kWord32:
927 case MachineRepresentation::kWord64:
928 if (rep != MachineType::PointerRepresentation()) {
929 return -1; // We currently only track pointer size fields.
930 }
931 break;
932 case MachineRepresentation::kWord8:
933 case MachineRepresentation::kWord16:
934 case MachineRepresentation::kFloat32:
935 return -1; // Currently untracked.
936 case MachineRepresentation::kFloat64:
937 if (kDoubleSize != kPointerSize) {
938 return -1; // We currently only track pointer size fields.
939 }
940 // Fall through.
941 case MachineRepresentation::kTaggedSigned:
942 case MachineRepresentation::kTaggedPointer:
943 case MachineRepresentation::kTagged:
944 // TODO(bmeurer): Check that we never do overlapping load/stores of
945 // individual parts of Float64 values.
946 break;
947 }
948 if (access.base_is_tagged != kTaggedBase) {
949 return -1; // We currently only track tagged objects.
950 }
951 return FieldIndexOf(access.offset);
952 }
953
common() const954 CommonOperatorBuilder* LoadElimination::common() const {
955 return jsgraph()->common();
956 }
957
graph() const958 Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
959
960 } // namespace compiler
961 } // namespace internal
962 } // namespace v8
963