v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
machine-operator-reducer.cc
Go to the documentation of this file.
1// Copyright 2014 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 <cmath>
8#include <cstdint>
9#include <limits>
10#include <optional>
11
12#include "src/base/bits.h"
14#include "src/base/ieee754.h"
15#include "src/base/logging.h"
26#include "src/numbers/ieee754.h"
27
28namespace v8 {
29namespace internal {
30namespace compiler {
31
32// Some optimizations performed by the MachineOperatorReducer can be applied
33// to both Word32 and Word64 operations. Those are implemented in a generic
34// way to be reused for different word sizes.
35// This class adapts a generic algorithm to Word32 operations.
37 public:
40 using intN_t = int32_t;
41 using uintN_t = uint32_t;
42 // WORD_SIZE refers to the N for which this adapter specializes.
43 static constexpr std::size_t WORD_SIZE = 32;
44
45 explicit Word32Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
46
47 template <typename T>
48 static bool IsWordNAnd(const T& x) {
49 return x.IsWord32And();
50 }
51 template <typename T>
52 static bool IsWordNShl(const T& x) {
53 return x.IsWord32Shl();
54 }
55 template <typename T>
56 static bool IsWordNShr(const T& x) {
57 return x.IsWord32Shr();
58 }
59 template <typename T>
60 static bool IsWordNSar(const T& x) {
61 return x.IsWord32Sar();
62 }
63 static bool IsWordNSarShiftOutZeros(const Operator* op) {
64 return op->opcode() == IrOpcode::kWord32Sar &&
66 }
67 template <typename T>
68 static bool IsWordNXor(const T& x) {
69 return x.IsWord32Xor();
70 }
71 template <typename T>
72 static bool IsIntNAdd(const T& x) {
73 return x.IsInt32Add();
74 }
75 template <typename T>
76 static bool IsIntNMul(const T& x) {
77 return x.IsInt32Mul();
78 }
79
81 return machine->Int32Add();
82 }
83 static const Operator* WordNEqual(MachineOperatorBuilder* machine) {
84 return machine->Word32Equal();
85 }
86
87 Reduction ReplaceIntN(int32_t value) { return r_->ReplaceInt32(value); }
89 Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt32Add(node); }
91
92 Node* IntNConstant(int32_t value) { return r_->Int32Constant(value); }
93 Node* UintNConstant(uint32_t value) { return r_->Uint32Constant(value); }
94 Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word32And(lhs, rhs); }
95
99
100 private:
102};
103
104// Some optimizations performed by the MachineOperatorReducer can be applied
105// to both Word32 and Word64 operations. Those are implemented in a generic
106// way to be reused for different word sizes.
107// This class adapts a generic algorithm to Word64 operations.
109 public:
112 using intN_t = int64_t;
113 using uintN_t = uint64_t;
114 // WORD_SIZE refers to the N for which this adapter specializes.
115 static constexpr std::size_t WORD_SIZE = 64;
116
117 explicit Word64Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
118
119 template <typename T>
120 static bool IsWordNAnd(const T& x) {
121 return x.IsWord64And();
122 }
123 template <typename T>
124 static bool IsWordNShl(const T& x) {
125 return x.IsWord64Shl();
126 }
127 template <typename T>
128 static bool IsWordNShr(const T& x) {
129 return x.IsWord64Shr();
130 }
131 template <typename T>
132 static bool IsWordNSar(const T& x) {
133 return x.IsWord64Sar();
134 }
135 static bool IsWordNSarShiftOutZeros(const Operator* op) {
136 return op->opcode() == IrOpcode::kWord64Sar &&
138 }
139 template <typename T>
140 static bool IsWordNXor(const T& x) {
141 return x.IsWord64Xor();
142 }
143 template <typename T>
144 static bool IsIntNAdd(const T& x) {
145 return x.IsInt64Add();
146 }
147 template <typename T>
148 static bool IsIntNMul(const T& x) {
149 return x.IsInt64Mul();
150 }
151
152 static const Operator* IntNAdd(MachineOperatorBuilder* machine) {
153 return machine->Int64Add();
154 }
156 return machine->Word64Equal();
157 }
158
159 Reduction ReplaceIntN(int64_t value) { return r_->ReplaceInt64(value); }
161 Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt64Add(node); }
163 // TODO(nicohartmann@): Add a MachineOperatorReducer::TryMatchWord64Ror.
164 return r_->NoChange();
165 }
166
167 Node* IntNConstant(int64_t value) { return r_->Int64Constant(value); }
168 Node* UintNConstant(uint64_t value) { return r_->Uint64Constant(value); }
169 Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word64And(lhs, rhs); }
170
174
175 private:
177};
178
179namespace {
180
181// TODO(jgruber): Consider replacing all uses of this function by
182// std::numeric_limits<T>::quiet_NaN().
183template <class T>
184T SilenceNaN(T x) {
185 DCHECK(std::isnan(x));
186 // Do some calculation to make a signalling NaN quiet.
187 return x - x;
188}
189
190} // namespace
191
193 Editor* editor, MachineGraph* mcgraph,
194 SignallingNanPropagation signalling_nan_propagation)
195 : AdvancedReducer(editor),
196 mcgraph_(mcgraph),
197 signalling_nan_propagation_(signalling_nan_propagation) {}
198
200
204
206 return mcgraph()->Float64Constant(value);
207}
208
210 return mcgraph()->Int32Constant(value);
211}
212
214 return graph()->NewNode(common()->Int64Constant(value));
215}
216
218 return graph()->NewNode(machine()->Float64Mul(), lhs, rhs);
219}
220
222 value =
223 graph()->NewNode(machine()->Float64Add(), Float64Constant(0.0), value);
224 Diamond d(graph(), common(),
225 graph()->NewNode(machine()->Float64LessThanOrEqual(), value,
229 graph()->NewNode(machine()->Float64Sqrt(), value));
230}
231
233 Node* const node = graph()->NewNode(machine()->Word32And(), lhs, rhs);
234 Reduction const reduction = ReduceWord32And(node);
235 return reduction.Changed() ? reduction.replacement() : node;
236}
237
239 if (rhs == 0) return lhs;
240 return graph()->NewNode(machine()->Word32Sar(), lhs, Uint32Constant(rhs));
241}
242
244 if (rhs == 0) return lhs;
245 return graph()->NewNode(machine()->Word64Sar(), lhs, Uint64Constant(rhs));
246}
247
249 if (rhs == 0) return lhs;
250 return graph()->NewNode(machine()->Word32Shr(), lhs, Uint32Constant(rhs));
251}
252
254 if (rhs == 0) return lhs;
255 return graph()->NewNode(machine()->Word64Shr(), lhs, Uint64Constant(rhs));
256}
257
259 return graph()->NewNode(machine()->Word32Equal(), lhs, rhs);
260}
261
263 return graph()->NewNode(machine()->Word64Equal(), lhs, rhs);
264}
265
267 Node* const node = graph()->NewNode(machine()->Word64And(), lhs, rhs);
268 Reduction const reduction = ReduceWord64And(node);
269 return reduction.Changed() ? reduction.replacement() : node;
270}
271
273 Node* const node = graph()->NewNode(machine()->Int32Add(), lhs, rhs);
274 Reduction const reduction = ReduceInt32Add(node);
275 return reduction.Changed() ? reduction.replacement() : node;
276}
277
279 Node* const node = graph()->NewNode(machine()->Int64Add(), lhs, rhs);
280 Reduction const reduction = ReduceInt64Add(node);
281 return reduction.Changed() ? reduction.replacement() : node;
282}
283
285 Node* const node = graph()->NewNode(machine()->Int32Sub(), lhs, rhs);
286 Reduction const reduction = ReduceInt32Sub(node);
287 return reduction.Changed() ? reduction.replacement() : node;
288}
289
291 Node* const node = graph()->NewNode(machine()->Int64Sub(), lhs, rhs);
292 Reduction const reduction = ReduceInt64Sub(node);
293 return reduction.Changed() ? reduction.replacement() : node;
294}
295
297 return graph()->NewNode(machine()->Int32Mul(), lhs, rhs);
298}
299
301 return graph()->NewNode(machine()->Int64Mul(), lhs, rhs);
302}
303
304Node* MachineOperatorReducer::Int32Div(Node* dividend, int32_t divisor) {
305 DCHECK_NE(0, divisor);
306 DCHECK_NE(std::numeric_limits<int32_t>::min(), divisor);
307 base::MagicNumbersForDivision<uint32_t> const mag =
309 Node* quotient = graph()->NewNode(machine()->Int32MulHigh(), dividend,
310 Uint32Constant(mag.multiplier));
311 if (divisor > 0 && base::bit_cast<int32_t>(mag.multiplier) < 0) {
312 quotient = Int32Add(quotient, dividend);
313 } else if (divisor < 0 && base::bit_cast<int32_t>(mag.multiplier) > 0) {
314 quotient = Int32Sub(quotient, dividend);
315 }
316 return Int32Add(Word32Sar(quotient, mag.shift), Word32Shr(dividend, 31));
317}
318
319Node* MachineOperatorReducer::Int64Div(Node* dividend, int64_t divisor) {
320 DCHECK_NE(0, divisor);
321 DCHECK_NE(std::numeric_limits<int64_t>::min(), divisor);
322 base::MagicNumbersForDivision<uint64_t> const mag =
324 Node* quotient = graph()->NewNode(machine()->Int64MulHigh(), dividend,
325 Uint64Constant(mag.multiplier));
326 if (divisor > 0 && base::bit_cast<int64_t>(mag.multiplier) < 0) {
327 quotient = Int64Add(quotient, dividend);
328 } else if (divisor < 0 && base::bit_cast<int64_t>(mag.multiplier) > 0) {
329 quotient = Int64Sub(quotient, dividend);
330 }
331 return Int64Add(Word64Sar(quotient, mag.shift), Word64Shr(dividend, 63));
332}
333
334Node* MachineOperatorReducer::Uint32Div(Node* dividend, uint32_t divisor) {
335 DCHECK_LT(0u, divisor);
336 // If the divisor is even, we can avoid using the expensive fixup by shifting
337 // the dividend upfront.
338 unsigned const shift = base::bits::CountTrailingZeros(divisor);
339 dividend = Word32Shr(dividend, shift);
340 divisor >>= shift;
341 // Compute the magic number for the (shifted) divisor.
342 base::MagicNumbersForDivision<uint32_t> const mag =
343 base::UnsignedDivisionByConstant(divisor, shift);
344 Node* quotient = graph()->NewNode(machine()->Uint32MulHigh(), dividend,
345 Uint32Constant(mag.multiplier));
346 if (mag.add) {
347 DCHECK_LE(1u, mag.shift);
348 quotient = Word32Shr(
349 Int32Add(Word32Shr(Int32Sub(dividend, quotient), 1), quotient),
350 mag.shift - 1);
351 } else {
352 quotient = Word32Shr(quotient, mag.shift);
353 }
354 return quotient;
355}
356
357Node* MachineOperatorReducer::Uint64Div(Node* dividend, uint64_t divisor) {
358 DCHECK_LT(0u, divisor);
359 // If the divisor is even, we can avoid using the expensive fixup by shifting
360 // the dividend upfront.
361 unsigned const shift = base::bits::CountTrailingZeros(divisor);
362 dividend = Word64Shr(dividend, shift);
363 divisor >>= shift;
364 // Compute the magic number for the (shifted) divisor.
365 base::MagicNumbersForDivision<uint64_t> const mag =
366 base::UnsignedDivisionByConstant(divisor, shift);
367 Node* quotient = graph()->NewNode(machine()->Uint64MulHigh(), dividend,
368 Uint64Constant(mag.multiplier));
369 if (mag.add) {
370 DCHECK_LE(1u, mag.shift);
371 quotient = Word64Shr(
372 Int64Add(Word64Shr(Int64Sub(dividend, quotient), 1), quotient),
373 mag.shift - 1);
374 } else {
375 quotient = Word64Shr(quotient, mag.shift);
376 }
377 return quotient;
378}
379
381 Node* const node = graph()->NewNode(machine()->TruncateInt64ToInt32(), value);
382 Reduction const reduction = ReduceTruncateInt64ToInt32(node);
383 return reduction.Changed() ? reduction.replacement() : node;
384}
385
389
390// Perform constant folding and strength reduction on machine operators.
392 switch (node->opcode()) {
393 case IrOpcode::kProjection:
394 return ReduceProjection(ProjectionIndexOf(node->op()), node->InputAt(0));
395 case IrOpcode::kWord32And:
396 return ReduceWord32And(node);
397 case IrOpcode::kWord64And:
398 return ReduceWord64And(node);
399 case IrOpcode::kWord32Or:
400 return ReduceWord32Or(node);
401 case IrOpcode::kWord64Or:
402 return ReduceWord64Or(node);
403 case IrOpcode::kWord32Xor:
404 return ReduceWord32Xor(node);
405 case IrOpcode::kWord64Xor:
406 return ReduceWord64Xor(node);
407 case IrOpcode::kWord32Shl:
408 return ReduceWord32Shl(node);
409 case IrOpcode::kWord64Shl:
410 return ReduceWord64Shl(node);
411 case IrOpcode::kWord32Shr:
412 return ReduceWord32Shr(node);
413 case IrOpcode::kWord64Shr:
414 return ReduceWord64Shr(node);
415 case IrOpcode::kWord32Sar:
416 return ReduceWord32Sar(node);
417 case IrOpcode::kWord64Sar:
418 return ReduceWord64Sar(node);
419 case IrOpcode::kWord32Ror: {
420 Int32BinopMatcher m(node);
421 if (m.right().Is(0)) return Replace(m.left().node()); // x ror 0 => x
422 if (m.IsFoldable()) { // K ror K => K (K stands for arbitrary constants)
424 m.left().ResolvedValue(), m.right().ResolvedValue() & 31));
425 }
426 break;
427 }
428 case IrOpcode::kWord32Equal:
429 return ReduceWord32Equal(node);
430 case IrOpcode::kWord64Equal:
431 return ReduceWord64Equal(node);
432 case IrOpcode::kInt32Add:
433 return ReduceInt32Add(node);
434 case IrOpcode::kInt64Add:
435 return ReduceInt64Add(node);
436 case IrOpcode::kInt32Sub:
437 return ReduceInt32Sub(node);
438 case IrOpcode::kInt64Sub:
439 return ReduceInt64Sub(node);
440 case IrOpcode::kInt32Mul: {
441 Int32BinopMatcher m(node);
442 if (m.right().Is(0)) return Replace(m.right().node()); // x * 0 => 0
443 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1 => x
444 if (m.IsFoldable()) { // K * K => K (K stands for arbitrary constants)
445 return ReplaceInt32(base::MulWithWraparound(m.left().ResolvedValue(),
446 m.right().ResolvedValue()));
447 }
448 if (m.right().Is(-1)) { // x * -1 => 0 - x
449 node->ReplaceInput(0, Int32Constant(0));
450 node->ReplaceInput(1, m.left().node());
452 return Changed(node);
453 }
454 if (m.right().IsPowerOf2()) { // x * 2^n => x << n
455 node->ReplaceInput(1, Int32Constant(base::bits::WhichPowerOfTwo(
456 m.right().ResolvedValue())));
457 NodeProperties::ChangeOp(node, machine()->Word32Shl());
458 return Changed(node).FollowedBy(ReduceWord32Shl(node));
459 }
460 // (x * Int32Constant(a)) * Int32Constant(b)) => x * Int32Constant(a * b)
461 if (m.right().HasResolvedValue() && m.left().IsInt32Mul()) {
462 Int32BinopMatcher n(m.left().node());
463 if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
464 node->ReplaceInput(
466 m.right().ResolvedValue(), n.right().ResolvedValue())));
467 node->ReplaceInput(0, n.left().node());
468 return Changed(node);
469 }
470 }
471 break;
472 }
473 case IrOpcode::kInt32MulWithOverflow: {
474 Int32BinopMatcher m(node);
475 if (m.right().Is(2)) {
476 node->ReplaceInput(1, m.left().node());
477 NodeProperties::ChangeOp(node, machine()->Int32AddWithOverflow());
478 return Changed(node);
479 }
480 if (m.right().Is(-1)) {
481 node->ReplaceInput(0, Int32Constant(0));
482 node->ReplaceInput(1, m.left().node());
483 NodeProperties::ChangeOp(node, machine()->Int32SubWithOverflow());
484 return Changed(node);
485 }
486 break;
487 }
488 case IrOpcode::kInt64Mul:
489 return ReduceInt64Mul(node);
490 case IrOpcode::kInt32Div:
491 return ReduceInt32Div(node);
492 case IrOpcode::kInt64Div:
493 return ReduceInt64Div(node);
494 case IrOpcode::kUint32Div:
495 return ReduceUint32Div(node);
496 case IrOpcode::kUint64Div:
497 return ReduceUint64Div(node);
498 case IrOpcode::kInt32Mod:
499 return ReduceInt32Mod(node);
500 case IrOpcode::kInt64Mod:
501 return ReduceInt64Mod(node);
502 case IrOpcode::kUint32Mod:
503 return ReduceUint32Mod(node);
504 case IrOpcode::kUint64Mod:
505 return ReduceUint64Mod(node);
506 case IrOpcode::kInt32LessThan: {
507 Int32BinopMatcher m(node);
508 if (m.IsFoldable()) { // K < K => K (K stands for arbitrary constants)
509 return ReplaceBool(m.left().ResolvedValue() <
510 m.right().ResolvedValue());
511 }
512 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
513 if (m.left().IsWord32Or() && m.right().Is(0)) {
514 // (x | K) < 0 => true or (K | x) < 0 => true iff K < 0
515 Int32BinopMatcher mleftmatcher(m.left().node());
516 if (mleftmatcher.left().IsNegative() ||
517 mleftmatcher.right().IsNegative()) {
518 return ReplaceBool(true);
519 }
520 }
521 return ReduceWord32Comparisons(node);
522 }
523 case IrOpcode::kInt32LessThanOrEqual: {
524 Int32BinopMatcher m(node);
525 if (m.IsFoldable()) { // K <= K => K (K stands for arbitrary constants)
526 return ReplaceBool(m.left().ResolvedValue() <=
527 m.right().ResolvedValue());
528 }
529 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
530 return ReduceWord32Comparisons(node);
531 }
532 case IrOpcode::kUint32LessThan: {
533 Uint32BinopMatcher m(node);
534 if (m.left().Is(kMaxUInt32)) return ReplaceBool(false); // M < x => false
535 if (m.right().Is(0)) return ReplaceBool(false); // x < 0 => false
536 if (m.IsFoldable()) { // K < K => K (K stands for arbitrary constants)
537 return ReplaceBool(m.left().ResolvedValue() <
538 m.right().ResolvedValue());
539 }
540 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
541 if (m.left().IsWord32Sar() && m.right().HasResolvedValue()) {
542 Int32BinopMatcher mleft(m.left().node());
543 if (mleft.right().HasResolvedValue()) {
544 // (x >> K) < C => x < (C << K)
545 // when C < (M >> K)
546 const uint32_t c = m.right().ResolvedValue();
547 const uint32_t k = mleft.right().ResolvedValue() & 0x1F;
548 if (c < static_cast<uint32_t>(kMaxInt >> k)) {
549 node->ReplaceInput(0, mleft.left().node());
550 node->ReplaceInput(1, Uint32Constant(c << k));
551 return Changed(node);
552 }
553 // TODO(turbofan): else the comparison is always true.
554 }
555 }
556 return ReduceWord32Comparisons(node);
557 }
558 case IrOpcode::kUint32LessThanOrEqual: {
560 }
561 case IrOpcode::kFloat32Sub: {
564 m.right().Is(0) &&
565 (std::copysign(1.0, m.right().ResolvedValue()) > 0)) {
566 return Replace(m.left().node()); // x - 0 => x
567 }
568 if (m.right().IsNaN()) { // x - NaN => NaN
569 return ReplaceFloat32(SilenceNaN(m.right().ResolvedValue()));
570 }
571 if (m.left().IsNaN()) { // NaN - x => NaN
572 return ReplaceFloat32(SilenceNaN(m.left().ResolvedValue()));
573 }
574 if (m.IsFoldable()) { // L - R => (L - R)
575 return ReplaceFloat32(m.left().ResolvedValue() -
576 m.right().ResolvedValue());
577 }
579 m.left().IsMinusZero()) {
580 // -0.0 - round_down(-0.0 - R) => round_up(R)
581 if (machine()->Float32RoundUp().IsSupported() &&
582 m.right().IsFloat32RoundDown()) {
583 if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat32Sub) {
584 Float32BinopMatcher mright0(m.right().InputAt(0));
585 if (mright0.left().IsMinusZero()) {
586 return Replace(graph()->NewNode(machine()->Float32RoundUp().op(),
587 mright0.right().node()));
588 }
589 }
590 }
591 // -0.0 - R => -R
592 node->RemoveInput(0);
593 NodeProperties::ChangeOp(node, machine()->Float32Neg());
594 return Changed(node);
595 }
596 break;
597 }
598 case IrOpcode::kFloat64Add: {
600 if (m.right().IsNaN()) { // x + NaN => NaN
601 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
602 }
603 if (m.left().IsNaN()) { // NaN + x => NaN
604 return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
605 }
606 if (m.IsFoldable()) { // K + K => K (K stands for arbitrary constants)
607 return ReplaceFloat64(m.left().ResolvedValue() +
608 m.right().ResolvedValue());
609 }
610 break;
611 }
612 case IrOpcode::kFloat64Sub: {
615 m.right().Is(0) &&
616 (base::Double(m.right().ResolvedValue()).Sign() > 0)) {
617 return Replace(m.left().node()); // x - 0 => x
618 }
619 if (m.right().IsNaN()) { // x - NaN => NaN
620 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
621 }
622 if (m.left().IsNaN()) { // NaN - x => NaN
623 return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
624 }
625 if (m.IsFoldable()) { // L - R => (L - R)
626 return ReplaceFloat64(m.left().ResolvedValue() -
627 m.right().ResolvedValue());
628 }
630 m.left().IsMinusZero()) {
631 // -0.0 - round_down(-0.0 - R) => round_up(R)
632 if (machine()->Float64RoundUp().IsSupported() &&
633 m.right().IsFloat64RoundDown()) {
634 if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat64Sub) {
635 Float64BinopMatcher mright0(m.right().InputAt(0));
636 if (mright0.left().IsMinusZero()) {
637 return Replace(graph()->NewNode(machine()->Float64RoundUp().op(),
638 mright0.right().node()));
639 }
640 }
641 }
642 // -0.0 - R => -R
643 node->RemoveInput(0);
644 NodeProperties::ChangeOp(node, machine()->Float64Neg());
645 return Changed(node);
646 }
647 break;
648 }
649 case IrOpcode::kFloat64Mul: {
652 m.right().Is(1))
653 return Replace(m.left().node()); // x * 1.0 => x
654 if (m.right().Is(-1)) { // x * -1.0 => -0.0 - x
655 node->ReplaceInput(0, Float64Constant(-0.0));
656 node->ReplaceInput(1, m.left().node());
657 NodeProperties::ChangeOp(node, machine()->Float64Sub());
658 return Changed(node);
659 }
660 if (m.right().IsNaN()) { // x * NaN => NaN
661 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
662 }
663 if (m.IsFoldable()) { // K * K => K (K stands for arbitrary constants)
664 return ReplaceFloat64(m.left().ResolvedValue() *
665 m.right().ResolvedValue());
666 }
667 if (m.right().Is(2)) { // x * 2.0 => x + x
668 node->ReplaceInput(1, m.left().node());
670 return Changed(node);
671 }
672 break;
673 }
674 case IrOpcode::kFloat64Div: {
677 m.right().Is(1))
678 return Replace(m.left().node()); // x / 1.0 => x
679 // TODO(ahaas): We could do x / 1.0 = x if we knew that x is not an sNaN.
680 if (m.right().IsNaN()) { // x / NaN => NaN
681 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
682 }
683 if (m.left().IsNaN()) { // NaN / x => NaN
684 return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
685 }
686 if (m.IsFoldable()) { // K / K => K (K stands for arbitrary constants)
687 return ReplaceFloat64(
688 base::Divide(m.left().ResolvedValue(), m.right().ResolvedValue()));
689 }
691 m.right().Is(-1)) { // x / -1.0 => -x
692 node->RemoveInput(1);
693 NodeProperties::ChangeOp(node, machine()->Float64Neg());
694 return Changed(node);
695 }
696 if (m.right().IsNormal() && m.right().IsPositiveOrNegativePowerOf2()) {
697 // All reciprocals of non-denormal powers of two can be represented
698 // exactly, so division by power of two can be reduced to
699 // multiplication by reciprocal, with the same result.
700 node->ReplaceInput(1, Float64Constant(1.0 / m.right().ResolvedValue()));
702 return Changed(node);
703 }
704 break;
705 }
706 case IrOpcode::kFloat64Mod: {
708 if (m.right().Is(0)) { // x % 0 => NaN
709 return ReplaceFloat64(std::numeric_limits<double>::quiet_NaN());
710 }
711 if (m.right().IsNaN()) { // x % NaN => NaN
712 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
713 }
714 if (m.left().IsNaN()) { // NaN % x => NaN
715 return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
716 }
717 if (m.IsFoldable()) { // K % K => K (K stands for arbitrary constants)
718 return ReplaceFloat64(
719 Modulo(m.left().ResolvedValue(), m.right().ResolvedValue()));
720 }
721 break;
722 }
723 case IrOpcode::kFloat64Acos: {
724 Float64Matcher m(node->InputAt(0));
725 if (m.HasResolvedValue())
726 return ReplaceFloat64(base::ieee754::acos(m.ResolvedValue()));
727 break;
728 }
729 case IrOpcode::kFloat64Acosh: {
730 Float64Matcher m(node->InputAt(0));
731 if (m.HasResolvedValue())
732 return ReplaceFloat64(base::ieee754::acosh(m.ResolvedValue()));
733 break;
734 }
735 case IrOpcode::kFloat64Asin: {
736 Float64Matcher m(node->InputAt(0));
737 if (m.HasResolvedValue())
738 return ReplaceFloat64(base::ieee754::asin(m.ResolvedValue()));
739 break;
740 }
741 case IrOpcode::kFloat64Asinh: {
742 Float64Matcher m(node->InputAt(0));
743 if (m.HasResolvedValue())
744 return ReplaceFloat64(base::ieee754::asinh(m.ResolvedValue()));
745 break;
746 }
747 case IrOpcode::kFloat64Atan: {
748 Float64Matcher m(node->InputAt(0));
749 if (m.HasResolvedValue())
750 return ReplaceFloat64(base::ieee754::atan(m.ResolvedValue()));
751 break;
752 }
753 case IrOpcode::kFloat64Atanh: {
754 Float64Matcher m(node->InputAt(0));
755 if (m.HasResolvedValue())
756 return ReplaceFloat64(base::ieee754::atanh(m.ResolvedValue()));
757 break;
758 }
759 case IrOpcode::kFloat64Atan2: {
761 if (m.right().IsNaN()) {
762 return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
763 }
764 if (m.left().IsNaN()) {
765 return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
766 }
767 if (m.IsFoldable()) {
768 return ReplaceFloat64(base::ieee754::atan2(m.left().ResolvedValue(),
769 m.right().ResolvedValue()));
770 }
771 break;
772 }
773 case IrOpcode::kFloat64Cbrt: {
774 Float64Matcher m(node->InputAt(0));
775 if (m.HasResolvedValue())
776 return ReplaceFloat64(base::ieee754::cbrt(m.ResolvedValue()));
777 break;
778 }
779 case IrOpcode::kFloat64Cos: {
780 Float64Matcher m(node->InputAt(0));
781 if (m.HasResolvedValue())
782 return ReplaceFloat64(COS_IMPL(m.ResolvedValue()));
783 break;
784 }
785 case IrOpcode::kFloat64Cosh: {
786 Float64Matcher m(node->InputAt(0));
787 if (m.HasResolvedValue())
788 return ReplaceFloat64(base::ieee754::cosh(m.ResolvedValue()));
789 break;
790 }
791 case IrOpcode::kFloat64Exp: {
792 Float64Matcher m(node->InputAt(0));
793 if (m.HasResolvedValue())
794 return ReplaceFloat64(base::ieee754::exp(m.ResolvedValue()));
795 break;
796 }
797 case IrOpcode::kFloat64Expm1: {
798 Float64Matcher m(node->InputAt(0));
799 if (m.HasResolvedValue())
800 return ReplaceFloat64(base::ieee754::expm1(m.ResolvedValue()));
801 break;
802 }
803 case IrOpcode::kFloat64Log: {
804 Float64Matcher m(node->InputAt(0));
805 if (m.HasResolvedValue())
806 return ReplaceFloat64(base::ieee754::log(m.ResolvedValue()));
807 break;
808 }
809 case IrOpcode::kFloat64Log1p: {
810 Float64Matcher m(node->InputAt(0));
811 if (m.HasResolvedValue())
812 return ReplaceFloat64(base::ieee754::log1p(m.ResolvedValue()));
813 break;
814 }
815 case IrOpcode::kFloat64Log10: {
816 Float64Matcher m(node->InputAt(0));
817 if (m.HasResolvedValue())
818 return ReplaceFloat64(base::ieee754::log10(m.ResolvedValue()));
819 break;
820 }
821 case IrOpcode::kFloat64Log2: {
822 Float64Matcher m(node->InputAt(0));
823 if (m.HasResolvedValue())
824 return ReplaceFloat64(base::ieee754::log2(m.ResolvedValue()));
825 break;
826 }
827 case IrOpcode::kFloat64Pow: {
829 if (m.IsFoldable()) {
830 return ReplaceFloat64(
831 math::pow(m.left().ResolvedValue(), m.right().ResolvedValue()));
832 } else if (m.right().Is(0.0)) { // x ** +-0.0 => 1.0
833 return ReplaceFloat64(1.0);
834 } else if (m.right().Is(2.0)) { // x ** 2.0 => x * x
835 node->ReplaceInput(1, m.left().node());
837 return Changed(node);
838 } else if (m.right().Is(0.5)) {
839 // x ** 0.5 => if x <= -Infinity then Infinity else sqrt(0.0 + x)
840 return Replace(Float64PowHalf(m.left().node()));
841 }
842 break;
843 }
844 case IrOpcode::kFloat64Sin: {
845 Float64Matcher m(node->InputAt(0));
846 if (m.HasResolvedValue())
847 return ReplaceFloat64(SIN_IMPL(m.ResolvedValue()));
848 break;
849 }
850 case IrOpcode::kFloat64Sinh: {
851 Float64Matcher m(node->InputAt(0));
852 if (m.HasResolvedValue())
853 return ReplaceFloat64(base::ieee754::sinh(m.ResolvedValue()));
854 break;
855 }
856 case IrOpcode::kFloat64Tan: {
857 Float64Matcher m(node->InputAt(0));
858 if (m.HasResolvedValue())
859 return ReplaceFloat64(base::ieee754::tan(m.ResolvedValue()));
860 break;
861 }
862 case IrOpcode::kFloat64Tanh: {
863 Float64Matcher m(node->InputAt(0));
864 if (m.HasResolvedValue())
865 return ReplaceFloat64(base::ieee754::tanh(m.ResolvedValue()));
866 break;
867 }
868 case IrOpcode::kChangeFloat32ToFloat64: {
869 Float32Matcher m(node->InputAt(0));
870 if (m.HasResolvedValue()) {
872 std::isnan(m.ResolvedValue())) {
873 return ReplaceFloat64(SilenceNaN(m.ResolvedValue()));
874 }
875 return ReplaceFloat64(m.ResolvedValue());
876 }
877 break;
878 }
879 case IrOpcode::kChangeFloat64ToInt32: {
880 Float64Matcher m(node->InputAt(0));
881 if (m.HasResolvedValue())
882 return ReplaceInt32(FastD2IChecked(m.ResolvedValue()));
883 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
884 break;
885 }
886 case IrOpcode::kChangeFloat64ToInt64: {
887 Float64Matcher m(node->InputAt(0));
888 if (m.HasResolvedValue())
889 return ReplaceInt64(static_cast<int64_t>(m.ResolvedValue()));
890 if (m.IsChangeInt64ToFloat64()) return Replace(m.node()->InputAt(0));
891 break;
892 }
893 case IrOpcode::kChangeFloat64ToUint32: {
894 Float64Matcher m(node->InputAt(0));
895 if (m.HasResolvedValue())
896 return ReplaceInt32(FastD2UI(m.ResolvedValue()));
897 if (m.IsChangeUint32ToFloat64()) return Replace(m.node()->InputAt(0));
898 break;
899 }
900 case IrOpcode::kChangeInt32ToFloat64: {
901 Int32Matcher m(node->InputAt(0));
902 if (m.HasResolvedValue())
903 return ReplaceFloat64(FastI2D(m.ResolvedValue()));
904 break;
905 }
906 case IrOpcode::kBitcastWord32ToWord64: {
907 Int32Matcher m(node->InputAt(0));
908 if (m.HasResolvedValue()) return ReplaceInt64(m.ResolvedValue());
909 // No need to truncate the value, since top 32 bits are not important.
910 if (m.IsTruncateInt64ToInt32()) return Replace(m.node()->InputAt(0));
911 break;
912 }
913 case IrOpcode::kChangeInt32ToInt64: {
914 Int32Matcher m(node->InputAt(0));
915 if (m.HasResolvedValue()) return ReplaceInt64(m.ResolvedValue());
916 break;
917 }
918 case IrOpcode::kChangeInt64ToFloat64: {
919 Int64Matcher m(node->InputAt(0));
920 if (m.HasResolvedValue())
921 return ReplaceFloat64(static_cast<double>(m.ResolvedValue()));
922 if (m.IsChangeFloat64ToInt64()) return Replace(m.node()->InputAt(0));
923 break;
924 }
925 case IrOpcode::kChangeUint32ToFloat64: {
926 Uint32Matcher m(node->InputAt(0));
927 if (m.HasResolvedValue())
928 return ReplaceFloat64(FastUI2D(m.ResolvedValue()));
929 break;
930 }
931 case IrOpcode::kChangeUint32ToUint64: {
932 Uint32Matcher m(node->InputAt(0));
933 if (m.HasResolvedValue())
934 return ReplaceInt64(static_cast<uint64_t>(m.ResolvedValue()));
935 break;
936 }
937 case IrOpcode::kTruncateFloat64ToWord32: {
938 Float64Matcher m(node->InputAt(0));
939 if (m.HasResolvedValue())
940 return ReplaceInt32(DoubleToInt32(m.ResolvedValue()));
941 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
942 return NoChange();
943 }
944 case IrOpcode::kTruncateInt64ToInt32:
945 return ReduceTruncateInt64ToInt32(node);
946 case IrOpcode::kTruncateFloat64ToFloat32: {
947 Float64Matcher m(node->InputAt(0));
948 if (m.HasResolvedValue()) {
950 return ReplaceFloat32(DoubleToFloat32(SilenceNaN(m.ResolvedValue())));
951 }
952 return ReplaceFloat32(DoubleToFloat32(m.ResolvedValue()));
953 }
955 m.IsChangeFloat32ToFloat64())
956 return Replace(m.node()->InputAt(0));
957 break;
958 }
959 case IrOpcode::kRoundFloat64ToInt32: {
960 Float64Matcher m(node->InputAt(0));
961 if (m.HasResolvedValue()) {
962 return ReplaceInt32(DoubleToInt32(m.ResolvedValue()));
963 }
964 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
965 break;
966 }
967 case IrOpcode::kFloat64InsertLowWord32:
968 return ReduceFloat64InsertLowWord32(node);
969 case IrOpcode::kFloat64InsertHighWord32:
971 case IrOpcode::kStore:
972 case IrOpcode::kUnalignedStore:
973 return ReduceStore(node);
974 case IrOpcode::kFloat64Equal:
975 case IrOpcode::kFloat64LessThan:
976 case IrOpcode::kFloat64LessThanOrEqual:
977 return ReduceFloat64Compare(node);
978 case IrOpcode::kFloat64RoundDown:
979 return ReduceFloat64RoundDown(node);
980 case IrOpcode::kBitcastTaggedToWord:
981 case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: {
982 NodeMatcher m(node->InputAt(0));
983 if (m.IsBitcastWordToTaggedSigned()) {
984 RelaxEffectsAndControls(node);
985 return Replace(m.InputAt(0));
986 }
987 break;
988 }
989 case IrOpcode::kBranch:
990 case IrOpcode::kDeoptimizeIf:
991 case IrOpcode::kDeoptimizeUnless:
992#if V8_ENABLE_WEBASSEMBLY
993 case IrOpcode::kTrapIf:
994 case IrOpcode::kTrapUnless:
995#endif
996 return ReduceConditional(node);
997 case IrOpcode::kInt64LessThan: {
998 Int64BinopMatcher m(node);
999 if (m.IsFoldable()) { // K < K => K (K stands for arbitrary constants)
1000 return ReplaceBool(m.left().ResolvedValue() <
1001 m.right().ResolvedValue());
1002 }
1003 return ReduceWord64Comparisons(node);
1004 }
1005 case IrOpcode::kInt64LessThanOrEqual: {
1006 Int64BinopMatcher m(node);
1007 if (m.IsFoldable()) { // K <= K => K (K stands for arbitrary constants)
1008 return ReplaceBool(m.left().ResolvedValue() <=
1009 m.right().ResolvedValue());
1010 }
1011 return ReduceWord64Comparisons(node);
1012 }
1013 case IrOpcode::kUint64LessThan: {
1014 Uint64BinopMatcher m(node);
1015 if (m.IsFoldable()) { // K < K => K (K stands for arbitrary constants)
1016 return ReplaceBool(m.left().ResolvedValue() <
1017 m.right().ResolvedValue());
1018 }
1019 return ReduceWord64Comparisons(node);
1020 }
1021 case IrOpcode::kUint64LessThanOrEqual: {
1023 }
1024 case IrOpcode::kFloat32Select:
1025 case IrOpcode::kFloat64Select:
1026 case IrOpcode::kWord32Select:
1027 case IrOpcode::kWord64Select: {
1028 Int32Matcher match(node->InputAt(0));
1029 if (match.HasResolvedValue()) {
1030 if (match.Is(0)) {
1031 return Replace(node->InputAt(2));
1032 } else {
1033 return Replace(node->InputAt(1));
1034 }
1035 }
1036 break;
1037 }
1038 case IrOpcode::kLoad:
1039 case IrOpcode::kProtectedLoad:
1040 case IrOpcode::kLoadTrapOnNull: {
1041 Node* input0 = node->InputAt(0);
1042 Node* input1 = node->InputAt(1);
1043 if (input0->opcode() == IrOpcode::kInt64Add) {
1044 Int64BinopMatcher m(input0);
1045 if (m.right().HasResolvedValue()) {
1046 int64_t value = m.right().ResolvedValue();
1047 node->ReplaceInput(0, m.left().node());
1048 Node* new_node = Int64Add(input1, Int64Constant(value));
1049 node->ReplaceInput(1, new_node);
1050 return Changed(node);
1051 }
1052 }
1053 break;
1054 }
1055 default:
1056 break;
1057 }
1058 return NoChange();
1059}
1060
1062 Int64Matcher m(node->InputAt(0));
1063 if (m.HasResolvedValue())
1064 return ReplaceInt32(static_cast<int32_t>(m.ResolvedValue()));
1065 if (m.IsChangeInt32ToInt64() || m.IsChangeUint32ToUint64())
1066 return Replace(m.node()->InputAt(0));
1067 // TruncateInt64ToInt32(BitcastTaggedToWordForTagAndSmiBits(Load(x))) =>
1068 // Load(x)
1069 // where the new Load uses Int32 rather than the tagged representation.
1070 if (m.IsBitcastTaggedToWordForTagAndSmiBits() && m.node()->UseCount() == 1) {
1071 Node* input = m.node()->InputAt(0);
1072 if (input->opcode() == IrOpcode::kLoad ||
1073 input->opcode() == IrOpcode::kLoadImmutable) {
1074 LoadRepresentation load_rep = LoadRepresentationOf(input->op());
1075 if (ElementSizeLog2Of(load_rep.representation()) == 2) {
1076 // Ensure that the value output of the load is only ever used by the
1077 // BitcastTaggedToWordForTagAndSmiBits.
1078 int value_edges = 0;
1079 for (Edge edge : input->use_edges()) {
1080 if (NodeProperties::IsValueEdge(edge)) ++value_edges;
1081 }
1082 if (value_edges == 1) {
1083 // Removing the input is required as node is replaced by the Load, but
1084 // is still used by the the BitcastTaggedToWordForTagAndSmiBits, so
1085 // will prevent future CanCover calls being true.
1086 m.node()->RemoveInput(0);
1088 input,
1089 input->opcode() == IrOpcode::kLoad
1090 ? machine()->Load(LoadRepresentation::Int32())
1091 : machine()->LoadImmutable(LoadRepresentation::Int32()));
1092 return Replace(input);
1093 }
1094 }
1095 }
1096 }
1097 return NoChange();
1098}
1099
1101 DCHECK_EQ(IrOpcode::kInt32Add, node->opcode());
1102 Int32BinopMatcher m(node);
1103 if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => x
1104 if (m.IsFoldable()) { // K + K => K (K stands for arbitrary constants)
1105 return ReplaceInt32(base::AddWithWraparound(m.left().ResolvedValue(),
1106 m.right().ResolvedValue()));
1107 }
1108 if (m.left().IsInt32Sub()) {
1109 Int32BinopMatcher mleft(m.left().node());
1110 if (mleft.left().Is(0)) { // (0 - x) + y => y - x
1111 node->ReplaceInput(0, m.right().node());
1112 node->ReplaceInput(1, mleft.right().node());
1114 return Changed(node).FollowedBy(ReduceInt32Sub(node));
1115 }
1116 }
1117 if (m.right().IsInt32Sub()) {
1118 Int32BinopMatcher mright(m.right().node());
1119 if (mright.left().Is(0)) { // y + (0 - x) => y - x
1120 node->ReplaceInput(1, mright.right().node());
1122 return Changed(node).FollowedBy(ReduceInt32Sub(node));
1123 }
1124 }
1125 // (x + Int32Constant(a)) + Int32Constant(b)) => x + Int32Constant(a + b)
1126 if (m.right().HasResolvedValue() && m.left().IsInt32Add()) {
1127 Int32BinopMatcher n(m.left().node());
1128 if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1129 node->ReplaceInput(
1130 1, Int32Constant(base::AddWithWraparound(m.right().ResolvedValue(),
1131 n.right().ResolvedValue())));
1132 node->ReplaceInput(0, n.left().node());
1133 return Changed(node);
1134 }
1135 }
1136
1137 return NoChange();
1138}
1139
1141 DCHECK_EQ(IrOpcode::kInt64Add, node->opcode());
1142 Int64BinopMatcher m(node);
1143 if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => 0
1144 if (m.IsFoldable()) {
1145 return ReplaceInt64(base::AddWithWraparound(m.left().ResolvedValue(),
1146 m.right().ResolvedValue()));
1147 }
1148 // (x + Int64Constant(a)) + Int64Constant(b) => x + Int64Constant(a + b)
1149 if (m.right().HasResolvedValue() && m.left().IsInt64Add()) {
1150 Int64BinopMatcher n(m.left().node());
1151 if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1152 node->ReplaceInput(
1153 1, Int64Constant(base::AddWithWraparound(m.right().ResolvedValue(),
1154 n.right().ResolvedValue())));
1155 node->ReplaceInput(0, n.left().node());
1156 return Changed(node);
1157 }
1158 }
1159 return NoChange();
1160}
1161
1163 DCHECK_EQ(IrOpcode::kInt32Sub, node->opcode());
1164 Int32BinopMatcher m(node);
1165 if (m.right().Is(0)) return Replace(m.left().node()); // x - 0 => x
1166 if (m.IsFoldable()) { // K - K => K (K stands for arbitrary constants)
1167 return ReplaceInt32(base::SubWithWraparound(m.left().ResolvedValue(),
1168 m.right().ResolvedValue()));
1169 }
1170 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x - x => 0
1171 if (m.right().HasResolvedValue()) { // x - K => x + -K
1172 node->ReplaceInput(
1173 1,
1174 Int32Constant(base::NegateWithWraparound(m.right().ResolvedValue())));
1176 return Changed(node).FollowedBy(ReduceInt32Add(node));
1177 }
1178 return NoChange();
1179}
1180
1182 DCHECK_EQ(IrOpcode::kInt64Sub, node->opcode());
1183 Int64BinopMatcher m(node);
1184 if (m.right().Is(0)) return Replace(m.left().node()); // x - 0 => x
1185 if (m.IsFoldable()) { // K - K => K (K stands for arbitrary constants)
1186 return ReplaceInt64(base::SubWithWraparound(m.left().ResolvedValue(),
1187 m.right().ResolvedValue()));
1188 }
1189 if (m.LeftEqualsRight()) return Replace(Int64Constant(0)); // x - x => 0
1190 if (m.right().HasResolvedValue()) { // x - K => x + -K
1191 node->ReplaceInput(
1192 1,
1193 Int64Constant(base::NegateWithWraparound(m.right().ResolvedValue())));
1195 return Changed(node).FollowedBy(ReduceInt64Add(node));
1196 }
1197 return NoChange();
1198}
1199
1201 DCHECK_EQ(IrOpcode::kInt64Mul, node->opcode());
1202 Int64BinopMatcher m(node);
1203 if (m.right().Is(0)) return Replace(m.right().node()); // x * 0 => 0
1204 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1 => x
1205 if (m.IsFoldable()) { // K * K => K (K stands for arbitrary constants)
1206 return ReplaceInt64(base::MulWithWraparound(m.left().ResolvedValue(),
1207 m.right().ResolvedValue()));
1208 }
1209 if (m.right().Is(-1)) { // x * -1 => 0 - x
1210 node->ReplaceInput(0, Int64Constant(0));
1211 node->ReplaceInput(1, m.left().node());
1213 return Changed(node);
1214 }
1215 if (m.right().IsPowerOf2()) { // x * 2^n => x << n
1216 node->ReplaceInput(
1217 1,
1218 Int64Constant(base::bits::WhichPowerOfTwo(m.right().ResolvedValue())));
1219 NodeProperties::ChangeOp(node, machine()->Word64Shl());
1220 return Changed(node).FollowedBy(ReduceWord64Shl(node));
1221 }
1222 // (x * Int64Constant(a)) * Int64Constant(b)) => x * Int64Constant(a * b)
1223 if (m.right().HasResolvedValue() && m.left().IsInt64Mul()) {
1224 Int64BinopMatcher n(m.left().node());
1225 if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1226 node->ReplaceInput(
1227 1, Int64Constant(base::MulWithWraparound(m.right().ResolvedValue(),
1228 n.right().ResolvedValue())));
1229 node->ReplaceInput(0, n.left().node());
1230 return Changed(node);
1231 }
1232 }
1233 return NoChange();
1234}
1235
1237 Int32BinopMatcher m(node);
1238 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
1239 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
1240 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
1241 if (m.IsFoldable()) { // K / K => K (K stands for arbitrary constants)
1242 return ReplaceInt32(base::bits::SignedDiv32(m.left().ResolvedValue(),
1243 m.right().ResolvedValue()));
1244 }
1245 if (m.LeftEqualsRight()) { // x / x => x != 0
1246 Node* const zero = Int32Constant(0);
1247 return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1248 }
1249 if (m.right().Is(-1)) { // x / -1 => 0 - x
1250 node->ReplaceInput(0, Int32Constant(0));
1251 node->ReplaceInput(1, m.left().node());
1252 node->TrimInputCount(2);
1254 return Changed(node);
1255 }
1256 if (m.right().HasResolvedValue()) {
1257 int32_t const divisor = m.right().ResolvedValue();
1258 Node* const dividend = m.left().node();
1259 Node* quotient = dividend;
1260 if (base::bits::IsPowerOfTwo(Abs(divisor))) {
1261 uint32_t const shift = base::bits::WhichPowerOfTwo(Abs(divisor));
1262 DCHECK_NE(0u, shift);
1263 if (shift > 1) {
1264 quotient = Word32Sar(quotient, 31);
1265 }
1266 quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend);
1267 quotient = Word32Sar(quotient, shift);
1268 } else {
1269 quotient = Int32Div(quotient, Abs(divisor));
1270 }
1271 if (divisor < 0) {
1272 node->ReplaceInput(0, Int32Constant(0));
1273 node->ReplaceInput(1, quotient);
1274 node->TrimInputCount(2);
1276 return Changed(node);
1277 }
1278 return Replace(quotient);
1279 }
1280 return NoChange();
1281}
1282
1284 Int64BinopMatcher m(node);
1285 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
1286 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
1287 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
1288 if (m.IsFoldable()) { // K / K => K (K stands for arbitrary constants)
1289 return ReplaceInt64(base::bits::SignedDiv64(m.left().ResolvedValue(),
1290 m.right().ResolvedValue()));
1291 }
1292 if (m.LeftEqualsRight()) { // x / x => x != 0
1293 Node* const zero = Int64Constant(0);
1294 // {Word64Equal} can get reduced to a bool/int32, but we need this
1295 // operation to produce an int64.
1296 return Replace(ChangeInt32ToInt64(
1297 Word64Equal(Word64Equal(m.left().node(), zero), zero)));
1298 }
1299 if (m.right().Is(-1)) { // x / -1 => 0 - x
1300 node->ReplaceInput(0, Int64Constant(0));
1301 node->ReplaceInput(1, m.left().node());
1302 node->TrimInputCount(2);
1304 return Changed(node);
1305 }
1306 if (m.right().HasResolvedValue()) {
1307 int64_t const divisor = m.right().ResolvedValue();
1308 Node* const dividend = m.left().node();
1309 Node* quotient = dividend;
1310 if (base::bits::IsPowerOfTwo(Abs(divisor))) {
1311 uint32_t const shift = base::bits::WhichPowerOfTwo(Abs(divisor));
1312 DCHECK_NE(0u, shift);
1313 if (shift > 1) {
1314 quotient = Word64Sar(quotient, 63);
1315 }
1316 quotient = Int64Add(Word64Shr(quotient, 64u - shift), dividend);
1317 quotient = Word64Sar(quotient, shift);
1318 } else {
1319 quotient = Int64Div(quotient, Abs(divisor));
1320 }
1321 if (divisor < 0) {
1322 node->ReplaceInput(0, Int64Constant(0));
1323 node->ReplaceInput(1, quotient);
1324 node->TrimInputCount(2);
1326 return Changed(node);
1327 }
1328 return Replace(quotient);
1329 }
1330 return NoChange();
1331}
1332
1334 Uint32BinopMatcher m(node);
1335 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
1336 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
1337 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
1338 if (m.IsFoldable()) { // K / K => K (K stands for arbitrary constants)
1339 return ReplaceUint32(base::bits::UnsignedDiv32(m.left().ResolvedValue(),
1340 m.right().ResolvedValue()));
1341 }
1342 if (m.LeftEqualsRight()) { // x / x => x != 0
1343 Node* const zero = Int32Constant(0);
1344 return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1345 }
1346 if (m.right().HasResolvedValue()) {
1347 Node* const dividend = m.left().node();
1348 uint32_t const divisor = m.right().ResolvedValue();
1349 if (base::bits::IsPowerOfTwo(divisor)) { // x / 2^n => x >> n
1350 node->ReplaceInput(1, Uint32Constant(base::bits::WhichPowerOfTwo(
1351 m.right().ResolvedValue())));
1352 node->TrimInputCount(2);
1354 return Changed(node);
1355 } else {
1356 return Replace(Uint32Div(dividend, divisor));
1357 }
1358 }
1359 return NoChange();
1360}
1361
1363 Uint64BinopMatcher m(node);
1364 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
1365 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
1366 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
1367 if (m.IsFoldable()) { // K / K => K (K stands for arbitrary constants)
1368 return ReplaceUint64(base::bits::UnsignedDiv64(m.left().ResolvedValue(),
1369 m.right().ResolvedValue()));
1370 }
1371 if (m.LeftEqualsRight()) { // x / x => x != 0
1372 Node* const zero = Int64Constant(0);
1373 // {Word64Equal} can get reduced to a bool/int32, but we need this
1374 // operation to produce an int64.
1375 return Replace(ChangeInt32ToInt64(
1376 Word64Equal(Word64Equal(m.left().node(), zero), zero)));
1377 }
1378 if (m.right().HasResolvedValue()) {
1379 Node* const dividend = m.left().node();
1380 uint64_t const divisor = m.right().ResolvedValue();
1381 if (base::bits::IsPowerOfTwo(divisor)) { // x / 2^n => x >> n
1382 node->ReplaceInput(1, Uint64Constant(base::bits::WhichPowerOfTwo(
1383 m.right().ResolvedValue())));
1384 node->TrimInputCount(2);
1386 return Changed(node);
1387 } else {
1388 return Replace(Uint64Div(dividend, divisor));
1389 }
1390 }
1391 return NoChange();
1392}
1393
1395 Int32BinopMatcher m(node);
1396 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1397 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1398 if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
1399 if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
1400 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0
1401 if (m.IsFoldable()) { // K % K => K (K stands for arbitrary constants)
1402 return ReplaceInt32(base::bits::SignedMod32(m.left().ResolvedValue(),
1403 m.right().ResolvedValue()));
1404 }
1405 if (m.right().HasResolvedValue()) {
1406 Node* const dividend = m.left().node();
1407 uint32_t const divisor = Abs(m.right().ResolvedValue());
1408 if (base::bits::IsPowerOfTwo(divisor)) {
1409 uint32_t const mask = divisor - 1;
1410 Node* const zero = Int32Constant(0);
1411 Diamond d(graph(), common(),
1412 graph()->NewNode(machine()->Int32LessThan(), dividend, zero),
1414 return Replace(
1416 Int32Sub(zero, Word32And(Int32Sub(zero, dividend), mask)),
1417 Word32And(dividend, mask)));
1418 } else {
1419 Node* quotient = Int32Div(dividend, divisor);
1420 DCHECK_EQ(dividend, node->InputAt(0));
1421 node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor)));
1422 node->TrimInputCount(2);
1424 }
1425 return Changed(node);
1426 }
1427 return NoChange();
1428}
1429
1431 Int64BinopMatcher m(node);
1432 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1433 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1434 if (m.right().Is(1)) return ReplaceInt64(0); // x % 1 => 0
1435 if (m.right().Is(-1)) return ReplaceInt64(0); // x % -1 => 0
1436 if (m.LeftEqualsRight()) return ReplaceInt64(0); // x % x => 0
1437 if (m.IsFoldable()) { // K % K => K (K stands for arbitrary constants)
1438 return ReplaceInt64(base::bits::SignedMod64(m.left().ResolvedValue(),
1439 m.right().ResolvedValue()));
1440 }
1441 if (m.right().HasResolvedValue()) {
1442 Node* const dividend = m.left().node();
1443 uint64_t const divisor = Abs(m.right().ResolvedValue());
1444 if (base::bits::IsPowerOfTwo(divisor)) {
1445 uint64_t const mask = divisor - 1;
1446 Node* const zero = Int64Constant(0);
1447 Diamond d(graph(), common(),
1448 graph()->NewNode(machine()->Int64LessThan(), dividend, zero),
1450 return Replace(
1452 Int64Sub(zero, Word64And(Int64Sub(zero, dividend), mask)),
1453 Word64And(dividend, mask)));
1454 } else {
1455 Node* quotient = Int64Div(dividend, divisor);
1456 DCHECK_EQ(dividend, node->InputAt(0));
1457 node->ReplaceInput(1, Int64Mul(quotient, Int64Constant(divisor)));
1458 node->TrimInputCount(2);
1460 }
1461 return Changed(node);
1462 }
1463 return NoChange();
1464}
1465
1467 Uint32BinopMatcher m(node);
1468 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1469 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1470 if (m.right().Is(1)) return ReplaceUint32(0); // x % 1 => 0
1471 if (m.LeftEqualsRight()) return ReplaceUint32(0); // x % x => 0
1472 if (m.IsFoldable()) { // K % K => K (K stands for arbitrary constants)
1473 return ReplaceUint32(base::bits::UnsignedMod32(m.left().ResolvedValue(),
1474 m.right().ResolvedValue()));
1475 }
1476 if (m.right().HasResolvedValue()) {
1477 Node* const dividend = m.left().node();
1478 uint32_t const divisor = m.right().ResolvedValue();
1479 if (base::bits::IsPowerOfTwo(divisor)) { // x % 2^n => x & 2^n-1
1480 node->ReplaceInput(1, Uint32Constant(m.right().ResolvedValue() - 1));
1481 node->TrimInputCount(2);
1483 } else {
1484 Node* quotient = Uint32Div(dividend, divisor);
1485 DCHECK_EQ(dividend, node->InputAt(0));
1486 node->ReplaceInput(1, Int32Mul(quotient, Uint32Constant(divisor)));
1487 node->TrimInputCount(2);
1489 }
1490 return Changed(node);
1491 }
1492 return NoChange();
1493}
1494
1496 Uint64BinopMatcher m(node);
1497 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1498 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1499 if (m.right().Is(1)) return ReplaceUint64(0); // x % 1 => 0
1500 if (m.LeftEqualsRight()) return ReplaceUint64(0); // x % x => 0
1501 if (m.IsFoldable()) { // K % K => K (K stands for arbitrary constants)
1502 return ReplaceUint64(base::bits::UnsignedMod64(m.left().ResolvedValue(),
1503 m.right().ResolvedValue()));
1504 }
1505 if (m.right().HasResolvedValue()) {
1506 Node* const dividend = m.left().node();
1507 uint64_t const divisor = m.right().ResolvedValue();
1508 if (base::bits::IsPowerOfTwo(divisor)) { // x % 2^n => x & 2^n-1
1509 node->ReplaceInput(1, Uint64Constant(m.right().ResolvedValue() - 1));
1510 node->TrimInputCount(2);
1512 } else {
1513 Node* quotient = Uint64Div(dividend, divisor);
1514 DCHECK_EQ(dividend, node->InputAt(0));
1515 node->ReplaceInput(1, Int64Mul(quotient, Uint64Constant(divisor)));
1516 node->TrimInputCount(2);
1518 }
1519 return Changed(node);
1520 }
1521 return NoChange();
1522}
1523
1525 NodeMatcher nm(node);
1526 DCHECK(nm.IsStore() || nm.IsUnalignedStore());
1528 nm.IsStore() ? StoreRepresentationOf(node->op()).representation()
1529 : UnalignedStoreRepresentationOf(node->op());
1530
1531 const int value_input = 2;
1532 Node* const value = node->InputAt(value_input);
1533
1534 switch (value->opcode()) {
1535 case IrOpcode::kWord32And: {
1536 Uint32BinopMatcher m(value);
1537 if (m.right().HasResolvedValue() &&
1539 (m.right().ResolvedValue() & 0xFF) == 0xFF) ||
1541 (m.right().ResolvedValue() & 0xFFFF) == 0xFFFF))) {
1542 node->ReplaceInput(value_input, m.left().node());
1543 return Changed(node);
1544 }
1545 break;
1546 }
1547 case IrOpcode::kWord32Sar: {
1548 Int32BinopMatcher m(value);
1549 if (m.left().IsWord32Shl() && ((rep == MachineRepresentation::kWord8 &&
1550 m.right().IsInRange(1, 24)) ||
1552 m.right().IsInRange(1, 16)))) {
1553 Int32BinopMatcher mleft(m.left().node());
1554 if (mleft.right().Is(m.right().ResolvedValue())) {
1555 node->ReplaceInput(value_input, mleft.left().node());
1556 return Changed(node);
1557 }
1558 }
1559 break;
1560 }
1561 default:
1562 break;
1563 }
1564 return NoChange();
1565}
1566
1568 switch (node->opcode()) {
1569 case IrOpcode::kInt32AddWithOverflow: {
1570 DCHECK(index == 0 || index == 1);
1571 Int32BinopMatcher m(node);
1572 if (m.IsFoldable()) {
1573 int32_t val;
1575 m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1576 return ReplaceInt32(index == 0 ? val : ovf);
1577 }
1578 if (m.right().Is(0)) {
1579 return Replace(index == 0 ? m.left().node() : m.right().node());
1580 }
1581 break;
1582 }
1583 case IrOpcode::kInt32SubWithOverflow: {
1584 DCHECK(index == 0 || index == 1);
1585 Int32BinopMatcher m(node);
1586 if (m.IsFoldable()) {
1587 int32_t val;
1589 m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1590 return ReplaceInt32(index == 0 ? val : ovf);
1591 }
1592 if (m.right().Is(0)) {
1593 return Replace(index == 0 ? m.left().node() : m.right().node());
1594 }
1595 break;
1596 }
1597 case IrOpcode::kInt32MulWithOverflow: {
1598 DCHECK(index == 0 || index == 1);
1599 Int32BinopMatcher m(node);
1600 if (m.IsFoldable()) {
1601 int32_t val;
1603 m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1604 return ReplaceInt32(index == 0 ? val : ovf);
1605 }
1606 if (m.right().Is(0)) {
1607 return Replace(m.right().node());
1608 }
1609 if (m.right().Is(1)) {
1610 return index == 0 ? Replace(m.left().node()) : ReplaceInt32(0);
1611 }
1612 break;
1613 }
1614 default:
1615 break;
1616 }
1617 return NoChange();
1618}
1619
1620namespace {
1621
1622// Returns true if "value << shift >> shift == value". This can be interpreted
1623// as "left shifting |value| by |shift| doesn't shift away significant bits".
1624// Or, equivalently, "left shifting |value| by |shift| doesn't have signed
1625// overflow".
1626template <typename T>
1627bool CanRevertLeftShiftWithRightShift(T value, T shift) {
1628 using unsigned_T = std::make_unsigned_t<T>;
1629 if (shift < 0 || shift >= std::numeric_limits<T>::digits + 1) {
1630 // This shift would be UB in C++
1631 return false;
1632 }
1633 if (static_cast<T>(static_cast<unsigned_T>(value) << shift) >> shift !=
1634 static_cast<T>(value)) {
1635 return false;
1636 }
1637 return true;
1638}
1639
1640bool CanTruncate(int64_t value) {
1641 return value >= std::numeric_limits<int32_t>::min() &&
1642 value <= std::numeric_limits<int32_t>::max();
1643}
1644
1645} // namespace
1646
1648 DCHECK(node->opcode() == IrOpcode::kInt32LessThan ||
1649 node->opcode() == IrOpcode::kInt32LessThanOrEqual ||
1650 node->opcode() == IrOpcode::kUint32LessThan ||
1651 node->opcode() == IrOpcode::kUint32LessThanOrEqual);
1652 Int32BinopMatcher m(node);
1653 // (x >> K) < (y >> K) => x < y if only zeros shifted out
1654 if (m.left().op() == machine()->Word32SarShiftOutZeros() &&
1655 m.right().op() == machine()->Word32SarShiftOutZeros()) {
1656 Int32BinopMatcher mleft(m.left().node());
1657 Int32BinopMatcher mright(m.right().node());
1658 if (mleft.right().HasResolvedValue() &&
1659 mright.right().Is(mleft.right().ResolvedValue())) {
1660 node->ReplaceInput(0, mleft.left().node());
1661 node->ReplaceInput(1, mright.left().node());
1662 return Changed(node);
1663 }
1664 }
1665 // Simplifying (x >> n) <= k into x <= (k << n), with "k << n" being
1666 // computed here at compile time.
1667 if (m.right().HasResolvedValue() &&
1668 m.left().op() == machine()->Word32SarShiftOutZeros() &&
1669 m.left().node()->UseCount() == 1) {
1670 uint32_t right = m.right().ResolvedValue();
1671 Int32BinopMatcher mleft(m.left().node());
1672 if (mleft.right().HasResolvedValue()) {
1673 auto shift = mleft.right().ResolvedValue();
1674 if (CanRevertLeftShiftWithRightShift<int32_t>(right, shift)) {
1675 node->ReplaceInput(0, mleft.left().node());
1676 node->ReplaceInput(1, Int32Constant(right << shift));
1677 return Changed(node);
1678 }
1679 }
1680 }
1681 // Simplifying k <= (x >> n) into (k << n) <= x, with "k << n" being
1682 // computed here at compile time.
1683 if (m.left().HasResolvedValue() &&
1684 m.right().op() == machine()->Word32SarShiftOutZeros() &&
1685 m.right().node()->UseCount() == 1) {
1686 uint32_t left = m.left().ResolvedValue();
1687 Int32BinopMatcher mright(m.right().node());
1688 if (mright.right().HasResolvedValue()) {
1689 auto shift = mright.right().ResolvedValue();
1690 if (CanRevertLeftShiftWithRightShift<int32_t>(left, shift)) {
1691 node->ReplaceInput(0, Int32Constant(left << shift));
1692 node->ReplaceInput(1, mright.left().node());
1693 return Changed(node);
1694 }
1695 }
1696 }
1697 return NoChange();
1698}
1699
1701 const Operator* op, bool sign_extended) {
1702 switch (op->opcode()) {
1703 case IrOpcode::kInt64LessThan:
1704 return sign_extended ? machine()->Int32LessThan()
1705 : machine()->Uint32LessThan();
1706 case IrOpcode::kInt64LessThanOrEqual:
1707 return sign_extended ? machine()->Int32LessThanOrEqual()
1709 case IrOpcode::kUint64LessThan:
1710 return machine()->Uint32LessThan();
1711 case IrOpcode::kUint64LessThanOrEqual:
1712 return machine()->Uint32LessThanOrEqual();
1713 default:
1714 UNREACHABLE();
1715 }
1716}
1717
1719 DCHECK(node->opcode() == IrOpcode::kInt64LessThan ||
1720 node->opcode() == IrOpcode::kInt64LessThanOrEqual ||
1721 node->opcode() == IrOpcode::kUint64LessThan ||
1722 node->opcode() == IrOpcode::kUint64LessThanOrEqual);
1723 Int64BinopMatcher m(node);
1724
1725 bool sign_extended =
1726 m.left().IsChangeInt32ToInt64() && m.right().IsChangeInt32ToInt64();
1727 if (sign_extended || (m.left().IsChangeUint32ToUint64() &&
1728 m.right().IsChangeUint32ToUint64())) {
1729 node->ReplaceInput(0, NodeProperties::GetValueInput(m.left().node(), 0));
1730 node->ReplaceInput(1, NodeProperties::GetValueInput(m.right().node(), 0));
1732 Map64To32Comparison(node->op(), sign_extended));
1733 return Changed(node).FollowedBy(Reduce(node));
1734 }
1735
1736 // (x >> K) < (y >> K) => x < y if only zeros shifted out
1737 // This is useful for Smi untagging, which results in such a shift.
1738 if (m.left().op() == machine()->Word64SarShiftOutZeros() &&
1739 m.right().op() == machine()->Word64SarShiftOutZeros()) {
1740 Int64BinopMatcher mleft(m.left().node());
1741 Int64BinopMatcher mright(m.right().node());
1742 if (mleft.right().HasResolvedValue() &&
1743 mright.right().Is(mleft.right().ResolvedValue())) {
1744 node->ReplaceInput(0, mleft.left().node());
1745 node->ReplaceInput(1, mright.left().node());
1746 return Changed(node);
1747 }
1748 }
1749
1750 // Simplifying (x >> n) <= k into x <= (k << n), with "k << n" being
1751 // computed here at compile time.
1752 if (m.right().HasResolvedValue() &&
1753 m.left().op() == machine()->Word64SarShiftOutZeros() &&
1754 m.left().node()->UseCount() == 1) {
1755 Int64BinopMatcher mleft(m.left().node());
1756 uint64_t right = m.right().ResolvedValue();
1757 if (mleft.right().HasResolvedValue()) {
1758 auto shift = mleft.right().ResolvedValue();
1759 if (CanRevertLeftShiftWithRightShift<int64_t>(right, shift)) {
1760 sign_extended = mleft.left().IsChangeInt32ToInt64();
1761 uint64_t value = right << shift;
1762 // Reducing to 32-bit comparison when possible.
1763 if ((sign_extended || mleft.left().IsChangeUint32ToUint64()) &&
1764 CanTruncate(static_cast<int64_t>(value))) {
1766 node, Map64To32Comparison(node->op(), sign_extended));
1767 node->ReplaceInput(0, mleft.left().node()->InputAt(0));
1768 node->ReplaceInput(1, Int32Constant(static_cast<int32_t>(value)));
1769 return Changed(node).FollowedBy(Reduce(node));
1770 }
1771 node->ReplaceInput(0, mleft.left().node());
1772 node->ReplaceInput(1, Int64Constant(value));
1773 return Changed(node);
1774 }
1775 }
1776 }
1777
1778 // Simplifying k <= (x >> n) into (k << n) <= x, with "k << n" being
1779 // computed here at compile time.
1780 if (m.left().HasResolvedValue() &&
1781 m.right().op() == machine()->Word64SarShiftOutZeros() &&
1782 m.right().node()->UseCount() == 1) {
1783 uint64_t left = m.left().ResolvedValue();
1784 Int64BinopMatcher mright(m.right().node());
1785 if (mright.right().HasResolvedValue()) {
1786 auto shift = mright.right().ResolvedValue();
1787 if (CanRevertLeftShiftWithRightShift<int64_t>(left, shift)) {
1788 sign_extended = mright.left().IsChangeInt32ToInt64();
1789 uint64_t value = left << shift;
1790 // Reducing to 32-bit comparison when possible.
1791 if ((sign_extended || mright.left().IsChangeUint32ToUint64()) &&
1792 CanTruncate(static_cast<int64_t>(value))) {
1794 node, Map64To32Comparison(node->op(), sign_extended));
1795 node->ReplaceInput(0, Int32Constant(static_cast<int32_t>(value)));
1796 node->ReplaceInput(1, mright.left().node()->InputAt(0));
1797 return Changed(node).FollowedBy(Reduce(node));
1798 }
1799 node->ReplaceInput(0, Int64Constant(value));
1800 node->ReplaceInput(1, mright.left().node());
1801 return Changed(node);
1802 }
1803 }
1804 }
1805
1806 /*
1807 If Int64Constant(c) can be casted from an Int32Constant:
1808 -------------------------------------------------
1809 Int64LessThan(Int32ToInt64(a), Int64Constant(c))
1810 ====>
1811 Int32LessThan(a,Int32Constant(c))
1812 -------------------------------------------------
1813 */
1814 if (node->opcode() == IrOpcode::kInt64LessThan ||
1815 node->opcode() == IrOpcode::kInt64LessThanOrEqual) {
1816 // Int64LessThan(Int32ToInt64(a), Int64Constant(c))
1817 if (m.left().IsChangeInt32ToInt64() && m.right().HasResolvedValue()) {
1818 int64_t right_value = static_cast<int64_t>(m.right().ResolvedValue());
1819 // Int64Constant can be casted from an Int32Constant
1820 if (right_value == static_cast<int32_t>(right_value)) {
1821 const Operator* new_op;
1822
1823 if (node->opcode() == IrOpcode::kInt64LessThan) {
1824 new_op = machine()->Int32LessThan();
1825 } else {
1826 new_op = machine()->Int32LessThanOrEqual();
1827 }
1828 NodeProperties::ChangeOp(node, new_op);
1829 node->ReplaceInput(0, m.left().InputAt(0));
1830 node->ReplaceInput(1, Int32Constant(static_cast<int32_t>(right_value)));
1831 return Changed(node);
1832 } else if (right_value < std::numeric_limits<int32_t>::min()) {
1833 // left > right always
1834 node->TrimInputCount(0);
1836 return Changed(node);
1837 } else if (right_value > std::numeric_limits<int32_t>::max()) {
1838 // left < right always
1839 node->TrimInputCount(0);
1841 return Changed(node);
1842 }
1843 }
1844 // Int64LessThan(Int64Constant(c), Int32ToInt64(a))
1845 if (m.right().IsChangeInt32ToInt64() && m.left().HasResolvedValue()) {
1846 int64_t left_value = static_cast<int64_t>(m.left().ResolvedValue());
1847 // Int64Constant can be casted from an Int32Constant
1848 if (left_value == static_cast<int32_t>(left_value)) {
1849 const Operator* new_op;
1850
1851 if (node->opcode() == IrOpcode::kInt64LessThan) {
1852 new_op = machine()->Int32LessThan();
1853 } else {
1854 new_op = machine()->Int32LessThanOrEqual();
1855 }
1856 NodeProperties::ChangeOp(node, new_op);
1857 node->ReplaceInput(1, m.right().InputAt(0));
1858 node->ReplaceInput(0, Int32Constant(static_cast<int32_t>(left_value)));
1859 return Changed(node);
1860 } else if (left_value < std::numeric_limits<int32_t>::min()) {
1861 // left < right always
1862 node->TrimInputCount(0);
1864 return Changed(node);
1865 } else if (left_value > std::numeric_limits<int32_t>::max()) {
1866 // left > right always
1867 node->TrimInputCount(0);
1869 return Changed(node);
1870 }
1871 }
1872 }
1873
1874 /*
1875 If Uint64Constant(c) can be casted from an Uint32Constant:
1876 -------------------------------------------------
1877 Uint64LessThan(Uint32ToInt64(a), Uint64Constant(c))
1878 ====>
1879 Uint32LessThan(a,Uint32Constant(c))
1880 -------------------------------------------------
1881 */
1882 if (node->opcode() == IrOpcode::kUint64LessThan ||
1883 node->opcode() == IrOpcode::kUint64LessThanOrEqual) {
1884 // Uint64LessThan(Uint32ToInt64(a), Uint32Constant(c))
1885 if (m.left().IsChangeUint32ToUint64() && m.right().HasResolvedValue()) {
1886 uint64_t right_value = static_cast<uint64_t>(m.right().ResolvedValue());
1887 // Uint64Constant can be casted from an Uint32Constant
1888 if (right_value == static_cast<uint32_t>(right_value)) {
1889 const Operator* new_op;
1890
1891 if (node->opcode() == IrOpcode::kUint64LessThan) {
1892 new_op = machine()->Uint32LessThan();
1893 } else {
1894 new_op = machine()->Uint32LessThanOrEqual();
1895 }
1896 NodeProperties::ChangeOp(node, new_op);
1897 node->ReplaceInput(0, m.left().InputAt(0));
1898 node->ReplaceInput(1,
1899 Uint32Constant(static_cast<uint32_t>(right_value)));
1900 return Changed(node);
1901 } else {
1902 // left < right always
1903 node->TrimInputCount(0);
1905 return Changed(node);
1906 }
1907 }
1908 // Uint64LessThan(Uint64Constant(c), Uint32ToInt64(a))
1909 if (m.right().IsChangeUint32ToUint64() && m.left().HasResolvedValue()) {
1910 uint64_t left_value = static_cast<uint64_t>(m.left().ResolvedValue());
1911 // Uint64Constant can be casted from an Uint32Constant
1912 if (left_value == static_cast<uint32_t>(left_value)) {
1913 const Operator* new_op;
1914 if (node->opcode() == IrOpcode::kUint64LessThan) {
1915 new_op = machine()->Uint32LessThan();
1916 } else {
1917 new_op = machine()->Uint32LessThanOrEqual();
1918 }
1919 NodeProperties::ChangeOp(node, new_op);
1920 node->ReplaceInput(1, m.right().InputAt(0));
1921 node->ReplaceInput(0,
1922 Uint32Constant(static_cast<uint32_t>(left_value)));
1923 return Changed(node);
1924 } else {
1925 // left > right always
1926 node->TrimInputCount(0);
1928 return Changed(node);
1929 }
1930 }
1931 }
1932 return NoChange();
1933}
1934
1936 DCHECK((node->opcode() == IrOpcode::kWord32Shl) ||
1937 (node->opcode() == IrOpcode::kWord32Shr) ||
1938 (node->opcode() == IrOpcode::kWord32Sar));
1939 if (machine()->Word32ShiftIsSafe()) {
1940 // Remove the explicit 'and' with 0x1F if the shift provided by the machine
1941 // instruction matches that required by JavaScript.
1942 Int32BinopMatcher m(node);
1943 if (m.right().IsWord32And()) {
1944 Int32BinopMatcher mright(m.right().node());
1945 if (mright.right().Is(0x1F)) {
1946 node->ReplaceInput(1, mright.left().node());
1947 return Changed(node);
1948 }
1949 }
1950 }
1951 return NoChange();
1952}
1953
1955 DCHECK_EQ(IrOpcode::kWord32Shl, node->opcode());
1956 Int32BinopMatcher m(node);
1957 if (m.right().Is(0)) return Replace(m.left().node()); // x << 0 => x
1958 if (m.IsFoldable()) { // K << K => K (K stands for arbitrary constants)
1959 return ReplaceInt32(base::ShlWithWraparound(m.left().ResolvedValue(),
1960 m.right().ResolvedValue()));
1961 }
1962 if (m.right().IsInRange(1, 31)) {
1963 if (m.left().IsWord32Sar() || m.left().IsWord32Shr()) {
1964 Int32BinopMatcher mleft(m.left().node());
1965
1966 // If x >> K only shifted out zeros:
1967 // (x >> K) << L => x if K == L
1968 // (x >> K) << L => x >> (K-L) if K > L
1969 // (x >> K) << L => x << (L-K) if K < L
1970 // Since this is used for Smi untagging, we currently only need it for
1971 // signed shifts.
1972 if (mleft.op() == machine()->Word32SarShiftOutZeros() &&
1973 mleft.right().IsInRange(1, 31)) {
1974 Node* x = mleft.left().node();
1975 int k = mleft.right().ResolvedValue();
1976 int l = m.right().ResolvedValue();
1977 if (k == l) {
1978 return Replace(x);
1979 } else if (k > l) {
1980 node->ReplaceInput(0, x);
1981 node->ReplaceInput(1, Uint32Constant(k - l));
1983 return Changed(node).FollowedBy(ReduceWord32Sar(node));
1984 } else {
1985 DCHECK(k < l);
1986 node->ReplaceInput(0, x);
1987 node->ReplaceInput(1, Uint32Constant(l - k));
1988 return Changed(node);
1989 }
1990 }
1991
1992 // (x >>> K) << K => x & ~(2^K - 1)
1993 // (x >> K) << K => x & ~(2^K - 1)
1994 if (mleft.right().Is(m.right().ResolvedValue())) {
1995 node->ReplaceInput(0, mleft.left().node());
1996 node->ReplaceInput(1,
1997 Uint32Constant(std::numeric_limits<uint32_t>::max()
1998 << m.right().ResolvedValue()));
2000 return Changed(node).FollowedBy(ReduceWord32And(node));
2001 }
2002 }
2003 }
2004 return ReduceWord32Shifts(node);
2005}
2006
2008 DCHECK_EQ(IrOpcode::kWord64Shl, node->opcode());
2009 Int64BinopMatcher m(node);
2010 if (m.right().Is(0)) return Replace(m.left().node()); // x << 0 => x
2011 if (m.IsFoldable()) { // K << K => K (K stands for arbitrary constants)
2012 return ReplaceInt64(base::ShlWithWraparound(m.left().ResolvedValue(),
2013 m.right().ResolvedValue()));
2014 }
2015 if (m.right().IsInRange(1, 63) &&
2016 (m.left().IsWord64Sar() || m.left().IsWord64Shr())) {
2017 Int64BinopMatcher mleft(m.left().node());
2018
2019 // If x >> K only shifted out zeros:
2020 // (x >> K) << L => x if K == L
2021 // (x >> K) << L => x >> (K-L) if K > L
2022 // (x >> K) << L => x << (L-K) if K < L
2023 // Since this is used for Smi untagging, we currently only need it for
2024 // signed shifts.
2025 if (mleft.op() == machine()->Word64SarShiftOutZeros() &&
2026 mleft.right().IsInRange(1, 63)) {
2027 Node* x = mleft.left().node();
2028 int64_t k = mleft.right().ResolvedValue();
2029 int64_t l = m.right().ResolvedValue();
2030 if (k == l) {
2031 return Replace(x);
2032 } else if (k > l) {
2033 node->ReplaceInput(0, x);
2034 node->ReplaceInput(1, Uint64Constant(k - l));
2036 return Changed(node).FollowedBy(ReduceWord64Sar(node));
2037 } else {
2038 DCHECK(k < l);
2039 node->ReplaceInput(0, x);
2040 node->ReplaceInput(1, Uint64Constant(l - k));
2041 return Changed(node);
2042 }
2043 }
2044
2045 // (x >>> K) << K => x & ~(2^K - 1)
2046 // (x >> K) << K => x & ~(2^K - 1)
2047 if (mleft.right().Is(m.right().ResolvedValue())) {
2048 node->ReplaceInput(0, mleft.left().node());
2049 node->ReplaceInput(1, Uint64Constant(std::numeric_limits<uint64_t>::max()
2050 << m.right().ResolvedValue()));
2052 return Changed(node).FollowedBy(ReduceWord64And(node));
2053 }
2054 }
2055 return NoChange();
2056}
2057
2059 Uint32BinopMatcher m(node);
2060 if (m.right().Is(0)) return Replace(m.left().node()); // x >>> 0 => x
2061 if (m.IsFoldable()) { // K >>> K => K (K stands for arbitrary constants)
2062 return ReplaceInt32(m.left().ResolvedValue() >>
2063 (m.right().ResolvedValue() & 31));
2064 }
2065 if (m.left().IsWord32And() && m.right().HasResolvedValue()) {
2066 Uint32BinopMatcher mleft(m.left().node());
2067 if (mleft.right().HasResolvedValue()) {
2068 uint32_t shift = m.right().ResolvedValue() & 31;
2069 uint32_t mask = mleft.right().ResolvedValue();
2070 if ((mask >> shift) == 0) {
2071 // (m >>> s) == 0 implies ((x & m) >>> s) == 0
2072 return ReplaceInt32(0);
2073 }
2074 }
2075 }
2076 return ReduceWord32Shifts(node);
2077}
2078
2080 DCHECK_EQ(IrOpcode::kWord64Shr, node->opcode());
2081 Uint64BinopMatcher m(node);
2082 if (m.right().Is(0)) return Replace(m.left().node()); // x >>> 0 => x
2083 if (m.IsFoldable()) { // K >> K => K (K stands for arbitrary constants)
2084 return ReplaceInt64(m.left().ResolvedValue() >>
2085 (m.right().ResolvedValue() & 63));
2086 }
2087 return NoChange();
2088}
2089
2091 Int32BinopMatcher m(node);
2092 if (m.right().Is(0)) return Replace(m.left().node()); // x >> 0 => x
2093 if (m.IsFoldable()) { // K >> K => K (K stands for arbitrary constants)
2094 return ReplaceInt32(m.left().ResolvedValue() >>
2095 (m.right().ResolvedValue() & 31));
2096 }
2097 if (m.left().IsWord32Shl()) {
2098 Int32BinopMatcher mleft(m.left().node());
2099 if (mleft.left().IsComparison()) {
2100 if (m.right().Is(31) && mleft.right().Is(31)) {
2101 // Comparison << 31 >> 31 => 0 - Comparison
2102 node->ReplaceInput(0, Int32Constant(0));
2103 node->ReplaceInput(1, mleft.left().node());
2105 return Changed(node).FollowedBy(ReduceInt32Sub(node));
2106 }
2107 } else if (mleft.left().IsLoad()) {
2108 LoadRepresentation const rep =
2109 LoadRepresentationOf(mleft.left().node()->op());
2110 if (m.right().Is(24) && mleft.right().Is(24) &&
2111 rep == MachineType::Int8()) {
2112 // Load[kMachInt8] << 24 >> 24 => Load[kMachInt8]
2113 return Replace(mleft.left().node());
2114 }
2115 if (m.right().Is(16) && mleft.right().Is(16) &&
2116 rep == MachineType::Int16()) {
2117 // Load[kMachInt16] << 16 >> 16 => Load[kMachInt8]
2118 return Replace(mleft.left().node());
2119 }
2120 }
2121 }
2122 return ReduceWord32Shifts(node);
2123}
2124
2126 Int64BinopMatcher m(node);
2127 if (m.right().Is(0)) return Replace(m.left().node()); // x >> 0 => x
2128 if (m.IsFoldable()) {
2129 return ReplaceInt64(m.left().ResolvedValue() >>
2130 (m.right().ResolvedValue() & 63));
2131 }
2132 return NoChange();
2133}
2134
2135template <typename WordNAdapter>
2137 using A = WordNAdapter;
2138 A a(this);
2139
2140 typename A::IntNBinopMatcher m(node);
2141 if (m.right().Is(0)) return Replace(m.right().node()); // x & 0 => 0
2142 if (m.right().Is(-1)) return Replace(m.left().node()); // x & -1 => x
2143 if (m.right().Is(1)) {
2144 // (x + x) & 1 => 0
2145 Node* left = m.left().node();
2146 while (left->opcode() == IrOpcode::kTruncateInt64ToInt32 ||
2147 left->opcode() == IrOpcode::kChangeInt32ToInt64 ||
2148 left->opcode() == IrOpcode::kChangeUint32ToUint64) {
2149 left = left->InputAt(0);
2150 }
2151 if ((left->opcode() == IrOpcode::kInt32Add ||
2152 left->opcode() == IrOpcode::kInt64Add) &&
2153 left->InputAt(0) == left->InputAt(1)) {
2154 return a.ReplaceIntN(0);
2155 }
2156 }
2157 if (m.left().IsComparison() && m.right().Is(1)) { // CMP & 1 => CMP
2158 return Replace(m.left().node());
2159 }
2160 if (m.IsFoldable()) { // K & K => K (K stands for arbitrary constants)
2161 return a.ReplaceIntN(m.left().ResolvedValue() & m.right().ResolvedValue());
2162 }
2163 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x & x => x
2164 if (A::IsWordNAnd(m.left()) && m.right().HasResolvedValue()) {
2165 typename A::IntNBinopMatcher mleft(m.left().node());
2166 if (mleft.right().HasResolvedValue()) { // (x & K) & K => x & K
2167 node->ReplaceInput(0, mleft.left().node());
2168 node->ReplaceInput(1, a.IntNConstant(m.right().ResolvedValue() &
2169 mleft.right().ResolvedValue()));
2170 return Changed(node).FollowedBy(a.ReduceWordNAnd(node));
2171 }
2172 }
2173 if (m.right().IsNegativePowerOf2()) {
2174 typename A::intN_t const mask = m.right().ResolvedValue();
2175 typename A::intN_t const neg_mask = base::NegateWithWraparound(mask);
2176 if (A::IsWordNShl(m.left())) {
2177 typename A::UintNBinopMatcher mleft(m.left().node());
2178 if (mleft.right().HasResolvedValue() &&
2179 (mleft.right().ResolvedValue() & (A::WORD_SIZE - 1)) >=
2181 // (x << L) & (-1 << K) => x << L iff L >= K
2182 return Replace(mleft.node());
2183 }
2184 } else if (A::IsIntNAdd(m.left())) {
2185 typename A::IntNBinopMatcher mleft(m.left().node());
2186 if (mleft.right().HasResolvedValue() &&
2187 (mleft.right().ResolvedValue() & mask) ==
2188 mleft.right().ResolvedValue()) {
2189 // (x + (K << L)) & (-1 << L) => (x & (-1 << L)) + (K << L)
2190 node->ReplaceInput(0,
2191 a.WordNAnd(mleft.left().node(), m.right().node()));
2192 node->ReplaceInput(1, mleft.right().node());
2193 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
2194 return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
2195 }
2196 if (A::IsIntNMul(mleft.left())) {
2197 typename A::IntNBinopMatcher mleftleft(mleft.left().node());
2198 if (mleftleft.right().IsMultipleOf(neg_mask)) {
2199 // (y * (K << L) + x) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
2200 node->ReplaceInput(
2201 0, a.WordNAnd(mleft.right().node(), m.right().node()));
2202 node->ReplaceInput(1, mleftleft.node());
2203 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
2204 return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
2205 }
2206 }
2207 if (A::IsIntNMul(mleft.right())) {
2208 typename A::IntNBinopMatcher mleftright(mleft.right().node());
2209 if (mleftright.right().IsMultipleOf(neg_mask)) {
2210 // (x + y * (K << L)) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
2211 node->ReplaceInput(0,
2212 a.WordNAnd(mleft.left().node(), m.right().node()));
2213 node->ReplaceInput(1, mleftright.node());
2214 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
2215 return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
2216 }
2217 }
2218 if (A::IsWordNShl(mleft.left())) {
2219 typename A::IntNBinopMatcher mleftleft(mleft.left().node());
2220 if (mleftleft.right().Is(base::bits::CountTrailingZeros(mask))) {
2221 // (y << L + x) & (-1 << L) => (x & (-1 << L)) + y << L
2222 node->ReplaceInput(
2223 0, a.WordNAnd(mleft.right().node(), m.right().node()));
2224 node->ReplaceInput(1, mleftleft.node());
2225 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
2226 return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
2227 }
2228 }
2229 if (A::IsWordNShl(mleft.right())) {
2230 typename A::IntNBinopMatcher mleftright(mleft.right().node());
2231 if (mleftright.right().Is(base::bits::CountTrailingZeros(mask))) {
2232 // (x + y << L) & (-1 << L) => (x & (-1 << L)) + y << L
2233 node->ReplaceInput(0,
2234 a.WordNAnd(mleft.left().node(), m.right().node()));
2235 node->ReplaceInput(1, mleftright.node());
2236 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
2237 return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
2238 }
2239 }
2240 } else if (A::IsIntNMul(m.left())) {
2241 typename A::IntNBinopMatcher mleft(m.left().node());
2242 if (mleft.right().IsMultipleOf(neg_mask)) {
2243 // (x * (K << L)) & (-1 << L) => x * (K << L)
2244 return Replace(mleft.node());
2245 }
2246 }
2247 }
2248 return NoChange();
2249}
2250
2251template <typename WordNAdapter>
2253 using A = WordNAdapter;
2254 A a(this);
2255
2256 typename A::UintNBinopMatcher m(node);
2257 typename A::uintN_t kMaxUIntN =
2258 std::numeric_limits<typename A::uintN_t>::max();
2259 if (m.left().Is(0)) return ReplaceBool(true); // 0 <= x => true
2260 if (m.right().Is(kMaxUIntN)) return ReplaceBool(true); // x <= M => true
2261 if (m.IsFoldable()) { // K <= K => K (K stands for arbitrary constants)
2262 return ReplaceBool(m.left().ResolvedValue() <= m.right().ResolvedValue());
2263 }
2264 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
2265 if (m.right().Is(0)) { // x <= 0 => x == 0
2266 NodeProperties::ChangeOp(node, a.WordNEqual(machine()));
2267 return Changed(node);
2268 }
2269 return a.ReduceWordNComparisons(node);
2270}
2271
2272namespace {
2273
2274// Represents an operation of the form `(source & mask) == masked_value`.
2275// where each bit set in masked_value also has to be set in mask.
2276struct BitfieldCheck {
2277 Node* const source;
2278 uint32_t const mask;
2279 uint32_t const masked_value;
2281
2282 BitfieldCheck(Node* source, uint32_t mask, uint32_t masked_value,
2283 bool truncate_from_64_bit)
2284 : source(source),
2285 mask(mask),
2288 CHECK_EQ(masked_value & ~mask, 0);
2289 }
2290
2291 static std::optional<BitfieldCheck> Detect(Node* node) {
2292 // There are two patterns to check for here:
2293 // 1. Single-bit checks: `(val >> shift) & 1`, where:
2294 // - the shift may be omitted, and/or
2295 // - the result may be truncated from 64 to 32
2296 // 2. Equality checks: `(val & mask) == expected`, where:
2297 // - val may be truncated from 64 to 32 before masking (see
2298 // ReduceWordEqualForConstantRhs)
2299 if (node->opcode() == IrOpcode::kWord32Equal) {
2300 Uint32BinopMatcher eq(node);
2301 if (eq.left().IsWord32And()) {
2302 Uint32BinopMatcher mand(eq.left().node());
2303 if (mand.right().HasResolvedValue() && eq.right().HasResolvedValue()) {
2304 uint32_t mask = mand.right().ResolvedValue();
2305 uint32_t masked_value = eq.right().ResolvedValue();
2306 if ((masked_value & ~mask) != 0) return {};
2307 if (mand.left().IsTruncateInt64ToInt32()) {
2308 return BitfieldCheck(
2309 NodeProperties::GetValueInput(mand.left().node(), 0), mask,
2310 masked_value, true);
2311 } else {
2312 return BitfieldCheck(mand.left().node(), mask, masked_value, false);
2313 }
2314 }
2315 }
2316 } else {
2317 if (node->opcode() == IrOpcode::kTruncateInt64ToInt32) {
2318 return TryDetectShiftAndMaskOneBit<Word64Adapter>(
2320 } else {
2321 return TryDetectShiftAndMaskOneBit<Word32Adapter>(node);
2322 }
2323 }
2324 return {};
2325 }
2326
2327 std::optional<BitfieldCheck> TryCombine(const BitfieldCheck& other) {
2328 if (source != other.source ||
2329 truncate_from_64_bit != other.truncate_from_64_bit) {
2330 return {};
2331 }
2332 uint32_t overlapping_bits = mask & other.mask;
2333 // It would be kind of strange to have any overlapping bits, but they can be
2334 // allowed as long as they don't require opposite values in the same
2335 // positions.
2336 if ((masked_value & overlapping_bits) !=
2337 (other.masked_value & overlapping_bits)) {
2338 return {};
2339 }
2340 return BitfieldCheck{source, mask | other.mask,
2341 masked_value | other.masked_value,
2343 }
2344
2345 private:
2346 template <typename WordNAdapter>
2347 static std::optional<BitfieldCheck> TryDetectShiftAndMaskOneBit(Node* node) {
2348 // Look for the pattern `(val >> shift) & 1`. The shift may be omitted.
2349 if (WordNAdapter::IsWordNAnd(NodeMatcher(node))) {
2350 typename WordNAdapter::IntNBinopMatcher mand(node);
2351 if (mand.right().HasResolvedValue() &&
2352 mand.right().ResolvedValue() == 1) {
2353 if (WordNAdapter::IsWordNShr(mand.left()) ||
2354 WordNAdapter::IsWordNSar(mand.left())) {
2355 typename WordNAdapter::UintNBinopMatcher shift(mand.left().node());
2356 if (shift.right().HasResolvedValue() &&
2357 shift.right().ResolvedValue() < 32u) {
2358 uint32_t mask = 1 << shift.right().ResolvedValue();
2359 return BitfieldCheck{shift.left().node(), mask, mask,
2360 WordNAdapter::WORD_SIZE == 64};
2361 }
2362 }
2363 return BitfieldCheck{mand.left().node(), 1, 1,
2364 WordNAdapter::WORD_SIZE == 64};
2365 }
2366 }
2367 return {};
2368 }
2369};
2370
2371} // namespace
2372
2374 DCHECK_EQ(IrOpcode::kWord32And, node->opcode());
2375 Reduction reduction = ReduceWordNAnd<Word32Adapter>(node);
2376 if (reduction.Changed()) {
2377 return reduction;
2378 }
2379
2380 // Attempt to detect multiple bitfield checks from the same bitfield struct
2381 // and fold them into a single check.
2382 Int32BinopMatcher m(node);
2383 if (auto right_bitfield = BitfieldCheck::Detect(m.right().node())) {
2384 if (auto left_bitfield = BitfieldCheck::Detect(m.left().node())) {
2385 if (auto combined_bitfield = left_bitfield->TryCombine(*right_bitfield)) {
2386 Node* source = combined_bitfield->source;
2387 if (combined_bitfield->truncate_from_64_bit) {
2388 source = TruncateInt64ToInt32(source);
2389 }
2390 node->ReplaceInput(0, Word32And(source, combined_bitfield->mask));
2391 node->ReplaceInput(1, Int32Constant(combined_bitfield->masked_value));
2393 return Changed(node).FollowedBy(ReduceWord32Equal(node));
2394 }
2395 }
2396 }
2397
2398 return NoChange();
2399}
2400
2402 DCHECK_EQ(IrOpcode::kWord64And, node->opcode());
2403 return ReduceWordNAnd<Word64Adapter>(node);
2404}
2405
2407 // Recognize rotation, we are matching and transforming as follows:
2408 // x << y | x >>> (32 - y) => x ror (32 - y)
2409 // x << (32 - y) | x >>> y => x ror y
2410 // x << y ^ x >>> (32 - y) => x ror (32 - y) if y & 31 != 0
2411 // x << (32 - y) ^ x >>> y => x ror y if y & 31 != 0
2412 // (As well as the commuted forms.)
2413 // Note the side condition for XOR: the optimization doesn't hold for
2414 // multiples of 32.
2415
2416 DCHECK(IrOpcode::kWord32Or == node->opcode() ||
2417 IrOpcode::kWord32Xor == node->opcode());
2418 Int32BinopMatcher m(node);
2419 Node* shl = nullptr;
2420 Node* shr = nullptr;
2421 if (m.left().IsWord32Shl() && m.right().IsWord32Shr()) {
2422 shl = m.left().node();
2423 shr = m.right().node();
2424 } else if (m.left().IsWord32Shr() && m.right().IsWord32Shl()) {
2425 shl = m.right().node();
2426 shr = m.left().node();
2427 } else {
2428 return NoChange();
2429 }
2430
2431 Int32BinopMatcher mshl(shl);
2432 Int32BinopMatcher mshr(shr);
2433 if (mshl.left().node() != mshr.left().node()) return NoChange();
2434
2435 if (mshl.right().HasResolvedValue() && mshr.right().HasResolvedValue()) {
2436 // Case where y is a constant.
2437 if (mshl.right().ResolvedValue() + mshr.right().ResolvedValue() != 32) {
2438 return NoChange();
2439 }
2440 if (node->opcode() == IrOpcode::kWord32Xor &&
2441 (mshl.right().ResolvedValue() & 31) == 0) {
2442 return NoChange();
2443 }
2444 } else {
2445 Node* sub = nullptr;
2446 Node* y = nullptr;
2447 if (mshl.right().IsInt32Sub()) {
2448 sub = mshl.right().node();
2449 y = mshr.right().node();
2450 } else if (mshr.right().IsInt32Sub()) {
2451 sub = mshr.right().node();
2452 y = mshl.right().node();
2453 } else {
2454 return NoChange();
2455 }
2456
2457 Int32BinopMatcher msub(sub);
2458 if (!msub.left().Is(32) || msub.right().node() != y) return NoChange();
2459 if (node->opcode() == IrOpcode::kWord32Xor) {
2460 return NoChange(); // Can't guarantee y & 31 != 0.
2461 }
2462 }
2463
2464 node->ReplaceInput(0, mshl.left().node());
2465 node->ReplaceInput(1, mshr.right().node());
2466 NodeProperties::ChangeOp(node, machine()->Word32Ror());
2467 return Changed(node);
2468}
2469
2470template <typename WordNAdapter>
2472 using A = WordNAdapter;
2473 A a(this);
2474
2475 typename A::IntNBinopMatcher m(node);
2476 if (m.right().Is(0)) return Replace(m.left().node()); // x | 0 => x
2477 if (m.right().Is(-1)) return Replace(m.right().node()); // x | -1 => -1
2478 if (m.IsFoldable()) { // K | K => K (K stands for arbitrary constants)
2479 return a.ReplaceIntN(m.left().ResolvedValue() | m.right().ResolvedValue());
2480 }
2481 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x | x => x
2482
2483 // (x & K1) | K2 => x | K2 if K2 has ones for every zero bit in K1.
2484 // This case can be constructed by UpdateWord and UpdateWord32 in CSA.
2485 if (m.right().HasResolvedValue()) {
2486 if (A::IsWordNAnd(m.left())) {
2487 typename A::IntNBinopMatcher mand(m.left().node());
2488 if (mand.right().HasResolvedValue()) {
2489 if ((m.right().ResolvedValue() | mand.right().ResolvedValue()) == -1) {
2490 node->ReplaceInput(0, mand.left().node());
2491 return Changed(node);
2492 }
2493 }
2494 }
2495 }
2496
2497 return a.TryMatchWordNRor(node);
2498}
2499
2501 DCHECK_EQ(IrOpcode::kWord32Or, node->opcode());
2502 return ReduceWordNOr<Word32Adapter>(node);
2503}
2504
2506 DCHECK_EQ(IrOpcode::kWord64Or, node->opcode());
2507 return ReduceWordNOr<Word64Adapter>(node);
2508}
2509
2510template <typename WordNAdapter>
2512 using A = WordNAdapter;
2513 A a(this);
2514
2515 typename A::IntNBinopMatcher m(node);
2516 if (m.right().Is(0)) return Replace(m.left().node()); // x ^ 0 => x
2517 if (m.IsFoldable()) { // K ^ K => K (K stands for arbitrary constants)
2518 return a.ReplaceIntN(m.left().ResolvedValue() ^ m.right().ResolvedValue());
2519 }
2520 if (m.LeftEqualsRight()) return Replace(a.IntNConstant(0)); // x ^ x => 0
2521 if (A::IsWordNXor(m.left()) && m.right().Is(-1)) {
2522 typename A::IntNBinopMatcher mleft(m.left().node());
2523 if (mleft.right().Is(-1)) { // (x ^ -1) ^ -1 => x
2524 return Replace(mleft.left().node());
2525 }
2526 }
2527
2528 return a.TryMatchWordNRor(node);
2529}
2530
2532 DCHECK_EQ(IrOpcode::kWord32Xor, node->opcode());
2533 Int32BinopMatcher m(node);
2534 if (m.right().IsWord32Equal() && m.left().Is(1)) {
2535 return Replace(Word32Equal(m.right().node(), Int32Constant(0)));
2536 }
2537 return ReduceWordNXor<Word32Adapter>(node);
2538}
2539
2541 DCHECK_EQ(IrOpcode::kWord64Xor, node->opcode());
2542 return ReduceWordNXor<Word64Adapter>(node);
2543}
2544
2546 Int32BinopMatcher m(node);
2547 if (m.IsFoldable()) { // K == K => K (K stands for arbitrary constants)
2548 return ReplaceBool(m.left().ResolvedValue() == m.right().ResolvedValue());
2549 }
2550 if (m.left().IsInt32Sub() && m.right().Is(0)) { // x - y == 0 => x == y
2551 Int32BinopMatcher msub(m.left().node());
2552 node->ReplaceInput(0, msub.left().node());
2553 node->ReplaceInput(1, msub.right().node());
2554 return Changed(node);
2555 }
2556 // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
2557 if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
2558 if (m.right().HasResolvedValue()) {
2559 std::optional<std::pair<Node*, uint32_t>> replacements;
2560 if (m.left().IsTruncateInt64ToInt32()) {
2562 NodeProperties::GetValueInput(m.left().node(), 0),
2563 static_cast<uint32_t>(m.right().ResolvedValue()));
2564 } else {
2566 m.left().node(), static_cast<uint32_t>(m.right().ResolvedValue()));
2567 }
2568 if (replacements) {
2569 node->ReplaceInput(0, replacements->first);
2570 node->ReplaceInput(1, Uint32Constant(replacements->second));
2571 return Changed(node);
2572 }
2573
2574 // Simplifying (x+k1)==k2 into x==k2-k1.
2575 if (m.left().IsInt32Add() && m.right().IsInt32Constant()) {
2576 Int32AddMatcher m_add(m.left().node());
2577 if (m_add.right().IsInt32Constant()) {
2578 int32_t lte_right = m.right().ResolvedValue();
2579 int32_t add_right = m_add.right().ResolvedValue();
2580 // No need to consider overflow in this condition (==).
2581 node->ReplaceInput(0, m_add.left().node());
2582 node->ReplaceInput(1, Int32Constant(static_cast<uint32_t>(lte_right) -
2583 static_cast<uint32_t>(add_right)));
2584 return Changed(node);
2585 }
2586 }
2587 }
2588
2589 return NoChange();
2590}
2591
2593 Int64BinopMatcher m(node);
2594 if (m.IsFoldable()) { // K == K => K (K stands for arbitrary constants)
2595 return ReplaceBool(m.left().ResolvedValue() == m.right().ResolvedValue());
2596 }
2597 if (m.left().IsInt64Sub() && m.right().Is(0)) { // x - y == 0 => x == y
2598 Int64BinopMatcher msub(m.left().node());
2599 node->ReplaceInput(0, msub.left().node());
2600 node->ReplaceInput(1, msub.right().node());
2601 return Changed(node);
2602 }
2603 // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
2604 if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
2605 if (m.right().HasResolvedValue()) {
2606 std::optional<std::pair<Node*, uint64_t>> replacements =
2608 m.left().node(), static_cast<uint64_t>(m.right().ResolvedValue()));
2609 if (replacements) {
2610 node->ReplaceInput(0, replacements->first);
2611 node->ReplaceInput(1, Uint64Constant(replacements->second));
2612 return Changed(node);
2613 }
2614
2615 // Simplifying (x+k1)==k2 into x==k2-k1.
2616 if (m.left().IsInt64Add() && m.right().IsInt64Constant()) {
2617 Int64AddMatcher m_add(m.left().node());
2618 if (m_add.right().IsInt64Constant()) {
2619 int64_t lte_right = m.right().ResolvedValue();
2620 int64_t add_right = m_add.right().ResolvedValue();
2621 // No need to consider overflow in this condition (==).
2622 node->ReplaceInput(0, m_add.left().node());
2623 node->ReplaceInput(1, Int64Constant(static_cast<uint64_t>(lte_right) -
2624 static_cast<uint64_t>(add_right)));
2625 return Changed(node);
2626 }
2627 }
2628
2629 /*
2630 If Int64Constant(c) can be casted from an Int32Constant:
2631 -------------------------------------------------
2632 Word64Equal(Int32ToInt64(a), Int64Constant(c))
2633 ====>
2634 Word32Equal(a,Int32Constant(c))
2635 -------------------------------------------------
2636 */
2637 if (m.left().IsChangeInt32ToInt64()) {
2638 int64_t right_value = m.right().ResolvedValue();
2639 // Int64Constant can be casted from an Int32Constant
2640 if (right_value == static_cast<int32_t>(right_value)) {
2642 node->ReplaceInput(0, m.left().InputAt(0));
2643 node->ReplaceInput(1, Int32Constant(static_cast<int32_t>(right_value)));
2644 return Changed(node);
2645 } else {
2646 // Always false, change node op to zero(false).
2647 node->TrimInputCount(0);
2649 return Changed(node);
2650 }
2651 }
2652 }
2653
2654 return NoChange();
2655}
2656
2658 DCHECK_EQ(IrOpcode::kFloat64InsertLowWord32, node->opcode());
2659 Float64Matcher mlhs(node->InputAt(0));
2660 Uint32Matcher mrhs(node->InputAt(1));
2661 if (mlhs.HasResolvedValue() && mrhs.HasResolvedValue()) {
2662 return ReplaceFloat64(
2664 uint64_t{0xFFFFFFFF00000000}) |
2665 mrhs.ResolvedValue()));
2666 }
2667 return NoChange();
2668}
2669
2671 DCHECK_EQ(IrOpcode::kFloat64InsertHighWord32, node->opcode());
2672 Float64Matcher mlhs(node->InputAt(0));
2673 Uint32Matcher mrhs(node->InputAt(1));
2674 if (mlhs.HasResolvedValue() && mrhs.HasResolvedValue()) {
2677 uint64_t{0xFFFFFFFF}) |
2678 (static_cast<uint64_t>(mrhs.ResolvedValue()) << 32)));
2679 }
2680 return NoChange();
2681}
2682
2683namespace {
2684
2685bool IsFloat64RepresentableAsFloat32(const Float64Matcher& m) {
2686 if (m.HasResolvedValue()) {
2687 double v = m.ResolvedValue();
2688 return DoubleToFloat32(v) == v;
2689 }
2690 return false;
2691}
2692
2693} // namespace
2694
2696 DCHECK(IrOpcode::kFloat64Equal == node->opcode() ||
2697 IrOpcode::kFloat64LessThan == node->opcode() ||
2698 IrOpcode::kFloat64LessThanOrEqual == node->opcode());
2699 Float64BinopMatcher m(node);
2700 if (m.IsFoldable()) {
2701 switch (node->opcode()) {
2702 case IrOpcode::kFloat64Equal:
2703 return ReplaceBool(m.left().ResolvedValue() ==
2704 m.right().ResolvedValue());
2705 case IrOpcode::kFloat64LessThan:
2706 return ReplaceBool(m.left().ResolvedValue() <
2707 m.right().ResolvedValue());
2708 case IrOpcode::kFloat64LessThanOrEqual:
2709 return ReplaceBool(m.left().ResolvedValue() <=
2710 m.right().ResolvedValue());
2711 default:
2712 UNREACHABLE();
2713 }
2714 } else if ((m.left().IsChangeFloat32ToFloat64() &&
2715 m.right().IsChangeFloat32ToFloat64()) ||
2716 (m.left().IsChangeFloat32ToFloat64() &&
2717 IsFloat64RepresentableAsFloat32(m.right())) ||
2718 (IsFloat64RepresentableAsFloat32(m.left()) &&
2719 m.right().IsChangeFloat32ToFloat64())) {
2720 // As all Float32 values have an exact representation in Float64, comparing
2721 // two Float64 values both converted from Float32 is equivalent to comparing
2722 // the original Float32s, so we can ignore the conversions. We can also
2723 // reduce comparisons of converted Float64 values against constants that
2724 // can be represented exactly as Float32.
2725 switch (node->opcode()) {
2726 case IrOpcode::kFloat64Equal:
2727 NodeProperties::ChangeOp(node, machine()->Float32Equal());
2728 break;
2729 case IrOpcode::kFloat64LessThan:
2730 NodeProperties::ChangeOp(node, machine()->Float32LessThan());
2731 break;
2732 case IrOpcode::kFloat64LessThanOrEqual:
2733 NodeProperties::ChangeOp(node, machine()->Float32LessThanOrEqual());
2734 break;
2735 default:
2736 UNREACHABLE();
2737 }
2738 node->ReplaceInput(
2739 0, m.left().HasResolvedValue()
2740 ? Float32Constant(static_cast<float>(m.left().ResolvedValue()))
2741 : m.left().InputAt(0));
2742 node->ReplaceInput(
2743 1, m.right().HasResolvedValue()
2744 ? Float32Constant(static_cast<float>(m.right().ResolvedValue()))
2745 : m.right().InputAt(0));
2746 return Changed(node);
2747 }
2748 return NoChange();
2749}
2750
2752 DCHECK_EQ(IrOpcode::kFloat64RoundDown, node->opcode());
2753 Float64Matcher m(node->InputAt(0));
2754 if (m.HasResolvedValue()) {
2755 return ReplaceFloat64(std::floor(m.ResolvedValue()));
2756 }
2757 return NoChange();
2758}
2759
2760namespace {
2761
2762// Returns true if |node| is a constant whose value is 0.
2763bool IsZero(Node* node) {
2764 switch (node->opcode()) {
2765#define CASE_IS_ZERO(opcode, matcher) \
2766 case IrOpcode::opcode: { \
2767 matcher m(node); \
2768 return m.Is(0); \
2769 }
2770 CASE_IS_ZERO(kInt32Constant, Int32Matcher)
2771 CASE_IS_ZERO(kInt64Constant, Int64Matcher)
2772#undef CASE_IS_ZERO
2773 default:
2774 break;
2775 }
2776 return false;
2777}
2778
2779// If |node| is of the form "x == 0", then return "x" (in order to remove the
2780// "== 0" part).
2781std::optional<Node*> TryGetInvertedCondition(Node* cond) {
2782 if (cond->opcode() == IrOpcode::kWord32Equal) {
2783 Int32BinopMatcher m(cond);
2784 if (IsZero(m.right().node())) {
2785 return m.left().node();
2786 }
2787 }
2788 return std::nullopt;
2789}
2790
2791struct SimplifiedCondition {
2794};
2795
2796// Tries to simplifies |cond| by removing all top-level "== 0". Everytime such a
2797// construction is removed, the meaning of the comparison is inverted. This is
2798// recorded by the variable |is_inverted| throughout this function, and returned
2799// at the end. If |is_inverted| is true at the end, the caller should invert the
2800// if/else branches following the comparison.
2801std::optional<SimplifiedCondition> TrySimplifyCompareZero(Node* cond) {
2802 bool is_inverted = false;
2803 bool changed = false;
2804 std::optional<Node*> new_cond;
2805 while ((new_cond = TryGetInvertedCondition(cond)).has_value()) {
2806 cond = *new_cond;
2808 changed = true;
2809 }
2810 if (changed) {
2811 return SimplifiedCondition{cond, is_inverted};
2812 } else {
2813 return {};
2814 }
2815}
2816
2817/*
2818Remove WordEqual after WordAnd if it aims to test a bit.
2819For Example:
2820------------------------
2821691: Int32Constant[8]
28221857: Word32And(1838,691)
28231858: Word32Equal(1857,691)
28241859: Branch(1858,2141)
2825======>
2826691: Int32Constant[8]
28271857: Word32And(1838,691)
28281859: Branch(1857,2141)
2829------------------------
2830
2831Assembly code:
2832------------------------
2833andl r9,0x8
2834cmpb r9l,0x8
2835jz 0x7f242017bf3c
2836======>
2837testb r9,0x8
2838jnz 0x7f56c017be2e
2839------------------------
2840*/
2841Node* TrySimplifyCompareForTestBit(Node* cond) {
2842 if (cond->opcode() != IrOpcode::kWord32Equal) {
2843 return nullptr;
2844 }
2845 Node* word_equal_left = cond->InputAt(0);
2846 Node* word_equal_right = cond->InputAt(1);
2847
2848 if (word_equal_left->opcode() != IrOpcode::kWord32And ||
2849 word_equal_right->opcode() != IrOpcode::kInt32Constant) {
2850 return nullptr;
2851 }
2852
2853 Node* word_and_right = word_equal_left->InputAt(1);
2854 if (word_and_right->opcode() != IrOpcode::kInt32Constant) {
2855 return nullptr;
2856 }
2857 int32_t a = OpParameter<int32_t>(word_and_right->op());
2858 int32_t b = OpParameter<int32_t>(word_equal_right->op());
2859 if (a != b || !base::bits::IsPowerOfTwo(a)) {
2860 return nullptr;
2861 }
2862 DCHECK_EQ(word_equal_left->opcode(), IrOpcode::kWord32And);
2863 return word_equal_left;
2864}
2865
2866} // namespace
2867
2869 DCHECK_EQ(node->opcode(), IrOpcode::kBranch);
2870 for (Node* const use : node->uses()) {
2871 switch (use->opcode()) {
2872 case IrOpcode::kIfTrue:
2873 NodeProperties::ChangeOp(use, common()->IfFalse());
2874 break;
2875 case IrOpcode::kIfFalse:
2876 NodeProperties::ChangeOp(use, common()->IfTrue());
2877 break;
2878 default:
2879 UNREACHABLE();
2880 }
2881 }
2883 node, common()->Branch(NegateBranchHint(BranchHintOf(node->op()))));
2884}
2885
2886// If |node| is a branch, removes all top-level 32-bit "== 0" from |node|.
2888 Node* cond = node->InputAt(0);
2889 if (auto simplified = TrySimplifyCompareZero(cond)) {
2890 node->ReplaceInput(0, simplified->condition);
2891 if (simplified->is_inverted) {
2892 switch (node->opcode()) {
2893 case IrOpcode::kBranch:
2894 SwapBranches(node);
2895 break;
2896#if V8_ENABLE_WEBASSEMBLY
2897 case IrOpcode::kTrapIf: {
2898 const bool has_frame_state = node->op()->ValueInputCount() > 1;
2900 node,
2901 common()->TrapUnless(TrapIdOf(node->op()), has_frame_state));
2902 break;
2903 }
2904 case IrOpcode::kTrapUnless: {
2905 const bool has_frame_state = node->op()->ValueInputCount() > 1;
2907 node, common()->TrapIf(TrapIdOf(node->op()), has_frame_state));
2908 break;
2909 }
2910#endif // V8_ENABLE_WEBASSEMBLY
2911 case IrOpcode::kDeoptimizeIf: {
2914 node, common()->DeoptimizeUnless(p.reason(), p.feedback()));
2915 break;
2916 }
2917 case IrOpcode::kDeoptimizeUnless: {
2920 node, common()->DeoptimizeIf(p.reason(), p.feedback()));
2921 break;
2922 }
2923 default:
2924
2925 UNREACHABLE();
2926 }
2927 }
2928 return Changed(node);
2929 } else if (auto new_cond = TrySimplifyCompareForTestBit(cond)) {
2930 node->ReplaceInput(0, new_cond);
2931 return Changed(node);
2932 }
2933 return NoChange();
2934}
2935
2937 DCHECK(node->opcode() == IrOpcode::kBranch ||
2938 node->opcode() == IrOpcode::kDeoptimizeIf ||
2939 node->opcode() == IrOpcode::kDeoptimizeUnless ||
2940 node->opcode() == IrOpcode::kTrapIf ||
2941 node->opcode() == IrOpcode::kTrapUnless);
2942 // This reducer only applies operator reductions to the branch condition.
2943 // Reductions involving control flow happen elsewhere. Non-zero inputs are
2944 // considered true in all conditional ops.
2946 Reduction reduction = NoChange();
2947 if (condition.IsTruncateInt64ToInt32()) {
2948 if (auto replacement =
2950 NodeProperties::ReplaceValueInput(node, *replacement, 0);
2951 reduction = Changed(node);
2952 }
2953 } else if (auto replacement = ReduceConditionalN<Word32Adapter>(node)) {
2954 NodeProperties::ReplaceValueInput(node, *replacement, 0);
2955 reduction = Changed(node);
2956 }
2957 return reduction.FollowedBy(SimplifyBranch(node));
2958}
2959
2960template <typename WordNAdapter>
2963 // Branch conditions are 32-bit comparisons against zero, so they are the
2964 // opposite of a 32-bit `x == 0` node. To avoid repetition, we can reuse logic
2965 // for Word32Equal: if `x == 0` can reduce to `y == 0`, then branch(x) can
2966 // reduce to branch(y).
2968 condition.node(), 0);
2969 if (replacements && replacements->second == 0) return replacements->first;
2970 return {};
2971}
2972
2973template <typename WordNAdapter, typename uintN_t, typename intN_t>
2974std::optional<std::pair<Node*, uintN_t>>
2976 if (WordNAdapter::IsWordNAnd(NodeMatcher(lhs))) {
2977 typename WordNAdapter::UintNBinopMatcher mand(lhs);
2978 if ((WordNAdapter::IsWordNShr(mand.left()) ||
2979 WordNAdapter::IsWordNSar(mand.left())) &&
2980 mand.right().HasResolvedValue()) {
2981 typename WordNAdapter::UintNBinopMatcher mshift(mand.left().node());
2982 // ((x >> K1) & K2) == K3 => (x & (K2 << K1)) == (K3 << K1)
2983 if (mshift.right().HasResolvedValue()) {
2984 auto shift_bits = mshift.right().ResolvedValue();
2985 auto mask = mand.right().ResolvedValue();
2986 // Make sure that we won't shift data off the end, and that all of the
2987 // data ends up in the lower 32 bits for 64-bit mode.
2990 (std::is_same_v<uintN_t, uint64_t> ||
2991 mask << shift_bits <= std::numeric_limits<uintN_t>::max())) {
2992 Node* new_input = mshift.left().node();
2993 uintN_t new_mask = static_cast<uintN_t>(mask << shift_bits);
2994 uintN_t new_rhs = rhs << shift_bits;
2995 if (std::is_same_v<uintN_t, uint32_t> &&
2996 WordNAdapter::WORD_SIZE == 64) {
2997 // We can truncate before performing the And.
2998 new_input = TruncateInt64ToInt32(new_input);
2999 return std::make_pair(Word32And(new_input, new_mask), new_rhs);
3000 } else {
3001 WordNAdapter a(this);
3002 return std::make_pair(
3003 a.WordNAnd(new_input, a.UintNConstant(new_mask)), new_rhs);
3004 }
3005 }
3006 }
3007 }
3008 }
3009 // Replaces (x >> n) == k with x == k << n, with "k << n" being computed
3010 // here at compile time.
3011 if (std::is_same_v<intN_t, typename WordNAdapter::intN_t> &&
3012 WordNAdapter::IsWordNSarShiftOutZeros(lhs->op()) &&
3013 lhs->UseCount() == 1) {
3014 typename WordNAdapter::UintNBinopMatcher mshift(lhs);
3015 if (mshift.right().HasResolvedValue()) {
3016 intN_t shift = static_cast<intN_t>(mshift.right().ResolvedValue());
3017 if (CanRevertLeftShiftWithRightShift<intN_t>(rhs, shift)) {
3018 return std::make_pair(mshift.left().node(), rhs << shift);
3019 }
3020 }
3021 }
3022 return {};
3023}
3024
3028
3032
3034
3035} // namespace compiler
3036} // namespace internal
3037} // namespace v8
#define SIN_IMPL(X)
Definition builtins.h:470
#define COS_IMPL(X)
Definition builtins.h:471
constexpr int Sign() const
Definition double.h:120
constexpr MachineRepresentation representation() const
static constexpr MachineType Int32()
static constexpr MachineType Int16()
static constexpr MachineType Int8()
const FeedbackSource & feedback() const
CommonOperatorBuilder * common() const
MachineOperatorBuilder * machine() const
Node * Uint32Div(Node *dividend, uint32_t divisor)
Node * Int64Div(Node *dividend, int64_t divisor)
MachineOperatorReducer(Editor *editor, MachineGraph *mcgraph, SignallingNanPropagation signalling_nan_propagation)
Node * Uint64Div(Node *dividend, uint64_t divisor)
Node * Int32Div(Node *dividend, int32_t divisor)
const Operator * Map64To32Comparison(const Operator *op, bool sign_extended)
std::optional< std::pair< Node *, uintN_t > > ReduceWordEqualForConstantRhs(Node *lhs, uintN_t rhs)
static void ChangeOp(Node *node, const Operator *new_op)
static Node * GetValueInput(Node *node, int index)
static void ReplaceValueInput(Node *node, Node *value, int index)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
const Operator * op() const
Definition node.h:50
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
constexpr Opcode opcode() const
Definition operator.h:75
MachineRepresentation representation() const
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static const Operator * WordNEqual(MachineOperatorBuilder *machine)
Word32Adapter(MachineOperatorReducer *reducer)
static bool IsWordNSarShiftOutZeros(const Operator *op)
const Operator * IntNAdd(MachineOperatorBuilder *machine)
static bool IsWordNSarShiftOutZeros(const Operator *op)
static const Operator * WordNEqual(MachineOperatorBuilder *machine)
static const Operator * IntNAdd(MachineOperatorBuilder *machine)
Word64Adapter(MachineOperatorReducer *reducer)
#define V8_INFINITY
Definition globals.h:23
std::optional< TNode< JSArray > > a
Node * node
int x
uint32_t const mask
bool const truncate_from_64_bit
uint32_t const masked_value
#define CASE_IS_ZERO(opcode, matcher)
int m
Definition mul-fft.cc:294
int n
Definition mul-fft.cc:296
int int32_t
Definition unicode.cc:40
constexpr unsigned CountLeadingZeros(T value)
Definition bits.h:100
int32_t SignedDiv32(int32_t lhs, int32_t rhs)
Definition bits.cc:69
int64_t SignedDiv64(int64_t lhs, int64_t rhs)
Definition bits.cc:75
constexpr unsigned CountTrailingZeros(T value)
Definition bits.h:144
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
uint32_t UnsignedDiv32(uint32_t lhs, uint32_t rhs)
Definition bits.h:444
uint32_t UnsignedMod32(uint32_t lhs, uint32_t rhs)
Definition bits.h:456
constexpr uint32_t RotateRight32(uint32_t value, uint32_t shift)
Definition bits.h:274
int64_t SignedMod64(int64_t lhs, int64_t rhs)
Definition bits.cc:86
uint64_t UnsignedDiv64(uint64_t lhs, uint64_t rhs)
Definition bits.h:450
bool SignedAddOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
Definition bits.h:296
uint64_t UnsignedMod64(uint64_t lhs, uint64_t rhs)
Definition bits.h:462
int32_t SignedMod32(int32_t lhs, int32_t rhs)
Definition bits.cc:81
bool SignedSubOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
Definition bits.h:310
bool SignedMulOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
Definition bits.h:323
constexpr int WhichPowerOfTwo(T value)
Definition bits.h:195
double atanh(double x)
Definition ieee754.cc:1557
double tan(double x)
Definition ieee754.cc:2510
double log(double x)
Definition ieee754.cc:1638
double log2(double x)
Definition ieee754.cc:1973
double sinh(double x)
Definition ieee754.cc:2930
double atan2(double y, double x)
Definition ieee754.cc:1228
double cosh(double x)
Definition ieee754.cc:2554
double acosh(double x)
Definition ieee754.cc:936
double exp(double x)
Definition ieee754.cc:1447
double log10(double x)
Definition ieee754.cc:2079
double acos(double x)
Definition ieee754.cc:862
double log1p(double x)
Definition ieee754.cc:1783
double expm1(double x)
Definition ieee754.cc:2213
double tanh(double x)
Definition ieee754.cc:2986
double cbrt(double x)
Definition ieee754.cc:2333
double asinh(double x)
Definition ieee754.cc:1067
double asin(double x)
Definition ieee754.cc:991
double atan(double x)
Definition ieee754.cc:1116
int16_t MulWithWraparound(int16_t a, int16_t b)
MagicNumbersForDivision< T > UnsignedDivisionByConstant(T d, unsigned leading_zeros)
signed_type NegateWithWraparound(signed_type a)
V8_INLINE Dest bit_cast(Source const &source)
Definition macros.h:95
signed_type ShlWithWraparound(signed_type a, signed_type b)
T Divide(T x, T y)
MagicNumbersForDivision< T > SignedDivisionByConstant(T d)
BinopMatcher< Int32Matcher, Int32Matcher, MachineRepresentation::kWord32 > Int32BinopMatcher
size_t ProjectionIndexOf(const Operator *const op)
BinopMatcher< Int64Matcher, Int64Matcher, MachineRepresentation::kWord64 > Int64BinopMatcher
BranchHint BranchHintOf(const Operator *const op)
StoreRepresentation const & StoreRepresentationOf(Operator const *op)
BranchHint NegateBranchHint(BranchHint hint)
TNode< Float64T > Float64Add(TNode< Float64T > a, TNode< Float64T > b)
DeoptimizeParameters const & DeoptimizeParametersOf(Operator const *const op)
LoadRepresentation LoadRepresentationOf(Operator const *op)
BinopMatcher< Uint32Matcher, Uint32Matcher, MachineRepresentation::kWord32 > Uint32BinopMatcher
T const & OpParameter(const Operator *op)
Definition operator.h:214
UnalignedStoreRepresentation const & UnalignedStoreRepresentationOf(Operator const *op)
BinopMatcher< Uint64Matcher, Uint64Matcher, MachineRepresentation::kWord64 > Uint64BinopMatcher
double pow(double x, double y)
Definition ieee754.cc:14
std::make_unsigned< T >::type Abs(T a)
Definition utils.h:93
double Modulo(double x, double y)
Definition utils.h:105
unsigned int FastD2UI(double x)
double FastI2D(int x)
int32_t DoubleToInt32(double x)
V8_EXPORT_PRIVATE constexpr int ElementSizeLog2Of(MachineRepresentation)
float DoubleToFloat32(double x)
double FastUI2D(unsigned x)
constexpr int kMaxInt
Definition globals.h:374
constexpr int A
int FastD2IChecked(double x)
Definition conversions.h:91
constexpr uint32_t kMaxUInt32
Definition globals.h:387
static bool IsZero(const Operand &rt)
i::Address Load(i::Address address)
Definition unwinder.cc:19
#define shr(value, bits)
Definition sha-256.cc:31
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_EQ(lhs, rhs)
#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
bool Is(const T &value) const
const Operator * op() const
MachineGraph * mcgraph_