V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
redundancy-elimination.cc
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/redundancy-elimination.h"
6 
7 #include "src/compiler/node-properties.h"
8 #include "src/compiler/simplified-operator.h"
9 
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13 
14 RedundancyElimination::RedundancyElimination(Editor* editor, Zone* zone)
15  : AdvancedReducer(editor), node_checks_(zone), zone_(zone) {}
16 
17 RedundancyElimination::~RedundancyElimination() = default;
18 
19 Reduction RedundancyElimination::Reduce(Node* node) {
20  if (node_checks_.Get(node)) return NoChange();
21  switch (node->opcode()) {
22  case IrOpcode::kCheckBounds:
23  case IrOpcode::kCheckEqualsInternalizedString:
24  case IrOpcode::kCheckEqualsSymbol:
25  case IrOpcode::kCheckFloat64Hole:
26  case IrOpcode::kCheckHeapObject:
27  case IrOpcode::kCheckIf:
28  case IrOpcode::kCheckInternalizedString:
29  case IrOpcode::kCheckNotTaggedHole:
30  case IrOpcode::kCheckNumber:
31  case IrOpcode::kCheckReceiver:
32  case IrOpcode::kCheckReceiverOrNullOrUndefined:
33  case IrOpcode::kCheckSmi:
34  case IrOpcode::kCheckString:
35  case IrOpcode::kCheckSymbol:
36 #define SIMPLIFIED_CHECKED_OP(Opcode) case IrOpcode::k##Opcode:
37  SIMPLIFIED_CHECKED_OP_LIST(SIMPLIFIED_CHECKED_OP)
38 #undef SIMPLIFIED_CHECKED_OP
39  return ReduceCheckNode(node);
40  case IrOpcode::kSpeculativeNumberEqual:
41  case IrOpcode::kSpeculativeNumberLessThan:
42  case IrOpcode::kSpeculativeNumberLessThanOrEqual:
43  return ReduceSpeculativeNumberComparison(node);
44  case IrOpcode::kSpeculativeNumberAdd:
45  case IrOpcode::kSpeculativeNumberSubtract:
46  case IrOpcode::kSpeculativeSafeIntegerAdd:
47  case IrOpcode::kSpeculativeSafeIntegerSubtract:
48  case IrOpcode::kSpeculativeToNumber:
49  return ReduceSpeculativeNumberOperation(node);
50  case IrOpcode::kEffectPhi:
51  return ReduceEffectPhi(node);
52  case IrOpcode::kDead:
53  break;
54  case IrOpcode::kStart:
55  return ReduceStart(node);
56  default:
57  return ReduceOtherNode(node);
58  }
59  return NoChange();
60 }
61 
62 // static
63 RedundancyElimination::EffectPathChecks*
64 RedundancyElimination::EffectPathChecks::Copy(Zone* zone,
65  EffectPathChecks const* checks) {
66  return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(*checks);
67 }
68 
69 // static
70 RedundancyElimination::EffectPathChecks const*
71 RedundancyElimination::EffectPathChecks::Empty(Zone* zone) {
72  return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(nullptr, 0);
73 }
74 
75 bool RedundancyElimination::EffectPathChecks::Equals(
76  EffectPathChecks const* that) const {
77  if (this->size_ != that->size_) return false;
78  Check* this_head = this->head_;
79  Check* that_head = that->head_;
80  while (this_head != that_head) {
81  if (this_head->node != that_head->node) return false;
82  this_head = this_head->next;
83  that_head = that_head->next;
84  }
85  return true;
86 }
87 
88 void RedundancyElimination::EffectPathChecks::Merge(
89  EffectPathChecks const* that) {
90  // Change the current check list to a longest common tail of this check
91  // list and the other list.
92 
93  // First, we throw away the prefix of the longer list, so that
94  // we have lists of the same length.
95  Check* that_head = that->head_;
96  size_t that_size = that->size_;
97  while (that_size > size_) {
98  that_head = that_head->next;
99  that_size--;
100  }
101  while (size_ > that_size) {
102  head_ = head_->next;
103  size_--;
104  }
105 
106  // Then we go through both lists in lock-step until we find
107  // the common tail.
108  while (head_ != that_head) {
109  DCHECK_LT(0u, size_);
110  DCHECK_NOT_NULL(head_);
111  size_--;
112  head_ = head_->next;
113  that_head = that_head->next;
114  }
115 }
116 
117 RedundancyElimination::EffectPathChecks const*
118 RedundancyElimination::EffectPathChecks::AddCheck(Zone* zone,
119  Node* node) const {
120  Check* head = new (zone->New(sizeof(Check))) Check(node, head_);
121  return new (zone->New(sizeof(EffectPathChecks)))
122  EffectPathChecks(head, size_ + 1);
123 }
124 
125 namespace {
126 
127 // Does check {a} subsume check {b}?
128 bool CheckSubsumes(Node const* a, Node const* b) {
129  if (a->op() != b->op()) {
130  if (a->opcode() == IrOpcode::kCheckInternalizedString &&
131  b->opcode() == IrOpcode::kCheckString) {
132  // CheckInternalizedString(node) implies CheckString(node)
133  } else if (a->opcode() == IrOpcode::kCheckSmi &&
134  b->opcode() == IrOpcode::kCheckNumber) {
135  // CheckSmi(node) implies CheckNumber(node)
136  } else if (a->opcode() == IrOpcode::kCheckedTaggedSignedToInt32 &&
137  b->opcode() == IrOpcode::kCheckedTaggedToInt32) {
138  // CheckedTaggedSignedToInt32(node) implies CheckedTaggedToInt32(node)
139  } else if (a->opcode() == IrOpcode::kCheckReceiver &&
140  b->opcode() == IrOpcode::kCheckReceiverOrNullOrUndefined) {
141  // CheckReceiver(node) implies CheckReceiverOrNullOrUndefined(node)
142  } else if (a->opcode() != b->opcode()) {
143  return false;
144  } else {
145  switch (a->opcode()) {
146  case IrOpcode::kCheckBounds:
147  case IrOpcode::kCheckSmi:
148  case IrOpcode::kCheckString:
149  case IrOpcode::kCheckNumber:
150  break;
151  case IrOpcode::kCheckedInt32ToTaggedSigned:
152  case IrOpcode::kCheckedInt64ToInt32:
153  case IrOpcode::kCheckedInt64ToTaggedSigned:
154  case IrOpcode::kCheckedTaggedSignedToInt32:
155  case IrOpcode::kCheckedTaggedToTaggedPointer:
156  case IrOpcode::kCheckedTaggedToTaggedSigned:
157  case IrOpcode::kCheckedUint32Bounds:
158  case IrOpcode::kCheckedUint32ToInt32:
159  case IrOpcode::kCheckedUint32ToTaggedSigned:
160  case IrOpcode::kCheckedUint64Bounds:
161  case IrOpcode::kCheckedUint64ToInt32:
162  case IrOpcode::kCheckedUint64ToTaggedSigned:
163  break;
164  case IrOpcode::kCheckedFloat64ToInt32:
165  case IrOpcode::kCheckedFloat64ToInt64:
166  case IrOpcode::kCheckedTaggedToInt32:
167  case IrOpcode::kCheckedTaggedToInt64: {
168  const CheckMinusZeroParameters& ap =
169  CheckMinusZeroParametersOf(a->op());
170  const CheckMinusZeroParameters& bp =
171  CheckMinusZeroParametersOf(b->op());
172  if (ap.mode() != bp.mode()) {
173  return false;
174  }
175  break;
176  }
177  case IrOpcode::kCheckedTaggedToFloat64:
178  case IrOpcode::kCheckedTruncateTaggedToWord32: {
179  CheckTaggedInputParameters const& ap =
180  CheckTaggedInputParametersOf(a->op());
181  CheckTaggedInputParameters const& bp =
182  CheckTaggedInputParametersOf(b->op());
183  // {a} subsumes {b} if the modes are either the same, or {a} checks
184  // for Number, in which case {b} will be subsumed no matter what.
185  if (ap.mode() != bp.mode() &&
186  ap.mode() != CheckTaggedInputMode::kNumber) {
187  return false;
188  }
189  break;
190  }
191  default:
192  DCHECK(!IsCheckedWithFeedback(a->op()));
193  return false;
194  }
195  }
196  }
197  for (int i = a->op()->ValueInputCount(); --i >= 0;) {
198  if (a->InputAt(i) != b->InputAt(i)) return false;
199  }
200  return true;
201 }
202 
203 } // namespace
204 
205 Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
206  for (Check const* check = head_; check != nullptr; check = check->next) {
207  if (CheckSubsumes(check->node, node)) {
208  DCHECK(!check->node->IsDead());
209  return check->node;
210  }
211  }
212  return nullptr;
213 }
214 
215 Node* RedundancyElimination::EffectPathChecks::LookupBoundsCheckFor(
216  Node* node) const {
217  for (Check const* check = head_; check != nullptr; check = check->next) {
218  if (check->node->opcode() == IrOpcode::kCheckBounds &&
219  check->node->InputAt(0) == node) {
220  return check->node;
221  }
222  }
223  return nullptr;
224 }
225 
226 RedundancyElimination::EffectPathChecks const*
227 RedundancyElimination::PathChecksForEffectNodes::Get(Node* node) const {
228  size_t const id = node->id();
229  if (id < info_for_node_.size()) return info_for_node_[id];
230  return nullptr;
231 }
232 
233 void RedundancyElimination::PathChecksForEffectNodes::Set(
234  Node* node, EffectPathChecks const* checks) {
235  size_t const id = node->id();
236  if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
237  info_for_node_[id] = checks;
238 }
239 
240 Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
241  Node* const effect = NodeProperties::GetEffectInput(node);
242  EffectPathChecks const* checks = node_checks_.Get(effect);
243  // If we do not know anything about the predecessor, do not propagate just yet
244  // because we will have to recompute anyway once we compute the predecessor.
245  if (checks == nullptr) return NoChange();
246  // See if we have another check that dominates us.
247  if (Node* check = checks->LookupCheck(node)) {
248  ReplaceWithValue(node, check);
249  return Replace(check);
250  }
251 
252  // Learn from this check.
253  return UpdateChecks(node, checks->AddCheck(zone(), node));
254 }
255 
256 Reduction RedundancyElimination::ReduceEffectPhi(Node* node) {
257  Node* const control = NodeProperties::GetControlInput(node);
258  if (control->opcode() == IrOpcode::kLoop) {
259  // Here we rely on having only reducible loops:
260  // The loop entry edge always dominates the header, so we can just use
261  // the information from the loop entry edge.
262  return TakeChecksFromFirstEffect(node);
263  }
264  DCHECK_EQ(IrOpcode::kMerge, control->opcode());
265 
266  // Shortcut for the case when we do not know anything about some input.
267  int const input_count = node->op()->EffectInputCount();
268  for (int i = 0; i < input_count; ++i) {
269  Node* const effect = NodeProperties::GetEffectInput(node, i);
270  if (node_checks_.Get(effect) == nullptr) return NoChange();
271  }
272 
273  // Make a copy of the first input's checks and merge with the checks
274  // from other inputs.
275  EffectPathChecks* checks = EffectPathChecks::Copy(
276  zone(), node_checks_.Get(NodeProperties::GetEffectInput(node, 0)));
277  for (int i = 1; i < input_count; ++i) {
278  Node* const input = NodeProperties::GetEffectInput(node, i);
279  checks->Merge(node_checks_.Get(input));
280  }
281  return UpdateChecks(node, checks);
282 }
283 
284 Reduction RedundancyElimination::ReduceSpeculativeNumberComparison(Node* node) {
285  NumberOperationHint const hint = NumberOperationHintOf(node->op());
286  Node* const first = NodeProperties::GetValueInput(node, 0);
287  Type const first_type = NodeProperties::GetType(first);
288  Node* const second = NodeProperties::GetValueInput(node, 1);
289  Type const second_type = NodeProperties::GetType(second);
290  Node* const effect = NodeProperties::GetEffectInput(node);
291  EffectPathChecks const* checks = node_checks_.Get(effect);
292 
293  // If we do not know anything about the predecessor, do not propagate just yet
294  // because we will have to recompute anyway once we compute the predecessor.
295  if (checks == nullptr) return NoChange();
296 
297  // Avoid the potentially expensive lookups below if the {node}
298  // has seen non-Smi inputs in the past, which is a clear signal
299  // that the comparison is probably not performed on a value that
300  // already passed an array bounds check.
301  if (hint == NumberOperationHint::kSignedSmall) {
302  // Don't bother trying to find a CheckBounds for the {first} input
303  // if it's type is already in UnsignedSmall range, since the bounds
304  // check is only going to narrow that range further, but the result
305  // is not going to make the representation selection any better.
306  if (!first_type.Is(Type::UnsignedSmall())) {
307  if (Node* check = checks->LookupBoundsCheckFor(first)) {
308  if (!first_type.Is(NodeProperties::GetType(check))) {
309  // Replace the {first} input with the {check}. This is safe,
310  // despite the fact that {check} can truncate -0 to 0, because
311  // the regular Number comparisons in JavaScript also identify
312  // 0 and -0 (unlike special comparisons as Object.is).
313  NodeProperties::ReplaceValueInput(node, check, 0);
314  Reduction const reduction = ReduceSpeculativeNumberComparison(node);
315  return reduction.Changed() ? reduction : Changed(node);
316  }
317  }
318  }
319 
320  // Don't bother trying to find a CheckBounds for the {second} input
321  // if it's type is already in UnsignedSmall range, since the bounds
322  // check is only going to narrow that range further, but the result
323  // is not going to make the representation selection any better.
324  if (!second_type.Is(Type::UnsignedSmall())) {
325  if (Node* check = checks->LookupBoundsCheckFor(second)) {
326  if (!second_type.Is(NodeProperties::GetType(check))) {
327  // Replace the {second} input with the {check}. This is safe,
328  // despite the fact that {check} can truncate -0 to 0, because
329  // the regular Number comparisons in JavaScript also identify
330  // 0 and -0 (unlike special comparisons as Object.is).
331  NodeProperties::ReplaceValueInput(node, check, 1);
332  Reduction const reduction = ReduceSpeculativeNumberComparison(node);
333  return reduction.Changed() ? reduction : Changed(node);
334  }
335  }
336  }
337  }
338 
339  return UpdateChecks(node, checks);
340 }
341 
342 Reduction RedundancyElimination::ReduceSpeculativeNumberOperation(Node* node) {
343  DCHECK(node->opcode() == IrOpcode::kSpeculativeNumberAdd ||
344  node->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
345  node->opcode() == IrOpcode::kSpeculativeSafeIntegerAdd ||
346  node->opcode() == IrOpcode::kSpeculativeSafeIntegerSubtract ||
347  node->opcode() == IrOpcode::kSpeculativeToNumber);
348  DCHECK_EQ(1, node->op()->EffectInputCount());
349  DCHECK_EQ(1, node->op()->EffectOutputCount());
350 
351  Node* const first = NodeProperties::GetValueInput(node, 0);
352  Node* const effect = NodeProperties::GetEffectInput(node);
353  EffectPathChecks const* checks = node_checks_.Get(effect);
354  // If we do not know anything about the predecessor, do not propagate just yet
355  // because we will have to recompute anyway once we compute the predecessor.
356  if (checks == nullptr) return NoChange();
357 
358  // Check if there's a CheckBounds operation on {first}
359  // in the graph already, which we might be able to
360  // reuse here to improve the representation selection
361  // for the {node} later on.
362  if (Node* check = checks->LookupBoundsCheckFor(first)) {
363  // Only use the bounds {check} if its type is better
364  // than the type of the {first} node, otherwise we
365  // would end up replacing NumberConstant inputs with
366  // CheckBounds operations, which is kind of pointless.
367  if (!NodeProperties::GetType(first).Is(NodeProperties::GetType(check))) {
368  NodeProperties::ReplaceValueInput(node, check, 0);
369  }
370  }
371 
372  return UpdateChecks(node, checks);
373 }
374 
375 Reduction RedundancyElimination::ReduceStart(Node* node) {
376  return UpdateChecks(node, EffectPathChecks::Empty(zone()));
377 }
378 
379 Reduction RedundancyElimination::ReduceOtherNode(Node* node) {
380  if (node->op()->EffectInputCount() == 1) {
381  if (node->op()->EffectOutputCount() == 1) {
382  return TakeChecksFromFirstEffect(node);
383  } else {
384  // Effect terminators should be handled specially.
385  return NoChange();
386  }
387  }
388  DCHECK_EQ(0, node->op()->EffectInputCount());
389  DCHECK_EQ(0, node->op()->EffectOutputCount());
390  return NoChange();
391 }
392 
393 Reduction RedundancyElimination::TakeChecksFromFirstEffect(Node* node) {
394  DCHECK_EQ(1, node->op()->EffectOutputCount());
395  Node* const effect = NodeProperties::GetEffectInput(node);
396  EffectPathChecks const* checks = node_checks_.Get(effect);
397  // If we do not know anything about the predecessor, do not propagate just yet
398  // because we will have to recompute anyway once we compute the predecessor.
399  if (checks == nullptr) return NoChange();
400  // We just propagate the information from the effect input (ideally,
401  // we would only revisit effect uses if there is change).
402  return UpdateChecks(node, checks);
403 }
404 
405 Reduction RedundancyElimination::UpdateChecks(Node* node,
406  EffectPathChecks const* checks) {
407  EffectPathChecks const* original = node_checks_.Get(node);
408  // Only signal that the {node} has Changed, if the information about {checks}
409  // has changed wrt. the {original}.
410  if (checks != original) {
411  if (original == nullptr || !checks->Equals(original)) {
412  node_checks_.Set(node, checks);
413  return Changed(node);
414  }
415  }
416  return NoChange();
417 }
418 
419 } // namespace compiler
420 } // namespace internal
421 } // namespace v8
Definition: libplatform.h:13