blob: 93c24a08e5cfd6af94a74e6f7e6e876ee601e3e7 [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
451Node* LoadElimination::AbstractState::LookupField(Node* object,
452 size_t index) const {
453 if (AbstractField const* this_field = this->fields_[index]) {
454 return this_field->Lookup(object);
455 }
456 return nullptr;
457}
458
Ben Murdochf3b273f2017-01-17 12:11:28 +0000459void LoadElimination::AbstractState::Print() const {
460 if (checks_) {
461 PrintF(" checks:\n");
462 checks_->Print();
463 }
464 if (elements_) {
465 PrintF(" elements:\n");
466 elements_->Print();
467 }
468 for (size_t i = 0; i < arraysize(fields_); ++i) {
469 if (AbstractField const* const field = fields_[i]) {
470 PrintF(" field %zu:\n", i);
471 field->Print();
472 }
473 }
474}
475
Ben Murdochf91f0612016-11-29 16:50:11 +0000476LoadElimination::AbstractState const*
477LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const {
478 size_t const id = node->id();
479 if (id < info_for_node_.size()) return info_for_node_[id];
480 return nullptr;
481}
482
483void LoadElimination::AbstractStateForEffectNodes::Set(
484 Node* node, AbstractState const* state) {
485 size_t const id = node->id();
486 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
487 info_for_node_[id] = state;
488}
489
Ben Murdochf3b273f2017-01-17 12:11:28 +0000490Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
491 Node* const effect = NodeProperties::GetEffectInput(node);
492 AbstractState const* state = node_states_.Get(effect);
493 if (state == nullptr) return NoChange();
494 if (Node* const check = state->LookupCheck(node)) {
495 ReplaceWithValue(node, check, effect);
496 return Replace(check);
497 }
498 state = state->AddCheck(node, zone());
499 return UpdateState(node, state);
500}
501
Ben Murdochf91f0612016-11-29 16:50:11 +0000502Reduction LoadElimination::ReduceCheckMaps(Node* node) {
503 Node* const object = NodeProperties::GetValueInput(node, 0);
504 Node* const effect = NodeProperties::GetEffectInput(node);
505 AbstractState const* state = node_states_.Get(effect);
506 if (state == nullptr) return NoChange();
507 int const map_input_count = node->op()->ValueInputCount() - 1;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000508 if (Node* const object_map =
509 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000510 for (int i = 0; i < map_input_count; ++i) {
511 Node* map = NodeProperties::GetValueInput(node, 1 + i);
512 if (map == object_map) return Replace(effect);
513 }
514 }
515 if (map_input_count == 1) {
516 Node* const map0 = NodeProperties::GetValueInput(node, 1);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000517 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset), map0,
518 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000519 }
520 return UpdateState(node, state);
521}
522
523Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
524 Node* const object = NodeProperties::GetValueInput(node, 0);
525 Node* const elements = NodeProperties::GetValueInput(node, 1);
526 Node* const effect = NodeProperties::GetEffectInput(node);
527 AbstractState const* state = node_states_.Get(effect);
528 if (state == nullptr) return NoChange();
529 Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000530 if (Node* const elements_map =
531 state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000532 // Check if the {elements} already have the fixed array map.
533 if (elements_map == fixed_array_map) {
534 ReplaceWithValue(node, elements, effect);
535 return Replace(elements);
536 }
537 }
538 // We know that the resulting elements have the fixed array map.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000539 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
540 fixed_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000541 // Kill the previous elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000542 state =
543 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000544 // Add the new elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000545 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
546 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000547 return UpdateState(node, state);
548}
549
550Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
551 GrowFastElementsFlags flags = GrowFastElementsFlagsOf(node->op());
552 Node* const object = NodeProperties::GetValueInput(node, 0);
553 Node* const effect = NodeProperties::GetEffectInput(node);
554 AbstractState const* state = node_states_.Get(effect);
555 if (state == nullptr) return NoChange();
556 if (flags & GrowFastElementsFlag::kDoubleElements) {
557 // We know that the resulting elements have the fixed double array map.
558 Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000559 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
560 fixed_double_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000561 } else {
562 // We know that the resulting elements have the fixed array map.
563 Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000564 state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
565 fixed_array_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000566 }
567 if (flags & GrowFastElementsFlag::kArrayObject) {
568 // Kill the previous Array::length on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000569 state =
570 state->KillField(object, FieldIndexOf(JSArray::kLengthOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000571 }
572 // Kill the previous elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000573 state =
574 state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000575 // Add the new elements on {object}.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000576 state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
577 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000578 return UpdateState(node, state);
579}
580
581Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
582 Node* const object = NodeProperties::GetValueInput(node, 0);
583 Node* const source_map = NodeProperties::GetValueInput(node, 1);
584 Node* const target_map = NodeProperties::GetValueInput(node, 2);
585 Node* const effect = NodeProperties::GetEffectInput(node);
586 AbstractState const* state = node_states_.Get(effect);
587 if (state == nullptr) return NoChange();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000588 if (Node* const object_map =
589 state->LookupField(object, FieldIndexOf(HeapObject::kMapOffset))) {
Ben Murdochf91f0612016-11-29 16:50:11 +0000590 if (target_map == object_map) {
591 // The {object} already has the {target_map}, so this TransitionElements
592 // {node} is fully redundant (independent of what {source_map} is).
593 return Replace(effect);
594 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000595 state =
596 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000597 if (source_map == object_map) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000598 state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset),
599 target_map, zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000600 }
601 } else {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000602 state =
603 state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000604 }
605 ElementsTransition transition = ElementsTransitionOf(node->op());
606 switch (transition) {
607 case ElementsTransition::kFastTransition:
608 break;
609 case ElementsTransition::kSlowTransition:
610 // Kill the elements as well.
Ben Murdochf3b273f2017-01-17 12:11:28 +0000611 state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
612 zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000613 break;
614 }
615 return UpdateState(node, state);
616}
617
618Reduction LoadElimination::ReduceLoadField(Node* node) {
619 FieldAccess const& access = FieldAccessOf(node->op());
620 Node* const object = NodeProperties::GetValueInput(node, 0);
621 Node* const effect = NodeProperties::GetEffectInput(node);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000622 Node* const control = NodeProperties::GetControlInput(node);
Ben Murdochf91f0612016-11-29 16:50:11 +0000623 AbstractState const* state = node_states_.Get(effect);
624 if (state == nullptr) return NoChange();
625 int field_index = FieldIndexOf(access);
626 if (field_index >= 0) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000627 if (Node* replacement = state->LookupField(object, field_index)) {
628 // Make sure we don't resurrect dead {replacement} nodes.
629 if (!replacement->IsDead()) {
630 // We might need to guard the {replacement} if the type of the
631 // {node} is more precise than the type of the {replacement}.
632 Type* const node_type = NodeProperties::GetType(node);
633 if (!NodeProperties::GetType(replacement)->Is(node_type)) {
634 replacement = graph()->NewNode(common()->TypeGuard(node_type),
635 replacement, control);
636 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000637 ReplaceWithValue(node, replacement, effect);
638 return Replace(replacement);
639 }
640 }
641 state = state->AddField(object, field_index, node, zone());
642 }
643 return UpdateState(node, state);
644}
645
646Reduction LoadElimination::ReduceStoreField(Node* node) {
647 FieldAccess const& access = FieldAccessOf(node->op());
648 Node* const object = NodeProperties::GetValueInput(node, 0);
649 Node* const new_value = NodeProperties::GetValueInput(node, 1);
650 Node* const effect = NodeProperties::GetEffectInput(node);
651 AbstractState const* state = node_states_.Get(effect);
652 if (state == nullptr) return NoChange();
653 int field_index = FieldIndexOf(access);
654 if (field_index >= 0) {
655 Node* const old_value = state->LookupField(object, field_index);
656 if (old_value == new_value) {
657 // This store is fully redundant.
658 return Replace(effect);
659 }
660 // Kill all potentially aliasing fields and record the new value.
661 state = state->KillField(object, field_index, zone());
662 state = state->AddField(object, field_index, new_value, zone());
663 } else {
664 // Unsupported StoreField operator.
665 state = empty_state();
666 }
667 return UpdateState(node, state);
668}
669
670Reduction LoadElimination::ReduceLoadElement(Node* node) {
671 Node* const object = NodeProperties::GetValueInput(node, 0);
672 Node* const index = NodeProperties::GetValueInput(node, 1);
673 Node* const effect = NodeProperties::GetEffectInput(node);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000674 Node* const control = NodeProperties::GetControlInput(node);
Ben Murdochf91f0612016-11-29 16:50:11 +0000675 AbstractState const* state = node_states_.Get(effect);
676 if (state == nullptr) return NoChange();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000677 if (Node* replacement = state->LookupElement(object, index)) {
678 // Make sure we don't resurrect dead {replacement} nodes.
679 if (!replacement->IsDead()) {
680 // We might need to guard the {replacement} if the type of the
681 // {node} is more precise than the type of the {replacement}.
682 Type* const node_type = NodeProperties::GetType(node);
683 if (!NodeProperties::GetType(replacement)->Is(node_type)) {
684 replacement = graph()->NewNode(common()->TypeGuard(node_type),
685 replacement, control);
686 }
Ben Murdochf91f0612016-11-29 16:50:11 +0000687 ReplaceWithValue(node, replacement, effect);
688 return Replace(replacement);
689 }
690 }
691 state = state->AddElement(object, index, node, zone());
692 return UpdateState(node, state);
693}
694
695Reduction LoadElimination::ReduceStoreElement(Node* node) {
696 ElementAccess const& access = ElementAccessOf(node->op());
697 Node* const object = NodeProperties::GetValueInput(node, 0);
698 Node* const index = NodeProperties::GetValueInput(node, 1);
699 Node* const new_value = NodeProperties::GetValueInput(node, 2);
700 Node* const effect = NodeProperties::GetEffectInput(node);
701 AbstractState const* state = node_states_.Get(effect);
702 if (state == nullptr) return NoChange();
703 Node* const old_value = state->LookupElement(object, index);
704 if (old_value == new_value) {
705 // This store is fully redundant.
706 return Replace(effect);
707 }
708 // Kill all potentially aliasing elements.
709 state = state->KillElement(object, index, zone());
710 // Only record the new value if the store doesn't have an implicit truncation.
711 switch (access.machine_type.representation()) {
712 case MachineRepresentation::kNone:
713 case MachineRepresentation::kBit:
714 UNREACHABLE();
715 break;
716 case MachineRepresentation::kWord8:
717 case MachineRepresentation::kWord16:
718 case MachineRepresentation::kWord32:
719 case MachineRepresentation::kWord64:
720 case MachineRepresentation::kFloat32:
721 // TODO(turbofan): Add support for doing the truncations.
722 break;
723 case MachineRepresentation::kFloat64:
724 case MachineRepresentation::kSimd128:
725 case MachineRepresentation::kTaggedSigned:
726 case MachineRepresentation::kTaggedPointer:
727 case MachineRepresentation::kTagged:
728 state = state->AddElement(object, index, new_value, zone());
729 break;
730 }
731 return UpdateState(node, state);
732}
733
734Reduction LoadElimination::ReduceStoreTypedElement(Node* node) {
735 Node* const effect = NodeProperties::GetEffectInput(node);
736 AbstractState const* state = node_states_.Get(effect);
737 if (state == nullptr) return NoChange();
738 return UpdateState(node, state);
739}
740
741Reduction LoadElimination::ReduceEffectPhi(Node* node) {
742 Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
743 Node* const control = NodeProperties::GetControlInput(node);
744 AbstractState const* state0 = node_states_.Get(effect0);
745 if (state0 == nullptr) return NoChange();
746 if (control->opcode() == IrOpcode::kLoop) {
747 // Here we rely on having only reducible loops:
748 // The loop entry edge always dominates the header, so we can just take
749 // the state from the first input, and compute the loop state based on it.
750 AbstractState const* state = ComputeLoopState(node, state0);
751 return UpdateState(node, state);
752 }
753 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
754
755 // Shortcut for the case when we do not know anything about some input.
756 int const input_count = node->op()->EffectInputCount();
757 for (int i = 1; i < input_count; ++i) {
758 Node* const effect = NodeProperties::GetEffectInput(node, i);
759 if (node_states_.Get(effect) == nullptr) return NoChange();
760 }
761
762 // Make a copy of the first input's state and merge with the state
763 // from other inputs.
764 AbstractState* state = new (zone()) AbstractState(*state0);
765 for (int i = 1; i < input_count; ++i) {
766 Node* const input = NodeProperties::GetEffectInput(node, i);
767 state->Merge(node_states_.Get(input), zone());
768 }
769 return UpdateState(node, state);
770}
771
772Reduction LoadElimination::ReduceStart(Node* node) {
773 return UpdateState(node, empty_state());
774}
775
776Reduction LoadElimination::ReduceOtherNode(Node* node) {
777 if (node->op()->EffectInputCount() == 1) {
778 if (node->op()->EffectOutputCount() == 1) {
779 Node* const effect = NodeProperties::GetEffectInput(node);
780 AbstractState const* state = node_states_.Get(effect);
781 // If we do not know anything about the predecessor, do not propagate
782 // just yet because we will have to recompute anyway once we compute
783 // the predecessor.
784 if (state == nullptr) return NoChange();
785 // Check if this {node} has some uncontrolled side effects.
786 if (!node->op()->HasProperty(Operator::kNoWrite)) {
787 state = empty_state();
788 }
789 return UpdateState(node, state);
790 } else {
791 // Effect terminators should be handled specially.
792 return NoChange();
793 }
794 }
795 DCHECK_EQ(0, node->op()->EffectInputCount());
796 DCHECK_EQ(0, node->op()->EffectOutputCount());
Emily Bernier958fae72015-03-24 16:35:39 -0400797 return NoChange();
798}
799
Ben Murdochf91f0612016-11-29 16:50:11 +0000800Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) {
801 AbstractState const* original = node_states_.Get(node);
802 // Only signal that the {node} has Changed, if the information about {state}
803 // has changed wrt. the {original}.
804 if (state != original) {
805 if (original == nullptr || !state->Equals(original)) {
806 node_states_.Set(node, state);
807 return Changed(node);
808 }
809 }
810 return NoChange();
811}
812
813LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
814 Node* node, AbstractState const* state) const {
815 Node* const control = NodeProperties::GetControlInput(node);
816 ZoneQueue<Node*> queue(zone());
817 ZoneSet<Node*> visited(zone());
818 visited.insert(node);
819 for (int i = 1; i < control->InputCount(); ++i) {
820 queue.push(node->InputAt(i));
821 }
822 while (!queue.empty()) {
823 Node* const current = queue.front();
824 queue.pop();
825 if (visited.find(current) == visited.end()) {
826 visited.insert(current);
827 if (!current->op()->HasProperty(Operator::kNoWrite)) {
828 switch (current->opcode()) {
829 case IrOpcode::kEnsureWritableFastElements: {
830 Node* const object = NodeProperties::GetValueInput(current, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000831 state = state->KillField(
832 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000833 break;
834 }
835 case IrOpcode::kMaybeGrowFastElements: {
836 GrowFastElementsFlags flags =
837 GrowFastElementsFlagsOf(current->op());
838 Node* const object = NodeProperties::GetValueInput(current, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000839 state = state->KillField(
840 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000841 if (flags & GrowFastElementsFlag::kArrayObject) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000842 state = state->KillField(
843 object, FieldIndexOf(JSArray::kLengthOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000844 }
845 break;
846 }
847 case IrOpcode::kTransitionElementsKind: {
848 Node* const object = NodeProperties::GetValueInput(current, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000849 state = state->KillField(
850 object, FieldIndexOf(HeapObject::kMapOffset), zone());
851 state = state->KillField(
852 object, FieldIndexOf(JSObject::kElementsOffset), zone());
Ben Murdochf91f0612016-11-29 16:50:11 +0000853 break;
854 }
855 case IrOpcode::kStoreField: {
856 FieldAccess const& access = FieldAccessOf(current->op());
857 Node* const object = NodeProperties::GetValueInput(current, 0);
858 int field_index = FieldIndexOf(access);
859 if (field_index < 0) return empty_state();
860 state = state->KillField(object, field_index, zone());
861 break;
862 }
863 case IrOpcode::kStoreElement: {
864 Node* const object = NodeProperties::GetValueInput(current, 0);
865 Node* const index = NodeProperties::GetValueInput(current, 1);
866 state = state->KillElement(object, index, zone());
867 break;
868 }
869 case IrOpcode::kStoreBuffer:
870 case IrOpcode::kStoreTypedElement: {
871 // Doesn't affect anything we track with the state currently.
872 break;
873 }
874 default:
875 return empty_state();
876 }
877 }
878 for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
879 queue.push(NodeProperties::GetEffectInput(current, i));
880 }
881 }
882 }
883 return state;
884}
885
886// static
Ben Murdochf3b273f2017-01-17 12:11:28 +0000887int LoadElimination::FieldIndexOf(int offset) {
888 DCHECK_EQ(0, offset % kPointerSize);
889 int field_index = offset / kPointerSize;
890 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
891 return field_index;
892}
893
894// static
Ben Murdochf91f0612016-11-29 16:50:11 +0000895int LoadElimination::FieldIndexOf(FieldAccess const& access) {
896 MachineRepresentation rep = access.machine_type.representation();
897 switch (rep) {
898 case MachineRepresentation::kNone:
899 case MachineRepresentation::kBit:
900 UNREACHABLE();
901 break;
902 case MachineRepresentation::kWord32:
903 case MachineRepresentation::kWord64:
904 if (rep != MachineType::PointerRepresentation()) {
905 return -1; // We currently only track pointer size fields.
906 }
907 break;
908 case MachineRepresentation::kWord8:
909 case MachineRepresentation::kWord16:
910 case MachineRepresentation::kFloat32:
911 return -1; // Currently untracked.
912 case MachineRepresentation::kFloat64:
913 case MachineRepresentation::kSimd128:
914 return -1; // Currently untracked.
915 case MachineRepresentation::kTaggedSigned:
916 case MachineRepresentation::kTaggedPointer:
917 case MachineRepresentation::kTagged:
918 // TODO(bmeurer): Check that we never do overlapping load/stores of
919 // individual parts of Float64/Simd128 values.
920 break;
921 }
922 DCHECK_EQ(kTaggedBase, access.base_is_tagged);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000923 return FieldIndexOf(access.offset);
Ben Murdochf91f0612016-11-29 16:50:11 +0000924}
925
Ben Murdochf3b273f2017-01-17 12:11:28 +0000926CommonOperatorBuilder* LoadElimination::common() const {
927 return jsgraph()->common();
928}
929
930Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
931
Emily Bernier958fae72015-03-24 16:35:39 -0400932} // namespace compiler
933} // namespace internal
934} // namespace v8