v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
typed-optimization.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
7#include <optional>
8
17
18namespace v8 {
19namespace internal {
20namespace compiler {
21
23 CompilationDependencies* dependencies,
25 : AdvancedReducer(editor),
26 dependencies_(dependencies),
27 jsgraph_(jsgraph),
29 true_type_(Type::Constant(broker, broker->true_value(), graph()->zone())),
30 false_type_(
31 Type::Constant(broker, broker->false_value(), graph()->zone())),
32 type_cache_(TypeCache::Get()) {}
33
35
37 switch (node->opcode()) {
38 case IrOpcode::kConvertReceiver:
39 return ReduceConvertReceiver(node);
40 case IrOpcode::kMaybeGrowFastElements:
41 return ReduceMaybeGrowFastElements(node);
42 case IrOpcode::kCheckHeapObject:
43 return ReduceCheckHeapObject(node);
44 case IrOpcode::kCheckBounds:
45 return ReduceCheckBounds(node);
46 case IrOpcode::kCheckNotTaggedHole:
47 return ReduceCheckNotTaggedHole(node);
48 case IrOpcode::kCheckMaps:
49 return ReduceCheckMaps(node);
50 case IrOpcode::kCheckNumber:
51 return ReduceCheckNumber(node);
52 case IrOpcode::kCheckNumberFitsInt32:
53 return ReduceCheckNumberFitsInt32(node);
54 case IrOpcode::kCheckString:
55 return ReduceCheckString(node);
56 case IrOpcode::kCheckStringOrStringWrapper:
58 case IrOpcode::kCheckEqualsInternalizedString:
60 case IrOpcode::kCheckEqualsSymbol:
61 return ReduceCheckEqualsSymbol(node);
62 case IrOpcode::kLoadField:
63 return ReduceLoadField(node);
64 case IrOpcode::kNumberCeil:
65 case IrOpcode::kNumberRound:
66 case IrOpcode::kNumberTrunc:
67 return ReduceNumberRoundop(node);
68 case IrOpcode::kNumberFloor:
69 return ReduceNumberFloor(node);
70 case IrOpcode::kNumberSilenceNaN:
71 return ReduceNumberSilenceNaN(node);
72 case IrOpcode::kNumberToUint8Clamped:
73 return ReduceNumberToUint8Clamped(node);
74 case IrOpcode::kPhi:
75 return ReducePhi(node);
76 case IrOpcode::kReferenceEqual:
77 return ReduceReferenceEqual(node);
78 case IrOpcode::kStringEqual:
79 case IrOpcode::kStringLessThan:
80 case IrOpcode::kStringLessThanOrEqual:
81 return ReduceStringComparison(node);
82 case IrOpcode::kStringLength:
83 return ReduceStringLength(node);
84 case IrOpcode::kSameValue:
85 return ReduceSameValue(node);
86 case IrOpcode::kSelect:
87 return ReduceSelect(node);
88 case IrOpcode::kTypedArrayLength:
89 return ReduceTypedArrayLength(node);
90 case IrOpcode::kTypeOf:
91 return ReduceTypeOf(node);
92 case IrOpcode::kToBoolean:
93 return ReduceToBoolean(node);
94 case IrOpcode::kSpeculativeToNumber:
95 return ReduceSpeculativeToNumber(node);
96 case IrOpcode::kSpeculativeNumberAdd:
97 return ReduceSpeculativeNumberAdd(node);
98 case IrOpcode::kSpeculativeNumberSubtract:
99 case IrOpcode::kSpeculativeNumberMultiply:
100 case IrOpcode::kSpeculativeNumberPow:
101 case IrOpcode::kSpeculativeNumberDivide:
102 case IrOpcode::kSpeculativeNumberModulus:
103 return ReduceSpeculativeNumberBinop(node);
104 case IrOpcode::kSpeculativeNumberEqual:
105 case IrOpcode::kSpeculativeNumberLessThan:
106 case IrOpcode::kSpeculativeNumberLessThanOrEqual:
108 case IrOpcode::kTransitionElementsKindOrCheckMap:
110 default:
111 break;
112 }
113 return NoChange();
114}
115
116namespace {
117
118OptionalMapRef GetStableMapFromObjectType(JSHeapBroker* broker,
119 Type object_type) {
120 if (object_type.IsHeapConstant()) {
121 HeapObjectRef object = object_type.AsHeapConstant()->Ref();
122 MapRef object_map = object.map(broker);
123 if (object_map.is_stable()) return object_map;
124 }
125 return {};
126}
127
128Node* ResolveSameValueRenames(Node* node) {
129 while (true) {
130 switch (node->opcode()) {
131 case IrOpcode::kCheckHeapObject:
132 case IrOpcode::kCheckNumber:
133 case IrOpcode::kCheckNumberFitsInt32:
134 case IrOpcode::kCheckSmi:
135 case IrOpcode::kFinishRegion:
136 case IrOpcode::kTypeGuard:
137 if (node->IsDead()) {
138 return node;
139 } else {
140 node = node->InputAt(0);
141 continue;
142 }
143 default:
144 return node;
145 }
146 }
147}
148
149} // namespace
150
152 Node* const value = NodeProperties::GetValueInput(node, 0);
153 Type const value_type = NodeProperties::GetType(value);
154 Node* const global_proxy = NodeProperties::GetValueInput(node, 2);
155 if (value_type.Is(Type::Receiver())) {
156 ReplaceWithValue(node, value);
157 return Replace(value);
158 } else if (value_type.Is(Type::NullOrUndefined())) {
159 ReplaceWithValue(node, global_proxy);
160 return Replace(global_proxy);
161 }
162 return NoChange();
163}
164
166 Node* const input = NodeProperties::GetValueInput(node, 0);
167 Type const input_type = NodeProperties::GetType(input);
168 if (!input_type.Maybe(Type::SignedSmall())) {
169 ReplaceWithValue(node, input);
170 return Replace(input);
171 }
172 return NoChange();
173}
174
176 Node* const elements = NodeProperties::GetValueInput(node, 1);
177 Node* const index = NodeProperties::GetValueInput(node, 2);
178 Node* const length = NodeProperties::GetValueInput(node, 3);
179 Node* const effect = NodeProperties::GetEffectInput(node);
180 Node* const control = NodeProperties::GetControlInput(node);
181
182 Type const index_type = NodeProperties::GetType(index);
183 Type const length_type = NodeProperties::GetType(length);
184 CHECK(index_type.Is(Type::Unsigned31()));
185 CHECK(length_type.Is(Type::Unsigned31()));
186
187 if (!index_type.IsNone() && !length_type.IsNone() &&
188 index_type.Max() < length_type.Min()) {
189 if (v8_flags.turbo_typer_hardening) {
190 Node* check_bounds = graph()->NewNode(
193 index, length, effect, control);
194 ReplaceWithValue(node, elements, check_bounds);
195 return Replace(check_bounds);
196 } else {
197 RelaxEffectsAndControls(node);
198 return Replace(elements);
199 }
200 }
201
202 return NoChange();
203}
204
206 CheckBoundsParameters const& p = CheckBoundsParametersOf(node->op());
207 Node* const input = NodeProperties::GetValueInput(node, 0);
208 Type const input_type = NodeProperties::GetType(input);
210 !input_type.Maybe(Type::String()) &&
211 !input_type.Maybe(Type::MinusZero())) {
213 node,
215 p.check_parameters().feedback(),
217 return Changed(node);
218 }
219 return NoChange();
220}
221
223 Node* const input = NodeProperties::GetValueInput(node, 0);
224 Type const input_type = NodeProperties::GetType(input);
225 if (!input_type.Maybe(Type::Hole())) {
226 ReplaceWithValue(node, input);
227 return Replace(input);
228 }
229 return NoChange();
230}
231
232namespace {
233// The CheckMaps(o, ...map...) can be eliminated if map is stable,
234// o has type Constant(object) and map == object->map, and either
235// (1) map cannot transition further, or
236// (2) we can add a code dependency on the stability of map
237// (to guard the Constant type information).
238bool CheckMapsHelper(OptionalMapRef object_map, ZoneRefSet<Map> maps,
239 CompilationDependencies* dependencies) {
240 if (object_map.has_value()) {
241 for (MapRef map : maps) {
242 if (map.equals(*object_map)) {
243 if (object_map->CanTransition()) {
244 dependencies->DependOnStableMap(*object_map);
245 }
246 return true;
247 }
248 }
249 }
250 return false;
251}
252} // namespace
253
255 Node* const object = NodeProperties::GetValueInput(node, 0);
256 Type const object_type = NodeProperties::GetType(object);
257 Node* const effect = NodeProperties::GetEffectInput(node);
258 OptionalMapRef object_map = GetStableMapFromObjectType(broker(), object_type);
260 if (CheckMapsHelper(object_map, p.maps(), dependencies())) {
261 return Replace(effect);
262 }
263 return NoChange();
264}
265
267 Node* node) {
268 Node* const object = NodeProperties::GetValueInput(node, 0);
269 Type const object_type = NodeProperties::GetType(object);
270 Node* const effect = NodeProperties::GetEffectInput(node);
271 OptionalMapRef object_map = GetStableMapFromObjectType(broker(), object_type);
274 if (CheckMapsHelper(object_map, ZoneRefSet<Map>(p.target()),
275 dependencies())) {
276 return Replace(effect);
277 }
278 return NoChange();
279}
280
282 Node* const input = NodeProperties::GetValueInput(node, 0);
283 Type const input_type = NodeProperties::GetType(input);
284 if (input_type.Is(Type::Number())) {
285 ReplaceWithValue(node, input);
286 return Replace(input);
287 }
288 return NoChange();
289}
290
292 Node* const input = NodeProperties::GetValueInput(node, 0);
293 Type const input_type = NodeProperties::GetType(input);
294 if (input_type.Is(Type::Signed32())) {
295 ReplaceWithValue(node, input);
296 return Replace(input);
297 }
298 return NoChange();
299}
300
302 Node* const input = NodeProperties::GetValueInput(node, 0);
303 Type const input_type = NodeProperties::GetType(input);
304 if (input_type.Is(Type::String())) {
305 ReplaceWithValue(node, input);
306 return Replace(input);
307 }
308 return NoChange();
309}
310
312 Node* const input = NodeProperties::GetValueInput(node, 0);
313 Type const input_type = NodeProperties::GetType(input);
314 if (input_type.Is(Type::StringOrStringWrapper())) {
315 ReplaceWithValue(node, input);
316 return Replace(input);
317 }
318 return NoChange();
319}
320
322 Node* const exp = NodeProperties::GetValueInput(node, 0);
323 Type const exp_type = NodeProperties::GetType(exp);
324 Node* const val = NodeProperties::GetValueInput(node, 1);
325 Type const val_type = NodeProperties::GetType(val);
326 Node* const effect = NodeProperties::GetEffectInput(node);
327 if (val_type.Is(exp_type)) return Replace(effect);
328 // TODO(turbofan): Should we also try to optimize the
329 // non-internalized String case for {val} here?
330 return NoChange();
331}
332
334 Node* const exp = NodeProperties::GetValueInput(node, 0);
335 Type const exp_type = NodeProperties::GetType(exp);
336 Node* const val = NodeProperties::GetValueInput(node, 1);
337 Type const val_type = NodeProperties::GetType(val);
338 Node* const effect = NodeProperties::GetEffectInput(node);
339 if (val_type.Is(exp_type)) return Replace(effect);
340 return NoChange();
341}
342
344 Node* const object = NodeProperties::GetValueInput(node, 0);
345 Type const object_type = NodeProperties::GetType(object);
346 FieldAccess const& access = FieldAccessOf(node->op());
347 if (access.base_is_tagged == kTaggedBase &&
348 access.offset == HeapObject::kMapOffset) {
349 // We can replace LoadField[Map](o) with map if is stable, and
350 // o has type Constant(object) and map == object->map, and either
351 // (1) map cannot transition further, or
352 // (2) deoptimization is enabled and we can add a code dependency on the
353 // stability of map (to guard the Constant type information).
354 OptionalMapRef object_map =
355 GetStableMapFromObjectType(broker(), object_type);
356 if (object_map.has_value()) {
357 dependencies()->DependOnStableMap(*object_map);
358 Node* const value = jsgraph()->ConstantNoHole(*object_map, broker());
359 ReplaceWithValue(node, value);
360 return Replace(value);
361 }
362 }
363 return NoChange();
364}
365
367 Node* const input = NodeProperties::GetValueInput(node, 0);
368 Type const input_type = NodeProperties::GetType(input);
369 if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) {
370 return Replace(input);
371 }
372 if (input_type.Is(Type::PlainNumber()) &&
373 (input->opcode() == IrOpcode::kNumberDivide ||
374 input->opcode() == IrOpcode::kSpeculativeNumberDivide)) {
375 Node* const lhs = NodeProperties::GetValueInput(input, 0);
376 Type const lhs_type = NodeProperties::GetType(lhs);
377 Node* const rhs = NodeProperties::GetValueInput(input, 1);
378 Type const rhs_type = NodeProperties::GetType(rhs);
379 if (lhs_type.IsNone() || rhs_type.IsNone()) return NoChange();
380 if (lhs_type.Is(Type::Unsigned32()) && rhs_type.Is(Type::Unsigned32())) {
381 // We can replace
382 //
383 // NumberFloor(NumberDivide(lhs: unsigned32,
384 // rhs: unsigned32)): plain-number
385 //
386 // with
387 //
388 // Unsigned32Divide(lhs, rhs)
389 //
390 // and have the new node typed to [0...lhs.Max],
391 // as the truncated result must be lower than {lhs}'s maximum
392 // value (note that {rhs} cannot be less than 1 due to the
393 // plain-number type constraint on the {node}).
394 node = graph()->NewNode(simplified()->Unsigned32Divide(), lhs, rhs);
395 return Replace(node);
396 }
397 }
398 return NoChange();
399}
400
402 Node* const input = NodeProperties::GetValueInput(node, 0);
403 Type const input_type = NodeProperties::GetType(input);
404 if (input_type.Is(type_cache_->kIntegerOrMinusZeroOrNaN)) {
405 return Replace(input);
406 }
407 return NoChange();
408}
409
411 Node* const input = NodeProperties::GetValueInput(node, 0);
412 Type const input_type = NodeProperties::GetType(input);
413 if (input_type.Is(Type::OrderedNumber())) {
414 return Replace(input);
415 }
416 return NoChange();
417}
418
420 Node* const input = NodeProperties::GetValueInput(node, 0);
421 Type const input_type = NodeProperties::GetType(input);
422 if (input_type.Is(type_cache_->kUint8)) {
423 return Replace(input);
424 }
425 return NoChange();
426}
427
429 // Try to narrow the type of the Phi {node}, which might be more precise now
430 // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
431 // precise type than the JSAdd that was in the graph when the Typer was run.
432 DCHECK_EQ(IrOpcode::kPhi, node->opcode());
433 // Prevent new types from being propagated through loop-related Phis for now.
434 // This is to avoid slow convergence of type narrowing when we learn very
435 // precise information about loop variables.
436 if (NodeProperties::GetControlInput(node, 0)->opcode() == IrOpcode::kLoop) {
437 return NoChange();
438 }
439 int arity = node->op()->ValueInputCount();
440 Type type = NodeProperties::GetType(node->InputAt(0));
441 for (int i = 1; i < arity; ++i) {
442 type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
443 graph()->zone());
444 }
445 Type const node_type = NodeProperties::GetType(node);
446 if (!node_type.Is(type)) {
447 type = Type::Intersect(node_type, type, graph()->zone());
448 NodeProperties::SetType(node, type);
449 return Changed(node);
450 }
451 return NoChange();
452}
453
455 DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode());
456 Node* const lhs = NodeProperties::GetValueInput(node, 0);
457 Node* const rhs = NodeProperties::GetValueInput(node, 1);
458 Type const lhs_type = NodeProperties::GetType(lhs);
459 Type const rhs_type = NodeProperties::GetType(rhs);
460 if (!lhs_type.Maybe(rhs_type)) {
461 Node* replacement = jsgraph()->FalseConstant();
462 // Make sure we do not widen the type.
463 if (NodeProperties::GetType(replacement)
464 .Is(NodeProperties::GetType(node))) {
465 return Replace(jsgraph()->FalseConstant());
466 }
467 }
468 if (rhs_type.Is(Type::Boolean()) && rhs_type.IsHeapConstant() &&
469 lhs_type.Is(Type::Boolean())) {
470 std::optional<bool> maybe_result =
471 rhs_type.AsHeapConstant()->Ref().TryGetBooleanValue(broker());
472 if (maybe_result.has_value()) {
473 if (maybe_result.value()) {
474 return Replace(node->InputAt(0));
475 } else {
476 node->TrimInputCount(1);
477 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
478 return Changed(node);
479 }
480 }
481 }
482 return NoChange();
483}
484
486 switch (op->opcode()) {
487 case IrOpcode::kStringEqual:
488 return simplified()->NumberEqual();
489 case IrOpcode::kStringLessThan:
490 return simplified()->NumberLessThan();
491 case IrOpcode::kStringLessThanOrEqual:
493 default:
494 break;
495 }
496 UNREACHABLE();
497}
498
501 Node* comparison, StringRef string, bool inverted) {
502 switch (comparison->opcode()) {
503 case IrOpcode::kStringEqual:
504 if (string.length() != 1) {
505 // String.fromCharCode(x) always has length 1.
506 return Replace(jsgraph()->BooleanConstant(false));
507 }
508 break;
509 case IrOpcode::kStringLessThan:
510 [[fallthrough]];
511 case IrOpcode::kStringLessThanOrEqual:
512 if (string.length() == 0) {
513 // String.fromCharCode(x) <= "" is always false,
514 // "" < String.fromCharCode(x) is always true.
515 return Replace(jsgraph()->BooleanConstant(inverted));
516 }
517 break;
518 default:
519 UNREACHABLE();
520 }
521 return NoChange();
522}
523
524// Try to reduces a string comparison of the form
525// String.fromCharCode(x) {comparison} {constant} if inverted is false,
526// and {constant} {comparison} String.fromCharCode(x) if inverted is true.
529 Node* comparison, Node* from_char_code, Type constant_type, bool inverted) {
530 DCHECK_EQ(IrOpcode::kStringFromSingleCharCode, from_char_code->opcode());
531
532 if (!constant_type.IsHeapConstant()) return NoChange();
533 ObjectRef constant = constant_type.AsHeapConstant()->Ref();
534
535 if (!constant.IsString()) return NoChange();
536 StringRef string = constant.AsString();
537
538 // Check if comparison can be resolved statically.
540 comparison, string, inverted);
541 if (red.Changed()) return red;
542
543 const Operator* comparison_op = NumberComparisonFor(comparison->op());
544 Node* from_char_code_repl = NodeProperties::GetValueInput(from_char_code, 0);
545 Type from_char_code_repl_type = NodeProperties::GetType(from_char_code_repl);
546 if (!from_char_code_repl_type.Is(type_cache_->kUint16)) {
547 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
548 from_char_code_repl =
549 graph()->NewNode(simplified()->NumberToInt32(), from_char_code_repl);
550 from_char_code_repl = graph()->NewNode(
551 simplified()->NumberBitwiseAnd(), from_char_code_repl,
552 jsgraph()->ConstantNoHole(std::numeric_limits<uint16_t>::max()));
553 }
554 if (!string.GetFirstChar(broker()).has_value()) return NoChange();
555 Node* constant_repl =
556 jsgraph()->ConstantNoHole(string.GetFirstChar(broker()).value());
557
558 Node* number_comparison = nullptr;
559 if (inverted) {
560 // "x..." <= String.fromCharCode(z) is true if x < z.
561 if (string.length() > 1 &&
562 comparison->opcode() == IrOpcode::kStringLessThanOrEqual) {
563 comparison_op = simplified()->NumberLessThan();
564 }
565 number_comparison =
566 graph()->NewNode(comparison_op, constant_repl, from_char_code_repl);
567 } else {
568 // String.fromCharCode(z) < "x..." is true if z <= x.
569 if (string.length() > 1 &&
570 comparison->opcode() == IrOpcode::kStringLessThan) {
571 comparison_op = simplified()->NumberLessThanOrEqual();
572 }
573 number_comparison =
574 graph()->NewNode(comparison_op, from_char_code_repl, constant_repl);
575 }
576 ReplaceWithValue(comparison, number_comparison);
577 return Replace(number_comparison);
578}
579
581 DCHECK(IrOpcode::kStringEqual == node->opcode() ||
582 IrOpcode::kStringLessThan == node->opcode() ||
583 IrOpcode::kStringLessThanOrEqual == node->opcode());
584 Node* const lhs = NodeProperties::GetValueInput(node, 0);
585 Node* const rhs = NodeProperties::GetValueInput(node, 1);
586 Type lhs_type = NodeProperties::GetType(lhs);
587 Type rhs_type = NodeProperties::GetType(rhs);
588 if (lhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
589 if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
590 Node* left = NodeProperties::GetValueInput(lhs, 0);
591 Node* right = NodeProperties::GetValueInput(rhs, 0);
592 Type left_type = NodeProperties::GetType(left);
593 Type right_type = NodeProperties::GetType(right);
594 if (!left_type.Is(type_cache_->kUint16)) {
595 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
596 left = graph()->NewNode(simplified()->NumberToInt32(), left);
597 left = graph()->NewNode(
598 simplified()->NumberBitwiseAnd(), left,
599 jsgraph()->ConstantNoHole(std::numeric_limits<uint16_t>::max()));
600 }
601 if (!right_type.Is(type_cache_->kUint16)) {
602 // Convert to signed int32 to satisfy type of {NumberBitwiseAnd}.
603 right = graph()->NewNode(simplified()->NumberToInt32(), right);
604 right = graph()->NewNode(
605 simplified()->NumberBitwiseAnd(), right,
606 jsgraph()->ConstantNoHole(std::numeric_limits<uint16_t>::max()));
607 }
608 Node* equal =
609 graph()->NewNode(NumberComparisonFor(node->op()), left, right);
610 ReplaceWithValue(node, equal);
611 return Replace(equal);
612 } else {
614 node, lhs, rhs_type, false);
615 }
616 } else if (rhs->opcode() == IrOpcode::kStringFromSingleCharCode) {
618 lhs_type, true);
619 }
620 return NoChange();
621}
622
624 DCHECK_EQ(IrOpcode::kStringLength, node->opcode());
625 Node* const input = NodeProperties::GetValueInput(node, 0);
626 switch (input->opcode()) {
627 case IrOpcode::kHeapConstant: {
628 // Constant-fold the String::length of the {input}.
629 HeapObjectMatcher m(input);
630 if (m.Ref(broker()).IsString()) {
631 uint32_t const length = m.Ref(broker()).AsString().length();
632 Node* value = jsgraph()->ConstantNoHole(length);
633 return Replace(value);
634 }
635 break;
636 }
637 case IrOpcode::kStringConcat: {
638 // The first value input to the {input} is the resulting length.
639 return Replace(input->InputAt(0));
640 }
641 case IrOpcode::kStringFromSingleCharCode: {
642 // Note that this isn't valid for StringFromCodePointAt, since it the
643 // string it returns can be 1 or 2 characters long.
644 return Replace(jsgraph()->ConstantNoHole(1));
645 }
646 default:
647 break;
648 }
649 return NoChange();
650}
651
653 DCHECK_EQ(IrOpcode::kSameValue, node->opcode());
654 Node* const lhs = NodeProperties::GetValueInput(node, 0);
655 Node* const rhs = NodeProperties::GetValueInput(node, 1);
656 Type const lhs_type = NodeProperties::GetType(lhs);
657 Type const rhs_type = NodeProperties::GetType(rhs);
658 if (ResolveSameValueRenames(lhs) == ResolveSameValueRenames(rhs)) {
659 if (NodeProperties::GetType(node).IsNone()) {
660 return NoChange();
661 }
662 // SameValue(x,x) => #true
663 return Replace(jsgraph()->TrueConstant());
664 } else if (lhs_type.Is(Type::Unique()) && rhs_type.Is(Type::Unique())) {
665 // SameValue(x:unique,y:unique) => ReferenceEqual(x,y)
666 NodeProperties::ChangeOp(node, simplified()->ReferenceEqual());
667 return Changed(node);
668 } else if (lhs_type.Is(Type::String()) && rhs_type.Is(Type::String())) {
669 // SameValue(x:string,y:string) => StringEqual(x,y)
670 NodeProperties::ChangeOp(node, simplified()->StringEqual());
671 return Changed(node);
672 } else if (lhs_type.Is(Type::MinusZero())) {
673 // SameValue(x:minus-zero,y) => ObjectIsMinusZero(y)
674 node->RemoveInput(0);
675 NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
676 return Changed(node);
677 } else if (rhs_type.Is(Type::MinusZero())) {
678 // SameValue(x,y:minus-zero) => ObjectIsMinusZero(x)
679 node->RemoveInput(1);
680 NodeProperties::ChangeOp(node, simplified()->ObjectIsMinusZero());
681 return Changed(node);
682 } else if (lhs_type.Is(Type::NaN())) {
683 // SameValue(x:nan,y) => ObjectIsNaN(y)
684 node->RemoveInput(0);
685 NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
686 return Changed(node);
687 } else if (rhs_type.Is(Type::NaN())) {
688 // SameValue(x,y:nan) => ObjectIsNaN(x)
689 node->RemoveInput(1);
690 NodeProperties::ChangeOp(node, simplified()->ObjectIsNaN());
691 return Changed(node);
692 } else if (lhs_type.Is(Type::PlainNumber()) &&
693 rhs_type.Is(Type::PlainNumber())) {
694 // SameValue(x:plain-number,y:plain-number) => NumberEqual(x,y)
695 NodeProperties::ChangeOp(node, simplified()->NumberEqual());
696 return Changed(node);
697 }
698 return NoChange();
699}
700
702 DCHECK_EQ(IrOpcode::kSelect, node->opcode());
704 Type const condition_type = NodeProperties::GetType(condition);
705 Node* const vtrue = NodeProperties::GetValueInput(node, 1);
706 Type const vtrue_type = NodeProperties::GetType(vtrue);
707 Node* const vfalse = NodeProperties::GetValueInput(node, 2);
708 Type const vfalse_type = NodeProperties::GetType(vfalse);
709 if (condition_type.Is(true_type_)) {
710 // Select(condition:true, vtrue, vfalse) => vtrue
711 return Replace(vtrue);
712 }
713 if (condition_type.Is(false_type_)) {
714 // Select(condition:false, vtrue, vfalse) => vfalse
715 return Replace(vfalse);
716 }
717 if (vtrue_type.Is(true_type_) && vfalse_type.Is(false_type_)) {
718 // Select(condition, vtrue:true, vfalse:false) => condition
719 return Replace(condition);
720 }
721 if (vtrue_type.Is(false_type_) && vfalse_type.Is(true_type_)) {
722 // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
723 node->TrimInputCount(1);
724 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
725 return Changed(node);
726 }
727 // Try to narrow the type of the Select {node}, which might be more precise
728 // now after lowering based on types.
729 Type type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
730 Type const node_type = NodeProperties::GetType(node);
731 if (!node_type.Is(type)) {
732 type = Type::Intersect(node_type, type, graph()->zone());
733 NodeProperties::SetType(node, type);
734 return Changed(node);
735 }
736 return NoChange();
737}
738
740 DCHECK_EQ(IrOpcode::kSpeculativeToNumber, node->opcode());
741 Node* const input = NodeProperties::GetValueInput(node, 0);
742 Type const input_type = NodeProperties::GetType(input);
743 if (input_type.Is(Type::Number())) {
744 // SpeculativeToNumber(x:number) => x
745 ReplaceWithValue(node, input);
746 return Replace(input);
747 }
748 return NoChange();
749}
750
752 Node* const input = node->InputAt(0);
753 Type const type = NodeProperties::GetType(input);
754 if (type.Is(Type::Boolean())) {
755 return Replace(
756 jsgraph()->ConstantNoHole(broker()->boolean_string(), broker()));
757 } else if (type.Is(Type::Number())) {
758 return Replace(
759 jsgraph()->ConstantNoHole(broker()->number_string(), broker()));
760 } else if (type.Is(Type::String())) {
761 return Replace(
762 jsgraph()->ConstantNoHole(broker()->string_string(), broker()));
763 } else if (type.Is(Type::BigInt())) {
764 return Replace(
765 jsgraph()->ConstantNoHole(broker()->bigint_string(), broker()));
766 } else if (type.Is(Type::Symbol())) {
767 return Replace(
768 jsgraph()->ConstantNoHole(broker()->symbol_string(), broker()));
769 } else if (type.Is(Type::OtherUndetectableOrUndefined())) {
770 return Replace(
771 jsgraph()->ConstantNoHole(broker()->undefined_string(), broker()));
772 } else if (type.Is(Type::NonCallableOrNull())) {
773 return Replace(
774 jsgraph()->ConstantNoHole(broker()->object_string(), broker()));
775 } else if (type.Is(Type::Function())) {
776 return Replace(
777 jsgraph()->ConstantNoHole(broker()->function_string(), broker()));
778 }
779 return NoChange();
780}
781
783 DCHECK_EQ(IrOpcode::kTypedArrayLength, node->opcode());
784 Node* const input = NodeProperties::GetValueInput(node, 0);
785 // Constant-fold the length of the {input} if possible.
786 HeapObjectMatcher m(input);
787 if (m.HasResolvedValue() && m.Ref(broker()).IsJSTypedArray()) {
788 JSTypedArrayRef typed_array = m.Ref(broker()).AsJSTypedArray();
789 size_t byte_length = typed_array.byte_length();
790 ElementsKind elements_kind = typed_array.elements_kind(broker());
791 if (!IsRabGsabTypedArrayElementsKind(elements_kind)) {
792 Node* value = jsgraph()->ConstantNoHole(
793 byte_length >> ElementsKindToShiftSize(elements_kind));
794 return Replace(value);
795 }
796 }
797 return NoChange();
798}
799
801 Node* const input = node->InputAt(0);
802 Type const input_type = NodeProperties::GetType(input);
803 if (input_type.Is(Type::Boolean())) {
804 // ToBoolean(x:boolean) => x
805 return Replace(input);
806 } else if (input_type.Is(Type::OrderedNumber())) {
807 // SToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
808 node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
809 jsgraph()->ZeroConstant()));
810 node->TrimInputCount(1);
811 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
812 return Changed(node);
813 } else if (input_type.Is(Type::Number())) {
814 // ToBoolean(x:number) => NumberToBoolean(x)
815 node->TrimInputCount(1);
816 NodeProperties::ChangeOp(node, simplified()->NumberToBoolean());
817 return Changed(node);
818 } else if (input_type.Is(Type::DetectableReceiverOrNull())) {
819 // ToBoolean(x:detectable receiver \/ null)
820 // => BooleanNot(ReferenceEqual(x,#null))
821 node->ReplaceInput(0, graph()->NewNode(simplified()->ReferenceEqual(),
822 input, jsgraph()->NullConstant()));
823 node->TrimInputCount(1);
824 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
825 return Changed(node);
826 } else if (input_type.Is(Type::ReceiverOrNullOrUndefined())) {
827 // ToBoolean(x:receiver \/ null \/ undefined)
828 // => BooleanNot(ObjectIsUndetectable(x))
829 node->ReplaceInput(
830 0, graph()->NewNode(simplified()->ObjectIsUndetectable(), input));
831 node->TrimInputCount(1);
832 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
833 return Changed(node);
834 } else if (input_type.Is(Type::String())) {
835 // ToBoolean(x:string) => BooleanNot(ReferenceEqual(x,""))
836 node->ReplaceInput(0,
837 graph()->NewNode(simplified()->ReferenceEqual(), input,
838 jsgraph()->EmptyStringConstant()));
839 node->TrimInputCount(1);
840 NodeProperties::ChangeOp(node, simplified()->BooleanNot());
841 return Changed(node);
842 }
843 return NoChange();
844}
845
846namespace {
847bool BothAre(Type t1, Type t2, Type t3) { return t1.Is(t3) && t2.Is(t3); }
848
849bool NeitherCanBe(Type t1, Type t2, Type t3) {
850 return !t1.Maybe(t3) && !t2.Maybe(t3);
851}
852
853const Operator* NumberOpFromSpeculativeNumberOp(
854 SimplifiedOperatorBuilder* simplified, const Operator* op) {
855 switch (op->opcode()) {
856 case IrOpcode::kSpeculativeNumberEqual:
857 return simplified->NumberEqual();
858 case IrOpcode::kSpeculativeNumberLessThan:
859 return simplified->NumberLessThan();
860 case IrOpcode::kSpeculativeNumberLessThanOrEqual:
861 return simplified->NumberLessThanOrEqual();
862 case IrOpcode::kSpeculativeNumberAdd:
863 // Handled by ReduceSpeculativeNumberAdd.
864 UNREACHABLE();
865 case IrOpcode::kSpeculativeNumberSubtract:
866 return simplified->NumberSubtract();
867 case IrOpcode::kSpeculativeNumberMultiply:
868 return simplified->NumberMultiply();
869 case IrOpcode::kSpeculativeNumberPow:
870 return simplified->NumberPow();
871 case IrOpcode::kSpeculativeNumberDivide:
872 return simplified->NumberDivide();
873 case IrOpcode::kSpeculativeNumberModulus:
874 return simplified->NumberModulus();
875 default:
876 break;
877 }
878 UNREACHABLE();
879}
880
881} // namespace
882
884 Node* const lhs = NodeProperties::GetValueInput(node, 0);
885 Node* const rhs = NodeProperties::GetValueInput(node, 1);
886 Type const lhs_type = NodeProperties::GetType(lhs);
887 Type const rhs_type = NodeProperties::GetType(rhs);
889 if ((hint == NumberOperationHint::kNumber ||
891 BothAre(lhs_type, rhs_type, Type::PlainPrimitive()) &&
892 NeitherCanBe(lhs_type, rhs_type, Type::StringOrReceiver())) {
893 // SpeculativeNumberAdd(x:-string, y:-string) =>
894 // NumberAdd(ToNumber(x), ToNumber(y))
895 Node* const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs);
896 Node* const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs);
897 Node* const value =
898 graph()->NewNode(simplified()->NumberAdd(), toNum_lhs, toNum_rhs);
899 ReplaceWithValue(node, value);
900 return Replace(value);
901 }
902 return NoChange();
903}
904
906 // Try constant-folding of JSToNumber with constant inputs.
907 Type input_type = NodeProperties::GetType(input);
908
909 if (input_type.Is(Type::String())) {
910 HeapObjectMatcher m(input);
911 if (m.HasResolvedValue() && m.Ref(broker()).IsString()) {
912 StringRef input_value = m.Ref(broker()).AsString();
913 std::optional<double> number = input_value.ToNumber(broker());
914 if (!number.has_value()) return NoChange();
915 return Replace(jsgraph()->ConstantNoHole(number.value()));
916 }
917 }
918 if (input_type.IsHeapConstant()) {
919 HeapObjectRef input_value = input_type.AsHeapConstant()->Ref();
920 double value;
921 if (input_value.OddballToNumber(broker()).To(&value)) {
922 return Replace(jsgraph()->ConstantNoHole(value));
923 }
924 }
925 if (input_type.Is(Type::Number())) {
926 // JSToNumber(x:number) => x
927 return Changed(input);
928 }
929 if (input_type.Is(Type::Undefined())) {
930 // JSToNumber(undefined) => #NaN
931 return Replace(jsgraph()->NaNConstant());
932 }
933 if (input_type.Is(Type::Null())) {
934 // JSToNumber(null) => #0
935 return Replace(jsgraph()->ZeroConstant());
936 }
937 return NoChange();
938}
939
941 DCHECK(NodeProperties::GetType(node).Is(Type::PlainPrimitive()));
942 // Avoid inserting too many eager ToNumber() operations.
943 Reduction const reduction = ReduceJSToNumberInput(node);
944 if (reduction.Changed()) return reduction.replacement();
945 if (NodeProperties::GetType(node).Is(Type::Number())) {
946 return node;
947 }
948 return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), node);
949}
950
952 Node* const lhs = NodeProperties::GetValueInput(node, 0);
953 Node* const rhs = NodeProperties::GetValueInput(node, 1);
954 Type const lhs_type = NodeProperties::GetType(lhs);
955 Type const rhs_type = NodeProperties::GetType(rhs);
957 if ((hint == NumberOperationHint::kNumber ||
959 BothAre(lhs_type, rhs_type, Type::NumberOrOddball())) {
960 // We intentionally do this only in the Number and NumberOrOddball hint case
961 // because simplified lowering of these speculative ops may do some clever
962 // reductions in the other cases.
963 Node* const toNum_lhs = ConvertPlainPrimitiveToNumber(lhs);
964 Node* const toNum_rhs = ConvertPlainPrimitiveToNumber(rhs);
965 Node* const value = graph()->NewNode(
966 NumberOpFromSpeculativeNumberOp(simplified(), node->op()), toNum_lhs,
967 toNum_rhs);
968 ReplaceWithValue(node, value);
969 return Replace(value);
970 }
971 return NoChange();
972}
973
975 Node* const lhs = NodeProperties::GetValueInput(node, 0);
976 Node* const rhs = NodeProperties::GetValueInput(node, 1);
977 Type const lhs_type = NodeProperties::GetType(lhs);
978 Type const rhs_type = NodeProperties::GetType(rhs);
979 if (BothAre(lhs_type, rhs_type, Type::Signed32()) ||
980 BothAre(lhs_type, rhs_type, Type::Unsigned32())) {
981 Node* const value = graph()->NewNode(
982 NumberOpFromSpeculativeNumberOp(simplified(), node->op()), lhs, rhs);
983 ReplaceWithValue(node, value);
984 return Replace(value);
985 }
986 return NoChange();
987}
988
990 return jsgraph()->isolate()->factory();
991}
992
994
998
999} // namespace compiler
1000} // namespace internal
1001} // namespace v8
JSGraph * jsgraph
Flags without(flag_type flag) const
Definition flags.h:92
static constexpr int kMapOffset
v8::internal::Factory * factory()
Definition isolate.h:1527
const CheckParameters & check_parameters() const
V8_EXPORT_PRIVATE MapRef map(JSHeapBroker *broker) const
SimplifiedOperatorBuilder * simplified() const
Definition js-graph.h:105
Isolate * isolate() const
Definition js-graph.h:106
Node * ConstantNoHole(ObjectRef ref, JSHeapBroker *broker)
Definition js-graph.cc:51
ElementsKind elements_kind(JSHeapBroker *broker) const
static void ChangeOp(Node *node, const Operator *new_op)
static Type GetType(const Node *node)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetValueInput(Node *node, int index)
static void SetType(Node *node, Type type)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
const Operator * op() const
Definition node.h:50
Node * InputAt(int index) const
Definition node.h:70
constexpr Opcode opcode() const
Definition operator.h:75
std::optional< double > ToNumber(JSHeapBroker *broker)
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static Type Union(Type type1, Type type2, Zone *zone)
bool Maybe(Type that) const
const HeapConstantType * AsHeapConstant() const
static Type Intersect(Type type1, Type type2, Zone *zone)
bool Is(Type that) const
SimplifiedOperatorBuilder * simplified() const
TypedOptimization(Editor *editor, CompilationDependencies *dependencies, JSGraph *jsgraph, JSHeapBroker *broker)
Reduction ReduceTransitionElementsKindOrCheckMap(Node *node)
const Operator * NumberComparisonFor(const Operator *op)
Reduction TryReduceStringComparisonOfStringFromSingleCharCode(Node *comparison, Node *from_char_code, Type constant_type, bool inverted)
CompilationDependencies * dependencies() const
Reduction TryReduceStringComparisonOfStringFromSingleCharCodeToConstant(Node *comparison, StringRef string, bool inverted)
JSHeapBroker *const broker_
JSHeapBroker * broker
Node * node
int m
Definition mul-fft.cc:294
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
CheckMapsParameters const & CheckMapsParametersOf(Operator const *op)
NumberOperationHint NumberOperationHintOf(const Operator *op)
const FieldAccess & FieldAccessOf(const Operator *op)
CheckBoundsParameters const & CheckBoundsParametersOf(Operator const *op)
ElementsTransitionWithMultipleSources const & ElementsTransitionWithMultipleSourcesOf(const Operator *op)
bool IsNone(Tagged< FieldType > obj)
Definition field-type.h:50
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
bool IsRabGsabTypedArrayElementsKind(ElementsKind kind)
constexpr int ElementsKindToShiftSize(ElementsKind elements_kind)
int32_t NumberToInt32(Tagged< Object > number)
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485