v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
redundancy-elimination.cc
Go to the documentation of this file.
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
6
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
16 Zone* zone)
17 : AdvancedReducer(editor),
18 node_checks_(zone),
19 jsgraph_(jsgraph),
20 zone_(zone) {}
21
23
25 if (node_checks_.Get(node)) return NoChange();
26 switch (node->opcode()) {
27 case IrOpcode::kCheckBigInt:
28 case IrOpcode::kCheckedBigIntToBigInt64:
29 case IrOpcode::kCheckBounds:
30 case IrOpcode::kCheckClosure:
31 case IrOpcode::kCheckEqualsInternalizedString:
32 case IrOpcode::kCheckEqualsSymbol:
33 case IrOpcode::kCheckFloat64Hole:
34 case IrOpcode::kCheckHeapObject:
35 case IrOpcode::kCheckIf:
36 case IrOpcode::kCheckInternalizedString:
37 case IrOpcode::kCheckNotTaggedHole:
38 case IrOpcode::kCheckNumber:
39 case IrOpcode::kCheckNumberFitsInt32:
40 case IrOpcode::kCheckReceiver:
41 case IrOpcode::kCheckReceiverOrNullOrUndefined:
42 case IrOpcode::kCheckSmi:
43 case IrOpcode::kCheckString:
44 case IrOpcode::kCheckStringOrStringWrapper:
45 case IrOpcode::kCheckSymbol:
46 // These are not really check nodes, but behave the same in that they can be
47 // folded together if repeated with identical inputs.
48 case IrOpcode::kStringCharCodeAt:
49 case IrOpcode::kStringCodePointAt:
50 case IrOpcode::kStringFromCodePointAt:
51 case IrOpcode::kStringSubstring:
52#define SIMPLIFIED_OP(Opcode) case IrOpcode::k##Opcode:
55#undef SIMPLIFIED_OP
56 return ReduceCheckNode(node);
57 case IrOpcode::kSpeculativeNumberEqual:
58 case IrOpcode::kSpeculativeNumberLessThan:
59 case IrOpcode::kSpeculativeNumberLessThanOrEqual:
61 case IrOpcode::kSpeculativeNumberAdd:
62 case IrOpcode::kSpeculativeNumberSubtract:
63 case IrOpcode::kSpeculativeAdditiveSafeIntegerAdd:
64 case IrOpcode::kSpeculativeAdditiveSafeIntegerSubtract:
65 case IrOpcode::kSpeculativeSmallIntegerAdd:
66 case IrOpcode::kSpeculativeSmallIntegerSubtract:
67 case IrOpcode::kSpeculativeToNumber:
69 case IrOpcode::kEffectPhi:
70 return ReduceEffectPhi(node);
71 case IrOpcode::kDead:
72 break;
73 case IrOpcode::kStart:
74 return ReduceStart(node);
75 default:
76 return ReduceOtherNode(node);
77 }
78 return NoChange();
79}
80
81// static
87
88// static
93
95 EffectPathChecks const* that) const {
96 if (this->size_ != that->size_) return false;
97 Check* this_head = this->head_;
98 Check* that_head = that->head_;
99 while (this_head != that_head) {
100 if (this_head->node != that_head->node) return false;
101 this_head = this_head->next;
102 that_head = that_head->next;
103 }
104 return true;
105}
106
108 EffectPathChecks const* that) {
109 // Change the current check list to a longest common tail of this check
110 // list and the other list.
111
112 // First, we throw away the prefix of the longer list, so that
113 // we have lists of the same length.
114 Check* that_head = that->head_;
115 size_t that_size = that->size_;
116 while (that_size > size_) {
117 that_head = that_head->next;
118 that_size--;
119 }
120 while (size_ > that_size) {
121 head_ = head_->next;
122 size_--;
123 }
124
125 // Then we go through both lists in lock-step until we find
126 // the common tail.
127 while (head_ != that_head) {
128 DCHECK_LT(0u, size_);
129 DCHECK_NOT_NULL(head_);
130 size_--;
131 head_ = head_->next;
132 that_head = that_head->next;
133 }
134}
135
138 Node* node) const {
139 Check* head = zone->New<Check>(node, head_);
140 return zone->New<EffectPathChecks>(head, size_ + 1);
141}
142
143namespace {
144
145struct Subsumption {
146 enum class Kind {
147 kNone,
148 kImplicit,
149 kWithConversion,
150 };
151
152 static Subsumption None() { return Subsumption(Kind::kNone, nullptr); }
153 static Subsumption Implicit() {
154 return Subsumption(Kind::kImplicit, nullptr);
155 }
156 static Subsumption WithConversion(const Operator* conversion_op) {
157 return Subsumption(Kind::kWithConversion, conversion_op);
158 }
159
160 bool IsNone() const { return kind_ == Kind::kNone; }
161 bool IsImplicit() const { return kind_ == Kind::kImplicit; }
162 bool IsWithConversion() const { return kind_ == Kind::kWithConversion; }
163 const Operator* conversion_operator() const {
164 DCHECK(IsWithConversion());
165 return conversion_op_;
166 }
167
168 private:
169 Subsumption(Kind kind, const Operator* conversion_op)
170 : kind_(kind), conversion_op_(conversion_op) {
171 DCHECK_EQ(kind_ == Kind::kWithConversion, conversion_op_ != nullptr);
172 }
173
174 Kind kind_;
175 const Operator* conversion_op_;
176};
177
178// Does check {a} subsume check {b}?
179Subsumption CheckSubsumes(Node const* a, Node const* b,
180 MachineOperatorBuilder* machine) {
181 Subsumption subsumption = Subsumption::Implicit();
182 if (a->op() != b->op()) {
183 if (a->opcode() == IrOpcode::kCheckInternalizedString &&
184 b->opcode() == IrOpcode::kCheckString) {
185 // CheckInternalizedString(node) implies CheckString(node)
186 } else if (a->opcode() == IrOpcode::kCheckString &&
187 b->opcode() == IrOpcode::kCheckStringOrStringWrapper) {
188 // CheckString(node) implies CheckStringOrStringWrapper(node)
189 } else if (a->opcode() == IrOpcode::kCheckInternalizedString &&
190 b->opcode() == IrOpcode::kCheckStringOrStringWrapper) {
191 // CheckInteralizedString(node) implies CheckStringOrStringWrapper(node)
192 } else if (a->opcode() == IrOpcode::kCheckSmi &&
193 b->opcode() == IrOpcode::kCheckNumber) {
194 // CheckSmi(node) implies CheckNumber(node)
195 } else if (a->opcode() == IrOpcode::kCheckSmi &&
196 b->opcode() == IrOpcode::kCheckNumberFitsInt32) {
197 // CheckSmi(node) implies CheckNumberFitsInt32(node)
198 } else if (a->opcode() == IrOpcode::kCheckNumberFitsInt32 &&
199 b->opcode() == IrOpcode::kCheckNumber) {
200 // CheckNumberFitsInt32(node) implies CheckNumber(node)
201 } else if (a->opcode() == IrOpcode::kCheckedTaggedSignedToInt32 &&
202 b->opcode() == IrOpcode::kCheckedTaggedToInt32) {
203 // CheckedTaggedSignedToInt32(node) implies CheckedTaggedToInt32(node)
204 } else if (a->opcode() == IrOpcode::kCheckedTaggedSignedToInt32 &&
205 b->opcode() == IrOpcode::kCheckedTaggedToArrayIndex) {
206 // CheckedTaggedSignedToInt32(node) implies
207 // CheckedTaggedToArrayIndex(node)
208 if (machine->Is64()) {
209 // On 64 bit architectures, ArrayIndex is 64 bit.
210 subsumption =
211 Subsumption::WithConversion(machine->ChangeInt32ToInt64());
212 }
213 } else if (a->opcode() == IrOpcode::kCheckedTaggedToInt32 &&
214 b->opcode() == IrOpcode::kCheckedTaggedToArrayIndex) {
215 // CheckedTaggedToInt32(node) implies CheckedTaggedToArrayIndex(node)
216 if (machine->Is64()) {
217 // On 64 bit architectures, ArrayIndex is 64 bit.
218 subsumption =
219 Subsumption::WithConversion(machine->ChangeInt32ToInt64());
220 }
221 } else if (a->opcode() == IrOpcode::kCheckReceiver &&
222 b->opcode() == IrOpcode::kCheckReceiverOrNullOrUndefined) {
223 // CheckReceiver(node) implies CheckReceiverOrNullOrUndefined(node)
224 } else if (a->opcode() != b->opcode()) {
225 return Subsumption::None();
226 } else {
227 switch (a->opcode()) {
228 case IrOpcode::kCheckBounds:
229 case IrOpcode::kCheckSmi:
230 case IrOpcode::kCheckString:
231 case IrOpcode::kCheckStringOrStringWrapper:
232 case IrOpcode::kCheckNumber:
233 case IrOpcode::kCheckNumberFitsInt32:
234 case IrOpcode::kCheckBigInt:
235 case IrOpcode::kCheckedBigIntToBigInt64:
236 break;
237 case IrOpcode::kCheckedInt32ToTaggedSigned:
238 case IrOpcode::kCheckedInt64ToInt32:
239 case IrOpcode::kCheckedInt64ToTaggedSigned:
240 case IrOpcode::kCheckedTaggedSignedToInt32:
241 case IrOpcode::kCheckedTaggedToTaggedPointer:
242 case IrOpcode::kCheckedTaggedToTaggedSigned:
243 case IrOpcode::kCheckedTaggedToArrayIndex:
244 case IrOpcode::kCheckedUint32Bounds:
245 case IrOpcode::kCheckedUint32ToInt32:
246 case IrOpcode::kCheckedUint32ToTaggedSigned:
247 case IrOpcode::kCheckedUint64Bounds:
248 case IrOpcode::kCheckedUint64ToInt32:
249 case IrOpcode::kCheckedUint64ToTaggedSigned:
250 break;
251 case IrOpcode::kCheckedFloat64ToInt32:
252 case IrOpcode::kCheckedFloat64ToAdditiveSafeInteger:
253 case IrOpcode::kCheckedFloat64ToInt64:
254 case IrOpcode::kCheckedTaggedToInt32:
255 case IrOpcode::kCheckedTaggedToAdditiveSafeInteger:
256 case IrOpcode::kCheckedTaggedToInt64: {
257 const CheckMinusZeroParameters& ap =
259 const CheckMinusZeroParameters& bp =
261 if (ap.mode() != bp.mode()) {
262 return Subsumption::None();
263 }
264 break;
265 }
266 case IrOpcode::kCheckedTaggedToFloat64:
267 case IrOpcode::kCheckedTruncateTaggedToWord32: {
268 CheckTaggedInputParameters const& ap =
270 CheckTaggedInputParameters const& bp =
272 // {a} subsumes {b} if the modes are either the same, or {a} checks
273 // for Number, in which case {b} will be subsumed no matter what.
274 if (ap.mode() != bp.mode() &&
275 ap.mode() != CheckTaggedInputMode::kNumber) {
276 return Subsumption::None();
277 }
278 break;
279 }
280 default:
281 DCHECK(!IsCheckedWithFeedback(a->op()));
282 return Subsumption::None();
283 }
284 }
285 }
286 for (int i = a->op()->ValueInputCount(); --i >= 0;) {
287 if (a->InputAt(i) != b->InputAt(i)) return Subsumption::None();
288 }
289 return subsumption;
290}
291
292bool TypeSubsumes(Node* node, Node* replacement) {
293 if (!NodeProperties::IsTyped(node) || !NodeProperties::IsTyped(replacement)) {
294 // If either node is untyped, we are running during an untyped optimization
295 // phase, and replacement is OK.
296 return true;
297 }
298 Type node_type = NodeProperties::GetType(node);
299 Type replacement_type = NodeProperties::GetType(replacement);
300 return replacement_type.Is(node_type);
301}
302
303} // namespace
304
306 Node* node, JSGraph* jsgraph) const {
307 for (Check const* check = head_; check != nullptr; check = check->next) {
308 Subsumption subsumption =
309 CheckSubsumes(check->node, node, jsgraph->machine());
310 if (!subsumption.IsNone() && TypeSubsumes(node, check->node)) {
311 DCHECK(!check->node->IsDead());
312 Node* result = check->node;
313 if (subsumption.IsWithConversion()) {
314 result = jsgraph->graph()->NewNode(subsumption.conversion_operator(),
315 result);
316 }
317 return result;
318 }
319 }
320 return nullptr;
321}
322
324 Node* node) const {
325 for (Check const* check = head_; check != nullptr; check = check->next) {
326 if (check->node->opcode() == IrOpcode::kCheckBounds &&
327 check->node->InputAt(0) == node && TypeSubsumes(node, check->node) &&
328 !(CheckBoundsParametersOf(check->node->op()).flags() &
330 return check->node;
331 }
332 }
333 return nullptr;
334}
335
338 size_t const id = node->id();
339 if (id < info_for_node_.size()) return info_for_node_[id];
340 return nullptr;
341}
342
344 Node* node, EffectPathChecks const* checks) {
345 size_t const id = node->id();
346 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
347 info_for_node_[id] = checks;
348}
349
351 Node* const effect = NodeProperties::GetEffectInput(node);
352 EffectPathChecks const* checks = node_checks_.Get(effect);
353 // If we do not know anything about the predecessor, do not propagate just yet
354 // because we will have to recompute anyway once we compute the predecessor.
355 if (checks == nullptr) return NoChange();
356 // See if we have another check that dominates us.
357 if (Node* check = checks->LookupCheck(node, jsgraph_)) {
358 ReplaceWithValue(node, check);
359 return Replace(check);
360 }
361
362 // Learn from this check.
363 return UpdateChecks(node, checks->AddCheck(zone(), node));
364}
365
367 Node* const control = NodeProperties::GetControlInput(node);
368 if (control->opcode() == IrOpcode::kLoop) {
369 // Here we rely on having only reducible loops:
370 // The loop entry edge always dominates the header, so we can just use
371 // the information from the loop entry edge.
372 return TakeChecksFromFirstEffect(node);
373 }
374 DCHECK_EQ(IrOpcode::kMerge, control->opcode());
375
376 // Shortcut for the case when we do not know anything about some input.
377 int const input_count = node->op()->EffectInputCount();
378 for (int i = 0; i < input_count; ++i) {
379 Node* const effect = NodeProperties::GetEffectInput(node, i);
380 if (node_checks_.Get(effect) == nullptr) return NoChange();
381 }
382
383 // Make a copy of the first input's checks and merge with the checks
384 // from other inputs.
387 for (int i = 1; i < input_count; ++i) {
388 Node* const input = NodeProperties::GetEffectInput(node, i);
389 checks->Merge(node_checks_.Get(input));
390 }
391 return UpdateChecks(node, checks);
392}
393
395 NumberOperationHint const hint = NumberOperationHintOf(node->op());
396 Node* const first = NodeProperties::GetValueInput(node, 0);
397 Type const first_type = NodeProperties::GetType(first);
398 Node* const second = NodeProperties::GetValueInput(node, 1);
399 Type const second_type = NodeProperties::GetType(second);
400 Node* const effect = NodeProperties::GetEffectInput(node);
401 EffectPathChecks const* checks = node_checks_.Get(effect);
402
403 // If we do not know anything about the predecessor, do not propagate just yet
404 // because we will have to recompute anyway once we compute the predecessor.
405 if (checks == nullptr) return NoChange();
406
407 // Avoid the potentially expensive lookups below if the {node}
408 // has seen non-Smi inputs in the past, which is a clear signal
409 // that the comparison is probably not performed on a value that
410 // already passed an array bounds check.
412 // Don't bother trying to find a CheckBounds for the {first} input
413 // if it's type is already in UnsignedSmall range, since the bounds
414 // check is only going to narrow that range further, but the result
415 // is not going to make the representation selection any better.
416 if (!first_type.Is(Type::UnsignedSmall())) {
417 if (Node* check = checks->LookupBoundsCheckFor(first)) {
418 if (!first_type.Is(NodeProperties::GetType(check))) {
419 // Replace the {first} input with the {check}. This is safe,
420 // despite the fact that {check} can truncate -0 to 0, because
421 // the regular Number comparisons in JavaScript also identify
422 // 0 and -0 (unlike special comparisons as Object.is).
423 NodeProperties::ReplaceValueInput(node, check, 0);
424 return Changed(node).FollowedBy(
426 }
427 }
428 }
429
430 // Don't bother trying to find a CheckBounds for the {second} input
431 // if it's type is already in UnsignedSmall range, since the bounds
432 // check is only going to narrow that range further, but the result
433 // is not going to make the representation selection any better.
434 if (!second_type.Is(Type::UnsignedSmall())) {
435 if (Node* check = checks->LookupBoundsCheckFor(second)) {
436 if (!second_type.Is(NodeProperties::GetType(check))) {
437 // Replace the {second} input with the {check}. This is safe,
438 // despite the fact that {check} can truncate -0 to 0, because
439 // the regular Number comparisons in JavaScript also identify
440 // 0 and -0 (unlike special comparisons as Object.is).
441 NodeProperties::ReplaceValueInput(node, check, 1);
442 return Changed(node).FollowedBy(
444 }
445 }
446 }
447 }
448
449 return UpdateChecks(node, checks);
450}
451
453 DCHECK(node->opcode() == IrOpcode::kSpeculativeNumberAdd ||
454 node->opcode() == IrOpcode::kSpeculativeNumberSubtract ||
455 node->opcode() == IrOpcode::kSpeculativeAdditiveSafeIntegerAdd ||
456 node->opcode() == IrOpcode::kSpeculativeAdditiveSafeIntegerSubtract ||
457 node->opcode() == IrOpcode::kSpeculativeSmallIntegerAdd ||
458 node->opcode() == IrOpcode::kSpeculativeSmallIntegerSubtract ||
459 node->opcode() == IrOpcode::kSpeculativeToNumber);
460 DCHECK_EQ(1, node->op()->EffectInputCount());
461 DCHECK_EQ(1, node->op()->EffectOutputCount());
462
463 Node* const first = NodeProperties::GetValueInput(node, 0);
464 Node* const effect = NodeProperties::GetEffectInput(node);
465 EffectPathChecks const* checks = node_checks_.Get(effect);
466 // If we do not know anything about the predecessor, do not propagate just yet
467 // because we will have to recompute anyway once we compute the predecessor.
468 if (checks == nullptr) return NoChange();
469
470 // Check if there's a CheckBounds operation on {first}
471 // in the graph already, which we might be able to
472 // reuse here to improve the representation selection
473 // for the {node} later on.
474 if (Node* check = checks->LookupBoundsCheckFor(first)) {
475 // Only use the bounds {check} if its type is better
476 // than the type of the {first} node, otherwise we
477 // would end up replacing NumberConstant inputs with
478 // CheckBounds operations, which is kind of pointless.
480 NodeProperties::ReplaceValueInput(node, check, 0);
481 }
482 }
483
484 return UpdateChecks(node, checks);
485}
486
490
492 if (node->op()->EffectInputCount() == 1) {
493 if (node->op()->EffectOutputCount() == 1) {
494 return TakeChecksFromFirstEffect(node);
495 } else {
496 // Effect terminators should be handled specially.
497 return NoChange();
498 }
499 }
500 DCHECK_EQ(0, node->op()->EffectInputCount());
501 DCHECK_EQ(0, node->op()->EffectOutputCount());
502 return NoChange();
503}
504
506 DCHECK_EQ(1, node->op()->EffectOutputCount());
507 Node* const effect = NodeProperties::GetEffectInput(node);
508 EffectPathChecks const* checks = node_checks_.Get(effect);
509 // If we do not know anything about the predecessor, do not propagate just yet
510 // because we will have to recompute anyway once we compute the predecessor.
511 if (checks == nullptr) return NoChange();
512 // We just propagate the information from the effect input (ideally,
513 // we would only revisit effect uses if there is change).
514 return UpdateChecks(node, checks);
515}
516
518 EffectPathChecks const* checks) {
519 EffectPathChecks const* original = node_checks_.Get(node);
520 // Only signal that the {node} has Changed, if the information about {checks}
521 // has changed wrt. the {original}.
522 if (checks != original) {
523 if (original == nullptr || !checks->Equals(original)) {
524 node_checks_.Set(node, checks);
525 return Changed(node);
526 }
527 }
528 return NoChange();
529}
530
531} // namespace compiler
532} // namespace internal
533} // namespace v8
JSGraph * jsgraph
Builtins::Kind kind
Definition builtins.cc:40
T * New(Args &&... args)
Definition zone.h:114
void ReplaceWithValue(Node *node, Node *value, Node *effect=nullptr, Node *control=nullptr)
static Reduction Replace(Node *node)
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static void ReplaceValueInput(Node *node, Node *value, int index)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
static Reduction Changed(Node *node)
Reduction FollowedBy(Reduction next) const
EffectPathChecks const * AddCheck(Zone *zone, Node *node) const
static EffectPathChecks * Copy(Zone *zone, EffectPathChecks const *checks)
RedundancyElimination(Editor *editor, JSGraph *jsgraph, Zone *zone)
Reduction UpdateChecks(Node *node, EffectPathChecks const *checks)
bool Is(Type that) const
Zone * zone_
const int size_
Definition assembler.cc:132
const PropertyKind kind_
Node * node
double second
ZoneVector< RpoNumber > & result
const CheckTaggedInputParameters & CheckTaggedInputParametersOf(const Operator *op)
const CheckMinusZeroParameters & CheckMinusZeroParametersOf(const Operator *op)
NumberOperationHint NumberOperationHintOf(const Operator *op)
CheckBoundsParameters const & CheckBoundsParametersOf(Operator const *op)
bool IsCheckedWithFeedback(const Operator *op)
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
#define SIMPLIFIED_BIGINT_BINOP_LIST(V)
Definition opcodes.h:367
#define SIMPLIFIED_CHECKED_OP_LIST(V)
Definition opcodes.h:290
#define SIMPLIFIED_OP(Opcode)
const Operator * conversion_op_
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485