blob: e50ebe19190c0313cd5c38f190199bf675655b76 [file] [log] [blame]
Ben Murdochf91f0612016-11-29 16:50:11 +00001// Copyright 2016 the V8 project authors. All rights reserved.
Emily Bernier958fae72015-03-24 16:35:39 -04002// 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
Ben Murdochf3b273f2017-01-17 12:11:28 +00007#include "src/compiler/common-operator.h"
Ben Murdochf91f0612016-11-29 16:50:11 +00008#include "src/compiler/js-graph.h"
Ben Murdoch014dc512016-03-22 12:00:34 +00009#include "src/compiler/node-properties.h"
Emily Bernier958fae72015-03-24 16:35:39 -040010#include "src/compiler/simplified-operator.h"
11
12namespace v8 {
13namespace internal {
14namespace compiler {
15
Ben Murdochf91f0612016-11-29 16:50:11 +000016namespace {
17
18enum Aliasing { kNoAlias, kMayAlias, kMustAlias };
19
20Aliasing QueryAlias(Node* a, Node* b) {
21 if (a == b) return kMustAlias;
22 if (!NodeProperties::GetType(a)->Maybe(NodeProperties::GetType(b))) {
23 return kNoAlias;
24 }
Ben Murdochf3b273f2017-01-17 12:11:28 +000025 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;
Ben Murdochf91f0612016-11-29 16:50:11 +000036 }
Ben Murdochf3b273f2017-01-17 12:11:28 +000037 case IrOpcode::kFinishRegion:
38 return QueryAlias(a, b->InputAt(0));
39 default:
40 break;
Ben Murdochf91f0612016-11-29 16:50:11 +000041 }
Ben Murdochf3b273f2017-01-17 12:11:28 +000042 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;
Ben Murdochf91f0612016-11-29 16:50:11 +000052 }
Ben Murdochf3b273f2017-01-17 12:11:28 +000053 case IrOpcode::kFinishRegion:
54 return QueryAlias(a->InputAt(0), b);
55 default:
56 break;
Ben Murdochf91f0612016-11-29 16:50:11 +000057 }
58 return kMayAlias;
59}
60
61bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; }
62
63bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
64
65} // namespace
Emily Bernier958fae72015-03-24 16:35:39 -040066
Emily Bernier958fae72015-03-24 16:35:39 -040067Reduction LoadElimination::Reduce(Node* node) {
Ben Murdochf3b273f2017-01-17 12:11:28 +000068 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 }
Emily Bernier958fae72015-03-24 16:35:39 -040094 switch (node->opcode()) {
Ben Murdochf3b273f2017-01-17 12:11:28 +000095 case IrOpcode::kArrayBufferWasNeutered:
96 return ReduceArrayBufferWasNeutered(node);
Ben Murdochf91f0612016-11-29 16:50:11 +000097 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);
Emily Bernier958fae72015-03-24 16:35:39 -0400105 case IrOpcode::kLoadField:
106 return ReduceLoadField(node);
Ben Murdochf91f0612016-11-29 16:50:11 +0000107 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:
Emily Bernier958fae72015-03-24 16:35:39 -0400118 break;
Ben Murdochf91f0612016-11-29 16:50:11 +0000119 case IrOpcode::kStart:
120 return ReduceStart(node);
121 default:
122 return ReduceOtherNode(node);
Emily Bernier958fae72015-03-24 16:35:39 -0400123 }
124 return NoChange();
125}
126
Ben Murdochf3b273f2017-01-17 12:11:28 +0000127namespace {
128
129bool 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
139Node* 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
148bool 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
169LoadElimination::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
186void 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000194Node* 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
207LoadElimination::AbstractElements const*
208LoadElimination::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) ||
Ben Murdochf3b273f2017-01-17 12:11:28 +0000219 !NodeProperties::GetType(index)->Maybe(
220 NodeProperties::GetType(element.index))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000221 that->elements_[that->next_index_++] = element;
Emily Bernier958fae72015-03-24 16:35:39 -0400222 }
Emily Bernier958fae72015-03-24 16:35:39 -0400223 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000224 that->next_index_ %= arraysize(elements_);
225 return that;
226 }
227 }
228 return this;
229}
230
231bool 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) {
Emily Bernier958fae72015-03-24 16:35:39 -0400243 break;
244 }
245 }
246 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000247 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
263LoadElimination::AbstractElements const*
264LoadElimination::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;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000275 break;
Ben Murdochf91f0612016-11-29 16:50:11 +0000276 }
277 }
278 }
279 copy->next_index_ %= arraysize(elements_);
280 return copy;
281}
282
Ben Murdochf3b273f2017-01-17 12:11:28 +0000283void 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000294Node* 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
301LoadElimination::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
Ben Murdochf3b273f2017-01-17 12:11:28 +0000315void 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000323bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000324 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 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000331 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
350void LoadElimination::AbstractState::Merge(AbstractState const* that,
351 Zone* zone) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000352 // 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000358 // Merge the information we have about the elements.
359 if (this->elements_) {
360 this->elements_ = that->elements_
361 ? that->elements_->Merge(this->elements_, zone)
Ben Murdochf3b273f2017-01-17 12:11:28 +0000362 : nullptr;
Ben Murdochf91f0612016-11-29 16:50:11 +0000363 }
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
Ben Murdochf3b273f2017-01-17 12:11:28 +0000377Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
378 return this->checks_ ? this->checks_->Lookup(node) : nullptr;
379}
380
381LoadElimination::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
Ben Murdochf91f0612016-11-29 16:50:11 +0000392Node* 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
400LoadElimination::AbstractState const*
401LoadElimination::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
412LoadElimination::AbstractState const*
413LoadElimination::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
427LoadElimination::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
438LoadElimination::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
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000451LoadElimination::AbstractState const*
452LoadElimination::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
Ben Murdochf91f0612016-11-29 16:50:11 +0000471Node* 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
Ben Murdochf3b273f2017-01-17 12:11:28 +0000479void 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000496LoadElimination::AbstractState const*
497LoadElimination::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
503void 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
Ben Murdochf3b273f2017-01-17 12:11:28 +0000510Reduction 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000522Reduction 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;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000528 if (Node* const object_map =
529 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000530 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);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000537 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), map0,
538 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000539 }
540 return UpdateState(node, state);
541}
542
543Reduction 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();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000550 if (Node* const elements_map =
551 state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000552 // 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.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000559 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
560 fixed_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000561 // Kill the previous elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000562 state =
563 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000564 // Add the new elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000565 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
566 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000567 return UpdateState(node, state);
568}
569
570Reduction 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();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000579 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
580 fixed_double_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000581 } else {
582 // We know that the resulting elements have the fixed array map.
583 Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000584 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
585 fixed_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000586 }
587 if (flags & GrowFastElementsFlag::kArrayObject) {
588 // Kill the previous Array::length on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000589 state =
590 state->KillField(object, FieldIndexOf(JSArray::kLengthOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000591 }
592 // Kill the previous elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000593 state =
594 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000595 // Add the new elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000596 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
597 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000598 return UpdateState(node, state);
599}
600
601Reduction 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();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000608 if (Node* const object_map =
609 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000610 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 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000615 state =
616 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000617 if (source_map == object_map) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000618 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset),
619 target_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000620 }
621 } else {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000622 state =
623 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000624 }
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.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000631 state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
632 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000633 break;
634 }
635 return UpdateState(node, state);
636}
637
638Reduction 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);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000642 Node* const control = NodeProperties::GetControlInput(node);
Ben Murdochf91f0612016-11-29 16:50:11 +0000643 AbstractState const* state = node_states_.Get(effect);
644 if (state == nullptr) return NoChange();
645 int field_index = FieldIndexOf(access);
646 if (field_index >= 0) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000647 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 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000657 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
666Reduction 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.
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000685 state = state->KillFields(object, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000686 }
687 return UpdateState(node, state);
688}
689
690Reduction 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);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000694 Node* const control = NodeProperties::GetControlInput(node);
Ben Murdochf91f0612016-11-29 16:50:11 +0000695 AbstractState const* state = node_states_.Get(effect);
696 if (state == nullptr) return NoChange();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000697 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 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000707 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
715Reduction 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
754Reduction 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
761Reduction 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
792Reduction LoadElimination::ReduceStart(Node* node) {
793 return UpdateState(node, empty_state());
794}
795
796Reduction 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());
Emily Bernier958fae72015-03-24 16:35:39 -0400817 return NoChange();
818}
819
Ben Murdochf91f0612016-11-29 16:50:11 +0000820Reduction 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
833LoadElimination::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);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000851 state = state->KillField(
852 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000853 break;
854 }
855 case IrOpcode::kMaybeGrowFastElements: {
856 GrowFastElementsFlags flags =
857 GrowFastElementsFlagsOf(current->op());
858 Node* const object = NodeProperties::GetValueInput(current, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000859 state = state->KillField(
860 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000861 if (flags & GrowFastElementsFlag::kArrayObject) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000862 state = state->KillField(
863 object, FieldIndexOf(JSArray::kLengthOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000864 }
865 break;
866 }
867 case IrOpcode::kTransitionElementsKind: {
868 Node* const object = NodeProperties::GetValueInput(current, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000869 state = state->KillField(
870 object, FieldIndexOf(HeapObject::kMapOffset), zone());
871 state = state->KillField(
872 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000873 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);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000879 if (field_index < 0) {
880 state = state->KillFields(object, zone());
881 } else {
882 state = state->KillField(object, field_index, zone());
883 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000884 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
Ben Murdochf3b273f2017-01-17 12:11:28 +0000910int 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
Ben Murdochf91f0612016-11-29 16:50:11 +0000918int LoadElimination::FieldIndexOf(FieldAccess const& access) {
919 MachineRepresentation rep = access.machine_type.representation();
920 switch (rep) {
921 case MachineRepresentation::kNone:
922 case MachineRepresentation::kBit:
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000923 case MachineRepresentation::kSimd128:
Ben Murdochf91f0612016-11-29 16:50:11 +0000924 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:
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000937 if (kDoubleSize != kPointerSize) {
938 return -1; // We currently only track pointer size fields.
939 }
940 // Fall through.
Ben Murdochf91f0612016-11-29 16:50:11 +0000941 case MachineRepresentation::kTaggedSigned:
942 case MachineRepresentation::kTaggedPointer:
943 case MachineRepresentation::kTagged:
944 // TODO(bmeurer): Check that we never do overlapping load/stores of
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000945 // individual parts of Float64 values.
Ben Murdochf91f0612016-11-29 16:50:11 +0000946 break;
947 }
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000948 if (access.base_is_tagged != kTaggedBase) {
949 return -1; // We currently only track tagged objects.
950 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000951 return FieldIndexOf(access.offset);
Ben Murdochf91f0612016-11-29 16:50:11 +0000952}
953
Ben Murdochf3b273f2017-01-17 12:11:28 +0000954CommonOperatorBuilder* LoadElimination::common() const {
955 return jsgraph()->common();
956}
957
958Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
959
Emily Bernier958fae72015-03-24 16:35:39 -0400960} // namespace compiler
961} // namespace internal
962} // namespace v8