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 
5 #include "src/compiler/machine-operator-reducer.h"
6 #include <cmath>
7 #include <limits>
8 
9 #include "src/base/bits.h"
10 #include "src/base/division-by-constant.h"
11 #include "src/base/ieee754.h"
12 #include "src/base/overflowing-math.h"
13 #include "src/compiler/diamond.h"
14 #include "src/compiler/graph.h"
15 #include "src/compiler/machine-graph.h"
16 #include "src/compiler/node-matchers.h"
17 #include "src/compiler/node-properties.h"
18 #include "src/compiler/opcodes.h"
19 #include "src/numbers/conversions-inl.h"
20 
21 namespace v8 {
22 namespace internal {
23 namespace compiler {
24 
25 // Some optimizations performed by the MachineOperatorReducer can be applied
26 // to both Word32 and Word64 operations. Those are implemented in a generic
27 // way to be reused for different word sizes.
28 // This class adapts a generic algorithm to Word32 operations.
29 class Word32Adapter {
30  public:
31   using IntNBinopMatcher = Int32BinopMatcher;
32   using UintNBinopMatcher = Uint32BinopMatcher;
33   using intN_t = int32_t;
34   // WORD_SIZE refers to the N for which this adapter specializes.
35   static constexpr std::size_t WORD_SIZE = 32;
36 
Word32Adapter(MachineOperatorReducer * reducer)37   explicit Word32Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
38 
39   template <typename T>
IsWordNAnd(const T & x)40   static bool IsWordNAnd(const T& x) {
41     return x.IsWord32And();
42   }
43   template <typename T>
IsWordNShl(const T & x)44   static bool IsWordNShl(const T& x) {
45     return x.IsWord32Shl();
46   }
47   template <typename T>
IsWordNShr(const T & x)48   static bool IsWordNShr(const T& x) {
49     return x.IsWord32Shr();
50   }
51   template <typename T>
IsWordNSar(const T & x)52   static bool IsWordNSar(const T& x) {
53     return x.IsWord32Sar();
54   }
55   template <typename T>
IsWordNXor(const T & x)56   static bool IsWordNXor(const T& x) {
57     return x.IsWord32Xor();
58   }
59   template <typename T>
IsIntNAdd(const T & x)60   static bool IsIntNAdd(const T& x) {
61     return x.IsInt32Add();
62   }
63   template <typename T>
IsIntNMul(const T & x)64   static bool IsIntNMul(const T& x) {
65     return x.IsInt32Mul();
66   }
67 
IntNAdd(MachineOperatorBuilder * machine)68   const Operator* IntNAdd(MachineOperatorBuilder* machine) {
69     return machine->Int32Add();
70   }
71 
ReplaceIntN(int32_t value)72   Reduction ReplaceIntN(int32_t value) { return r_->ReplaceInt32(value); }
ReduceWordNAnd(Node * node)73   Reduction ReduceWordNAnd(Node* node) { return r_->ReduceWord32And(node); }
ReduceIntNAdd(Node * node)74   Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt32Add(node); }
TryMatchWordNRor(Node * node)75   Reduction TryMatchWordNRor(Node* node) { return r_->TryMatchWord32Ror(node); }
76 
IntNConstant(int32_t value)77   Node* IntNConstant(int32_t value) { return r_->Int32Constant(value); }
UintNConstant(uint32_t value)78   Node* UintNConstant(uint32_t value) { return r_->Uint32Constant(value); }
WordNAnd(Node * lhs,Node * rhs)79   Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word32And(lhs, rhs); }
80 
81  private:
82   MachineOperatorReducer* r_;
83 };
84 
85 // Some optimizations performed by the MachineOperatorReducer can be applied
86 // to both Word32 and Word64 operations. Those are implemented in a generic
87 // way to be reused for different word sizes.
88 // This class adapts a generic algorithm to Word64 operations.
89 class Word64Adapter {
90  public:
91   using IntNBinopMatcher = Int64BinopMatcher;
92   using UintNBinopMatcher = Uint64BinopMatcher;
93   using intN_t = int64_t;
94   // WORD_SIZE refers to the N for which this adapter specializes.
95   static constexpr std::size_t WORD_SIZE = 64;
96 
Word64Adapter(MachineOperatorReducer * reducer)97   explicit Word64Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
98 
99   template <typename T>
IsWordNAnd(const T & x)100   static bool IsWordNAnd(const T& x) {
101     return x.IsWord64And();
102   }
103   template <typename T>
IsWordNShl(const T & x)104   static bool IsWordNShl(const T& x) {
105     return x.IsWord64Shl();
106   }
107   template <typename T>
IsWordNShr(const T & x)108   static bool IsWordNShr(const T& x) {
109     return x.IsWord64Shr();
110   }
111   template <typename T>
IsWordNSar(const T & x)112   static bool IsWordNSar(const T& x) {
113     return x.IsWord64Sar();
114   }
115   template <typename T>
IsWordNXor(const T & x)116   static bool IsWordNXor(const T& x) {
117     return x.IsWord64Xor();
118   }
119   template <typename T>
IsIntNAdd(const T & x)120   static bool IsIntNAdd(const T& x) {
121     return x.IsInt64Add();
122   }
123   template <typename T>
IsIntNMul(const T & x)124   static bool IsIntNMul(const T& x) {
125     return x.IsInt64Mul();
126   }
127 
IntNAdd(MachineOperatorBuilder * machine)128   static const Operator* IntNAdd(MachineOperatorBuilder* machine) {
129     return machine->Int64Add();
130   }
131 
ReplaceIntN(int64_t value)132   Reduction ReplaceIntN(int64_t value) { return r_->ReplaceInt64(value); }
ReduceWordNAnd(Node * node)133   Reduction ReduceWordNAnd(Node* node) { return r_->ReduceWord64And(node); }
ReduceIntNAdd(Node * node)134   Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt64Add(node); }
TryMatchWordNRor(Node * node)135   Reduction TryMatchWordNRor(Node* node) {
136     // TODO(nicohartmann@): Add a MachineOperatorReducer::TryMatchWord64Ror.
137     return r_->NoChange();
138   }
139 
IntNConstant(int64_t value)140   Node* IntNConstant(int64_t value) { return r_->Int64Constant(value); }
UintNConstant(uint64_t value)141   Node* UintNConstant(uint64_t value) { return r_->Uint64Constant(value); }
WordNAnd(Node * lhs,Node * rhs)142   Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word64And(lhs, rhs); }
143 
144  private:
145   MachineOperatorReducer* r_;
146 };
147 
148 namespace {
149 
150 // TODO(jgruber): Consider replacing all uses of this function by
151 // std::numeric_limits<T>::quiet_NaN().
152 template <class T>
SilenceNaN(T x)153 T SilenceNaN(T x) {
154   DCHECK(std::isnan(x));
155   // Do some calculation to make a signalling NaN quiet.
156   return x - x;
157 }
158 
159 }  // namespace
160 
MachineOperatorReducer(Editor * editor,MachineGraph * mcgraph,bool allow_signalling_nan)161 MachineOperatorReducer::MachineOperatorReducer(Editor* editor,
162                                                MachineGraph* mcgraph,
163                                                bool allow_signalling_nan)
164     : AdvancedReducer(editor),
165       mcgraph_(mcgraph),
166       allow_signalling_nan_(allow_signalling_nan) {}
167 
168 MachineOperatorReducer::~MachineOperatorReducer() = default;
169 
170 
Float32Constant(volatile float value)171 Node* MachineOperatorReducer::Float32Constant(volatile float value) {
172   return graph()->NewNode(common()->Float32Constant(value));
173 }
174 
Float64Constant(volatile double value)175 Node* MachineOperatorReducer::Float64Constant(volatile double value) {
176   return mcgraph()->Float64Constant(value);
177 }
178 
Int32Constant(int32_t value)179 Node* MachineOperatorReducer::Int32Constant(int32_t value) {
180   return mcgraph()->Int32Constant(value);
181 }
182 
Int64Constant(int64_t value)183 Node* MachineOperatorReducer::Int64Constant(int64_t value) {
184   return graph()->NewNode(common()->Int64Constant(value));
185 }
186 
Float64Mul(Node * lhs,Node * rhs)187 Node* MachineOperatorReducer::Float64Mul(Node* lhs, Node* rhs) {
188   return graph()->NewNode(machine()->Float64Mul(), lhs, rhs);
189 }
190 
Float64PowHalf(Node * value)191 Node* MachineOperatorReducer::Float64PowHalf(Node* value) {
192   value =
193       graph()->NewNode(machine()->Float64Add(), Float64Constant(0.0), value);
194   Diamond d(graph(), common(),
195             graph()->NewNode(machine()->Float64LessThanOrEqual(), value,
196                              Float64Constant(-V8_INFINITY)),
197             BranchHint::kFalse);
198   return d.Phi(MachineRepresentation::kFloat64, Float64Constant(V8_INFINITY),
199                graph()->NewNode(machine()->Float64Sqrt(), value));
200 }
201 
Word32And(Node * lhs,Node * rhs)202 Node* MachineOperatorReducer::Word32And(Node* lhs, Node* rhs) {
203   Node* const node = graph()->NewNode(machine()->Word32And(), lhs, rhs);
204   Reduction const reduction = ReduceWord32And(node);
205   return reduction.Changed() ? reduction.replacement() : node;
206 }
207 
Word32Sar(Node * lhs,uint32_t rhs)208 Node* MachineOperatorReducer::Word32Sar(Node* lhs, uint32_t rhs) {
209   if (rhs == 0) return lhs;
210   return graph()->NewNode(machine()->Word32Sar(), lhs, Uint32Constant(rhs));
211 }
212 
Word32Shr(Node * lhs,uint32_t rhs)213 Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) {
214   if (rhs == 0) return lhs;
215   return graph()->NewNode(machine()->Word32Shr(), lhs, Uint32Constant(rhs));
216 }
217 
Word32Equal(Node * lhs,Node * rhs)218 Node* MachineOperatorReducer::Word32Equal(Node* lhs, Node* rhs) {
219   return graph()->NewNode(machine()->Word32Equal(), lhs, rhs);
220 }
221 
Word64And(Node * lhs,Node * rhs)222 Node* MachineOperatorReducer::Word64And(Node* lhs, Node* rhs) {
223   Node* const node = graph()->NewNode(machine()->Word64And(), lhs, rhs);
224   Reduction const reduction = ReduceWord64And(node);
225   return reduction.Changed() ? reduction.replacement() : node;
226 }
227 
Int32Add(Node * lhs,Node * rhs)228 Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) {
229   Node* const node = graph()->NewNode(machine()->Int32Add(), lhs, rhs);
230   Reduction const reduction = ReduceInt32Add(node);
231   return reduction.Changed() ? reduction.replacement() : node;
232 }
233 
Int32Sub(Node * lhs,Node * rhs)234 Node* MachineOperatorReducer::Int32Sub(Node* lhs, Node* rhs) {
235   Node* const node = graph()->NewNode(machine()->Int32Sub(), lhs, rhs);
236   Reduction const reduction = ReduceInt32Sub(node);
237   return reduction.Changed() ? reduction.replacement() : node;
238 }
239 
Int32Mul(Node * lhs,Node * rhs)240 Node* MachineOperatorReducer::Int32Mul(Node* lhs, Node* rhs) {
241   return graph()->NewNode(machine()->Int32Mul(), lhs, rhs);
242 }
243 
Int32Div(Node * dividend,int32_t divisor)244 Node* MachineOperatorReducer::Int32Div(Node* dividend, int32_t divisor) {
245   DCHECK_NE(0, divisor);
246   DCHECK_NE(std::numeric_limits<int32_t>::min(), divisor);
247   base::MagicNumbersForDivision<uint32_t> const mag =
248       base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
249   Node* quotient = graph()->NewNode(machine()->Int32MulHigh(), dividend,
250                                     Uint32Constant(mag.multiplier));
251   if (divisor > 0 && bit_cast<int32_t>(mag.multiplier) < 0) {
252     quotient = Int32Add(quotient, dividend);
253   } else if (divisor < 0 && bit_cast<int32_t>(mag.multiplier) > 0) {
254     quotient = Int32Sub(quotient, dividend);
255   }
256   return Int32Add(Word32Sar(quotient, mag.shift), Word32Shr(dividend, 31));
257 }
258 
Uint32Div(Node * dividend,uint32_t divisor)259 Node* MachineOperatorReducer::Uint32Div(Node* dividend, uint32_t divisor) {
260   DCHECK_LT(0u, divisor);
261   // If the divisor is even, we can avoid using the expensive fixup by shifting
262   // the dividend upfront.
263   unsigned const shift = base::bits::CountTrailingZeros(divisor);
264   dividend = Word32Shr(dividend, shift);
265   divisor >>= shift;
266   // Compute the magic number for the (shifted) divisor.
267   base::MagicNumbersForDivision<uint32_t> const mag =
268       base::UnsignedDivisionByConstant(divisor, shift);
269   Node* quotient = graph()->NewNode(machine()->Uint32MulHigh(), dividend,
270                                     Uint32Constant(mag.multiplier));
271   if (mag.add) {
272     DCHECK_LE(1u, mag.shift);
273     quotient = Word32Shr(
274         Int32Add(Word32Shr(Int32Sub(dividend, quotient), 1), quotient),
275         mag.shift - 1);
276   } else {
277     quotient = Word32Shr(quotient, mag.shift);
278   }
279   return quotient;
280 }
281 
TruncateInt64ToInt32(Node * value)282 Node* MachineOperatorReducer::TruncateInt64ToInt32(Node* value) {
283   Node* const node = graph()->NewNode(machine()->TruncateInt64ToInt32(), value);
284   Reduction const reduction = ReduceTruncateInt64ToInt32(node);
285   return reduction.Changed() ? reduction.replacement() : node;
286 }
287 
288 namespace {
ObjectsMayAlias(Node * a,Node * b)289 bool ObjectsMayAlias(Node* a, Node* b) {
290   if (a != b) {
291     if (NodeProperties::IsFreshObject(b)) std::swap(a, b);
292     if (NodeProperties::IsFreshObject(a) &&
293         (NodeProperties::IsFreshObject(b) ||
294          b->opcode() == IrOpcode::kParameter ||
295          b->opcode() == IrOpcode::kLoadImmutable ||
296          IrOpcode::IsConstantOpcode(b->opcode()))) {
297       return false;
298     }
299   }
300   return true;
301 }
302 }  // namespace
303 
304 // Perform constant folding and strength reduction on machine operators.
Reduce(Node * node)305 Reduction MachineOperatorReducer::Reduce(Node* node) {
306   switch (node->opcode()) {
307     case IrOpcode::kProjection:
308       return ReduceProjection(ProjectionIndexOf(node->op()), node->InputAt(0));
309     case IrOpcode::kWord32And:
310       return ReduceWord32And(node);
311     case IrOpcode::kWord64And:
312       return ReduceWord64And(node);
313     case IrOpcode::kWord32Or:
314       return ReduceWord32Or(node);
315     case IrOpcode::kWord64Or:
316       return ReduceWord64Or(node);
317     case IrOpcode::kWord32Xor:
318       return ReduceWord32Xor(node);
319     case IrOpcode::kWord64Xor:
320       return ReduceWord64Xor(node);
321     case IrOpcode::kWord32Shl:
322       return ReduceWord32Shl(node);
323     case IrOpcode::kWord64Shl:
324       return ReduceWord64Shl(node);
325     case IrOpcode::kWord32Shr:
326       return ReduceWord32Shr(node);
327     case IrOpcode::kWord64Shr:
328       return ReduceWord64Shr(node);
329     case IrOpcode::kWord32Sar:
330       return ReduceWord32Sar(node);
331     case IrOpcode::kWord64Sar:
332       return ReduceWord64Sar(node);
333     case IrOpcode::kWord32Ror: {
334       Int32BinopMatcher m(node);
335       if (m.right().Is(0)) return Replace(m.left().node());  // x ror 0 => x
336       if (m.IsFoldable()) {  // K ror K => K  (K stands for arbitrary constants)
337         return ReplaceInt32(base::bits::RotateRight32(
338             m.left().ResolvedValue(), m.right().ResolvedValue() & 31));
339       }
340       break;
341     }
342     case IrOpcode::kWord32Equal: {
343       return ReduceWord32Equal(node);
344     }
345     case IrOpcode::kWord64Equal: {
346       Int64BinopMatcher m(node);
347       if (m.IsFoldable()) {  // K == K => K  (K stands for arbitrary constants)
348         return ReplaceBool(m.left().ResolvedValue() ==
349                            m.right().ResolvedValue());
350       }
351       if (m.left().IsInt64Sub() && m.right().Is(0)) {  // x - y == 0 => x == y
352         Int64BinopMatcher msub(m.left().node());
353         node->ReplaceInput(0, msub.left().node());
354         node->ReplaceInput(1, msub.right().node());
355         return Changed(node);
356       }
357       // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
358       if (m.LeftEqualsRight()) return ReplaceBool(true);  // x == x => true
359       // This is a workaround for not having escape analysis for wasm
360       // (machine-level) turbofan graphs.
361       if (!ObjectsMayAlias(m.left().node(), m.right().node())) {
362         return ReplaceBool(false);
363       }
364       break;
365     }
366     case IrOpcode::kInt32Add:
367       return ReduceInt32Add(node);
368     case IrOpcode::kInt64Add:
369       return ReduceInt64Add(node);
370     case IrOpcode::kInt32Sub:
371       return ReduceInt32Sub(node);
372     case IrOpcode::kInt64Sub:
373       return ReduceInt64Sub(node);
374     case IrOpcode::kInt32Mul: {
375       Int32BinopMatcher m(node);
376       if (m.right().Is(0)) return Replace(m.right().node());  // x * 0 => 0
377       if (m.right().Is(1)) return Replace(m.left().node());   // x * 1 => x
378       if (m.IsFoldable()) {  // K * K => K  (K stands for arbitrary constants)
379         return ReplaceInt32(base::MulWithWraparound(m.left().ResolvedValue(),
380                                                     m.right().ResolvedValue()));
381       }
382       if (m.right().Is(-1)) {  // x * -1 => 0 - x
383         node->ReplaceInput(0, Int32Constant(0));
384         node->ReplaceInput(1, m.left().node());
385         NodeProperties::ChangeOp(node, machine()->Int32Sub());
386         return Changed(node);
387       }
388       if (m.right().IsPowerOf2()) {  // x * 2^n => x << n
389         node->ReplaceInput(1, Int32Constant(base::bits::WhichPowerOfTwo(
390                                   m.right().ResolvedValue())));
391         NodeProperties::ChangeOp(node, machine()->Word32Shl());
392         return Changed(node).FollowedBy(ReduceWord32Shl(node));
393       }
394       // (x * Int32Constant(a)) * Int32Constant(b)) => x * Int32Constant(a * b)
395       if (m.right().HasResolvedValue() && m.left().IsInt32Mul()) {
396         Int32BinopMatcher n(m.left().node());
397         if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
398           node->ReplaceInput(
399               1, Int32Constant(base::MulWithWraparound(
400                      m.right().ResolvedValue(), n.right().ResolvedValue())));
401           node->ReplaceInput(0, n.left().node());
402           return Changed(node);
403         }
404       }
405       break;
406     }
407     case IrOpcode::kInt32MulWithOverflow: {
408       Int32BinopMatcher m(node);
409       if (m.right().Is(2)) {
410         node->ReplaceInput(1, m.left().node());
411         NodeProperties::ChangeOp(node, machine()->Int32AddWithOverflow());
412         return Changed(node);
413       }
414       if (m.right().Is(-1)) {
415         node->ReplaceInput(0, Int32Constant(0));
416         node->ReplaceInput(1, m.left().node());
417         NodeProperties::ChangeOp(node, machine()->Int32SubWithOverflow());
418         return Changed(node);
419       }
420       break;
421     }
422     case IrOpcode::kInt64Mul:
423       return ReduceInt64Mul(node);
424     case IrOpcode::kInt32Div:
425       return ReduceInt32Div(node);
426     case IrOpcode::kUint32Div:
427       return ReduceUint32Div(node);
428     case IrOpcode::kInt32Mod:
429       return ReduceInt32Mod(node);
430     case IrOpcode::kUint32Mod:
431       return ReduceUint32Mod(node);
432     case IrOpcode::kInt32LessThan: {
433       Int32BinopMatcher m(node);
434       if (m.IsFoldable()) {  // K < K => K  (K stands for arbitrary constants)
435         return ReplaceBool(m.left().ResolvedValue() <
436                            m.right().ResolvedValue());
437       }
438       if (m.LeftEqualsRight()) return ReplaceBool(false);  // x < x => false
439       if (m.left().IsWord32Or() && m.right().Is(0)) {
440         // (x | K) < 0 => true or (K | x) < 0 => true iff K < 0
441         Int32BinopMatcher mleftmatcher(m.left().node());
442         if (mleftmatcher.left().IsNegative() ||
443             mleftmatcher.right().IsNegative()) {
444           return ReplaceBool(true);
445         }
446       }
447       return ReduceWord32Comparisons(node);
448     }
449     case IrOpcode::kInt32LessThanOrEqual: {
450       Int32BinopMatcher m(node);
451       if (m.IsFoldable()) {  // K <= K => K  (K stands for arbitrary constants)
452         return ReplaceBool(m.left().ResolvedValue() <=
453                            m.right().ResolvedValue());
454       }
455       if (m.LeftEqualsRight()) return ReplaceBool(true);  // x <= x => true
456       return ReduceWord32Comparisons(node);
457     }
458     case IrOpcode::kUint32LessThan: {
459       Uint32BinopMatcher m(node);
460       if (m.left().Is(kMaxUInt32)) return ReplaceBool(false);  // M < x => false
461       if (m.right().Is(0)) return ReplaceBool(false);          // x < 0 => false
462       if (m.IsFoldable()) {  // K < K => K  (K stands for arbitrary constants)
463         return ReplaceBool(m.left().ResolvedValue() <
464                            m.right().ResolvedValue());
465       }
466       if (m.LeftEqualsRight()) return ReplaceBool(false);  // x < x => false
467       if (m.left().IsWord32Sar() && m.right().HasResolvedValue()) {
468         Int32BinopMatcher mleft(m.left().node());
469         if (mleft.right().HasResolvedValue()) {
470           // (x >> K) < C => x < (C << K)
471           // when C < (M >> K)
472           const uint32_t c = m.right().ResolvedValue();
473           const uint32_t k = mleft.right().ResolvedValue() & 0x1F;
474           if (c < static_cast<uint32_t>(kMaxInt >> k)) {
475             node->ReplaceInput(0, mleft.left().node());
476             node->ReplaceInput(1, Uint32Constant(c << k));
477             return Changed(node);
478           }
479           // TODO(turbofan): else the comparison is always true.
480         }
481       }
482       return ReduceWord32Comparisons(node);
483     }
484     case IrOpcode::kUint32LessThanOrEqual: {
485       Uint32BinopMatcher m(node);
486       if (m.left().Is(0)) return ReplaceBool(true);            // 0 <= x => true
487       if (m.right().Is(kMaxUInt32)) return ReplaceBool(true);  // x <= M => true
488       if (m.IsFoldable()) {  // K <= K => K  (K stands for arbitrary constants)
489         return ReplaceBool(m.left().ResolvedValue() <=
490                            m.right().ResolvedValue());
491       }
492       if (m.LeftEqualsRight()) return ReplaceBool(true);  // x <= x => true
493       return ReduceWord32Comparisons(node);
494     }
495     case IrOpcode::kFloat32Sub: {
496       Float32BinopMatcher m(node);
497       if (allow_signalling_nan_ && m.right().Is(0) &&
498           (std::copysign(1.0, m.right().ResolvedValue()) > 0)) {
499         return Replace(m.left().node());  // x - 0 => x
500       }
501       if (m.right().IsNaN()) {  // x - NaN => NaN
502         return ReplaceFloat32(SilenceNaN(m.right().ResolvedValue()));
503       }
504       if (m.left().IsNaN()) {  // NaN - x => NaN
505         return ReplaceFloat32(SilenceNaN(m.left().ResolvedValue()));
506       }
507       if (m.IsFoldable()) {  // L - R => (L - R)
508         return ReplaceFloat32(m.left().ResolvedValue() -
509                               m.right().ResolvedValue());
510       }
511       if (allow_signalling_nan_ && m.left().IsMinusZero()) {
512         // -0.0 - round_down(-0.0 - R) => round_up(R)
513         if (machine()->Float32RoundUp().IsSupported() &&
514             m.right().IsFloat32RoundDown()) {
515           if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat32Sub) {
516             Float32BinopMatcher mright0(m.right().InputAt(0));
517             if (mright0.left().IsMinusZero()) {
518               return Replace(graph()->NewNode(machine()->Float32RoundUp().op(),
519                                               mright0.right().node()));
520             }
521           }
522         }
523         // -0.0 - R => -R
524         node->RemoveInput(0);
525         NodeProperties::ChangeOp(node, machine()->Float32Neg());
526         return Changed(node);
527       }
528       break;
529     }
530     case IrOpcode::kFloat64Add: {
531       Float64BinopMatcher m(node);
532       if (m.right().IsNaN()) {  // x + NaN => NaN
533         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
534       }
535       if (m.left().IsNaN()) {  // NaN + x => NaN
536         return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
537       }
538       if (m.IsFoldable()) {  // K + K => K  (K stands for arbitrary constants)
539         return ReplaceFloat64(m.left().ResolvedValue() +
540                               m.right().ResolvedValue());
541       }
542       break;
543     }
544     case IrOpcode::kFloat64Sub: {
545       Float64BinopMatcher m(node);
546       if (allow_signalling_nan_ && m.right().Is(0) &&
547           (base::Double(m.right().ResolvedValue()).Sign() > 0)) {
548         return Replace(m.left().node());  // x - 0 => x
549       }
550       if (m.right().IsNaN()) {  // x - NaN => NaN
551         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
552       }
553       if (m.left().IsNaN()) {  // NaN - x => NaN
554         return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
555       }
556       if (m.IsFoldable()) {  // L - R => (L - R)
557         return ReplaceFloat64(m.left().ResolvedValue() -
558                               m.right().ResolvedValue());
559       }
560       if (allow_signalling_nan_ && m.left().IsMinusZero()) {
561         // -0.0 - round_down(-0.0 - R) => round_up(R)
562         if (machine()->Float64RoundUp().IsSupported() &&
563             m.right().IsFloat64RoundDown()) {
564           if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat64Sub) {
565             Float64BinopMatcher mright0(m.right().InputAt(0));
566             if (mright0.left().IsMinusZero()) {
567               return Replace(graph()->NewNode(machine()->Float64RoundUp().op(),
568                                               mright0.right().node()));
569             }
570           }
571         }
572         // -0.0 - R => -R
573         node->RemoveInput(0);
574         NodeProperties::ChangeOp(node, machine()->Float64Neg());
575         return Changed(node);
576       }
577       break;
578     }
579     case IrOpcode::kFloat64Mul: {
580       Float64BinopMatcher m(node);
581       if (allow_signalling_nan_ && m.right().Is(1))
582         return Replace(m.left().node());  // x * 1.0 => x
583       if (m.right().Is(-1)) {  // x * -1.0 => -0.0 - x
584         node->ReplaceInput(0, Float64Constant(-0.0));
585         node->ReplaceInput(1, m.left().node());
586         NodeProperties::ChangeOp(node, machine()->Float64Sub());
587         return Changed(node);
588       }
589       if (m.right().IsNaN()) {                               // x * NaN => NaN
590         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
591       }
592       if (m.IsFoldable()) {  // K * K => K  (K stands for arbitrary constants)
593         return ReplaceFloat64(m.left().ResolvedValue() *
594                               m.right().ResolvedValue());
595       }
596       if (m.right().Is(2)) {  // x * 2.0 => x + x
597         node->ReplaceInput(1, m.left().node());
598         NodeProperties::ChangeOp(node, machine()->Float64Add());
599         return Changed(node);
600       }
601       break;
602     }
603     case IrOpcode::kFloat64Div: {
604       Float64BinopMatcher m(node);
605       if (allow_signalling_nan_ && m.right().Is(1))
606         return Replace(m.left().node());  // x / 1.0 => x
607       // TODO(ahaas): We could do x / 1.0 = x if we knew that x is not an sNaN.
608       if (m.right().IsNaN()) {                               // x / NaN => NaN
609         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
610       }
611       if (m.left().IsNaN()) {  // NaN / x => NaN
612         return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
613       }
614       if (m.IsFoldable()) {  // K / K => K  (K stands for arbitrary constants)
615         return ReplaceFloat64(
616             base::Divide(m.left().ResolvedValue(), m.right().ResolvedValue()));
617       }
618       if (allow_signalling_nan_ && m.right().Is(-1)) {  // x / -1.0 => -x
619         node->RemoveInput(1);
620         NodeProperties::ChangeOp(node, machine()->Float64Neg());
621         return Changed(node);
622       }
623       if (m.right().IsNormal() && m.right().IsPositiveOrNegativePowerOf2()) {
624         // All reciprocals of non-denormal powers of two can be represented
625         // exactly, so division by power of two can be reduced to
626         // multiplication by reciprocal, with the same result.
627         node->ReplaceInput(1, Float64Constant(1.0 / m.right().ResolvedValue()));
628         NodeProperties::ChangeOp(node, machine()->Float64Mul());
629         return Changed(node);
630       }
631       break;
632     }
633     case IrOpcode::kFloat64Mod: {
634       Float64BinopMatcher m(node);
635       if (m.right().Is(0)) {  // x % 0 => NaN
636         return ReplaceFloat64(std::numeric_limits<double>::quiet_NaN());
637       }
638       if (m.right().IsNaN()) {  // x % NaN => NaN
639         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
640       }
641       if (m.left().IsNaN()) {  // NaN % x => NaN
642         return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
643       }
644       if (m.IsFoldable()) {  // K % K => K  (K stands for arbitrary constants)
645         return ReplaceFloat64(
646             Modulo(m.left().ResolvedValue(), m.right().ResolvedValue()));
647       }
648       break;
649     }
650     case IrOpcode::kFloat64Acos: {
651       Float64Matcher m(node->InputAt(0));
652       if (m.HasResolvedValue())
653         return ReplaceFloat64(base::ieee754::acos(m.ResolvedValue()));
654       break;
655     }
656     case IrOpcode::kFloat64Acosh: {
657       Float64Matcher m(node->InputAt(0));
658       if (m.HasResolvedValue())
659         return ReplaceFloat64(base::ieee754::acosh(m.ResolvedValue()));
660       break;
661     }
662     case IrOpcode::kFloat64Asin: {
663       Float64Matcher m(node->InputAt(0));
664       if (m.HasResolvedValue())
665         return ReplaceFloat64(base::ieee754::asin(m.ResolvedValue()));
666       break;
667     }
668     case IrOpcode::kFloat64Asinh: {
669       Float64Matcher m(node->InputAt(0));
670       if (m.HasResolvedValue())
671         return ReplaceFloat64(base::ieee754::asinh(m.ResolvedValue()));
672       break;
673     }
674     case IrOpcode::kFloat64Atan: {
675       Float64Matcher m(node->InputAt(0));
676       if (m.HasResolvedValue())
677         return ReplaceFloat64(base::ieee754::atan(m.ResolvedValue()));
678       break;
679     }
680     case IrOpcode::kFloat64Atanh: {
681       Float64Matcher m(node->InputAt(0));
682       if (m.HasResolvedValue())
683         return ReplaceFloat64(base::ieee754::atanh(m.ResolvedValue()));
684       break;
685     }
686     case IrOpcode::kFloat64Atan2: {
687       Float64BinopMatcher m(node);
688       if (m.right().IsNaN()) {
689         return ReplaceFloat64(SilenceNaN(m.right().ResolvedValue()));
690       }
691       if (m.left().IsNaN()) {
692         return ReplaceFloat64(SilenceNaN(m.left().ResolvedValue()));
693       }
694       if (m.IsFoldable()) {
695         return ReplaceFloat64(base::ieee754::atan2(m.left().ResolvedValue(),
696                                                    m.right().ResolvedValue()));
697       }
698       break;
699     }
700     case IrOpcode::kFloat64Cbrt: {
701       Float64Matcher m(node->InputAt(0));
702       if (m.HasResolvedValue())
703         return ReplaceFloat64(base::ieee754::cbrt(m.ResolvedValue()));
704       break;
705     }
706     case IrOpcode::kFloat64Cos: {
707       Float64Matcher m(node->InputAt(0));
708       if (m.HasResolvedValue())
709         return ReplaceFloat64(base::ieee754::cos(m.ResolvedValue()));
710       break;
711     }
712     case IrOpcode::kFloat64Cosh: {
713       Float64Matcher m(node->InputAt(0));
714       if (m.HasResolvedValue())
715         return ReplaceFloat64(base::ieee754::cosh(m.ResolvedValue()));
716       break;
717     }
718     case IrOpcode::kFloat64Exp: {
719       Float64Matcher m(node->InputAt(0));
720       if (m.HasResolvedValue())
721         return ReplaceFloat64(base::ieee754::exp(m.ResolvedValue()));
722       break;
723     }
724     case IrOpcode::kFloat64Expm1: {
725       Float64Matcher m(node->InputAt(0));
726       if (m.HasResolvedValue())
727         return ReplaceFloat64(base::ieee754::expm1(m.ResolvedValue()));
728       break;
729     }
730     case IrOpcode::kFloat64Log: {
731       Float64Matcher m(node->InputAt(0));
732       if (m.HasResolvedValue())
733         return ReplaceFloat64(base::ieee754::log(m.ResolvedValue()));
734       break;
735     }
736     case IrOpcode::kFloat64Log1p: {
737       Float64Matcher m(node->InputAt(0));
738       if (m.HasResolvedValue())
739         return ReplaceFloat64(base::ieee754::log1p(m.ResolvedValue()));
740       break;
741     }
742     case IrOpcode::kFloat64Log10: {
743       Float64Matcher m(node->InputAt(0));
744       if (m.HasResolvedValue())
745         return ReplaceFloat64(base::ieee754::log10(m.ResolvedValue()));
746       break;
747     }
748     case IrOpcode::kFloat64Log2: {
749       Float64Matcher m(node->InputAt(0));
750       if (m.HasResolvedValue())
751         return ReplaceFloat64(base::ieee754::log2(m.ResolvedValue()));
752       break;
753     }
754     case IrOpcode::kFloat64Pow: {
755       Float64BinopMatcher m(node);
756       if (m.IsFoldable()) {
757         return ReplaceFloat64(base::ieee754::pow(m.left().ResolvedValue(),
758                                                  m.right().ResolvedValue()));
759       } else if (m.right().Is(0.0)) {  // x ** +-0.0 => 1.0
760         return ReplaceFloat64(1.0);
761       } else if (m.right().Is(2.0)) {  // x ** 2.0 => x * x
762         node->ReplaceInput(1, m.left().node());
763         NodeProperties::ChangeOp(node, machine()->Float64Mul());
764         return Changed(node);
765       } else if (m.right().Is(0.5)) {
766         // x ** 0.5 => if x <= -Infinity then Infinity else sqrt(0.0 + x)
767         return Replace(Float64PowHalf(m.left().node()));
768       }
769       break;
770     }
771     case IrOpcode::kFloat64Sin: {
772       Float64Matcher m(node->InputAt(0));
773       if (m.HasResolvedValue())
774         return ReplaceFloat64(base::ieee754::sin(m.ResolvedValue()));
775       break;
776     }
777     case IrOpcode::kFloat64Sinh: {
778       Float64Matcher m(node->InputAt(0));
779       if (m.HasResolvedValue())
780         return ReplaceFloat64(base::ieee754::sinh(m.ResolvedValue()));
781       break;
782     }
783     case IrOpcode::kFloat64Tan: {
784       Float64Matcher m(node->InputAt(0));
785       if (m.HasResolvedValue())
786         return ReplaceFloat64(base::ieee754::tan(m.ResolvedValue()));
787       break;
788     }
789     case IrOpcode::kFloat64Tanh: {
790       Float64Matcher m(node->InputAt(0));
791       if (m.HasResolvedValue())
792         return ReplaceFloat64(base::ieee754::tanh(m.ResolvedValue()));
793       break;
794     }
795     case IrOpcode::kChangeFloat32ToFloat64: {
796       Float32Matcher m(node->InputAt(0));
797       if (m.HasResolvedValue()) {
798         if (!allow_signalling_nan_ && std::isnan(m.ResolvedValue())) {
799           return ReplaceFloat64(SilenceNaN(m.ResolvedValue()));
800         }
801         return ReplaceFloat64(m.ResolvedValue());
802       }
803       break;
804     }
805     case IrOpcode::kChangeFloat64ToInt32: {
806       Float64Matcher m(node->InputAt(0));
807       if (m.HasResolvedValue())
808         return ReplaceInt32(FastD2IChecked(m.ResolvedValue()));
809       if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
810       break;
811     }
812     case IrOpcode::kChangeFloat64ToInt64: {
813       Float64Matcher m(node->InputAt(0));
814       if (m.HasResolvedValue())
815         return ReplaceInt64(static_cast<int64_t>(m.ResolvedValue()));
816       if (m.IsChangeInt64ToFloat64()) return Replace(m.node()->InputAt(0));
817       break;
818     }
819     case IrOpcode::kChangeFloat64ToUint32: {
820       Float64Matcher m(node->InputAt(0));
821       if (m.HasResolvedValue())
822         return ReplaceInt32(FastD2UI(m.ResolvedValue()));
823       if (m.IsChangeUint32ToFloat64()) return Replace(m.node()->InputAt(0));
824       break;
825     }
826     case IrOpcode::kChangeInt32ToFloat64: {
827       Int32Matcher m(node->InputAt(0));
828       if (m.HasResolvedValue())
829         return ReplaceFloat64(FastI2D(m.ResolvedValue()));
830       break;
831     }
832     case IrOpcode::kBitcastWord32ToWord64: {
833       Int32Matcher m(node->InputAt(0));
834       if (m.HasResolvedValue()) return ReplaceInt64(m.ResolvedValue());
835       break;
836     }
837     case IrOpcode::kChangeInt32ToInt64: {
838       Int32Matcher m(node->InputAt(0));
839       if (m.HasResolvedValue()) return ReplaceInt64(m.ResolvedValue());
840       break;
841     }
842     case IrOpcode::kChangeInt64ToFloat64: {
843       Int64Matcher m(node->InputAt(0));
844       if (m.HasResolvedValue())
845         return ReplaceFloat64(static_cast<double>(m.ResolvedValue()));
846       if (m.IsChangeFloat64ToInt64()) return Replace(m.node()->InputAt(0));
847       break;
848     }
849     case IrOpcode::kChangeUint32ToFloat64: {
850       Uint32Matcher m(node->InputAt(0));
851       if (m.HasResolvedValue())
852         return ReplaceFloat64(FastUI2D(m.ResolvedValue()));
853       break;
854     }
855     case IrOpcode::kChangeUint32ToUint64: {
856       Uint32Matcher m(node->InputAt(0));
857       if (m.HasResolvedValue())
858         return ReplaceInt64(static_cast<uint64_t>(m.ResolvedValue()));
859       break;
860     }
861     case IrOpcode::kTruncateFloat64ToWord32: {
862       Float64Matcher m(node->InputAt(0));
863       if (m.HasResolvedValue())
864         return ReplaceInt32(DoubleToInt32(m.ResolvedValue()));
865       if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
866       return NoChange();
867     }
868     case IrOpcode::kTruncateInt64ToInt32:
869       return ReduceTruncateInt64ToInt32(node);
870     case IrOpcode::kTruncateFloat64ToFloat32: {
871       Float64Matcher m(node->InputAt(0));
872       if (m.HasResolvedValue()) {
873         if (!allow_signalling_nan_ && m.IsNaN()) {
874           return ReplaceFloat32(DoubleToFloat32(SilenceNaN(m.ResolvedValue())));
875         }
876         return ReplaceFloat32(DoubleToFloat32(m.ResolvedValue()));
877       }
878       if (allow_signalling_nan_ && m.IsChangeFloat32ToFloat64())
879         return Replace(m.node()->InputAt(0));
880       break;
881     }
882     case IrOpcode::kRoundFloat64ToInt32: {
883       Float64Matcher m(node->InputAt(0));
884       if (m.HasResolvedValue()) {
885         return ReplaceInt32(DoubleToInt32(m.ResolvedValue()));
886       }
887       if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
888       break;
889     }
890     case IrOpcode::kFloat64InsertLowWord32:
891       return ReduceFloat64InsertLowWord32(node);
892     case IrOpcode::kFloat64InsertHighWord32:
893       return ReduceFloat64InsertHighWord32(node);
894     case IrOpcode::kStore:
895     case IrOpcode::kUnalignedStore:
896       return ReduceStore(node);
897     case IrOpcode::kFloat64Equal:
898     case IrOpcode::kFloat64LessThan:
899     case IrOpcode::kFloat64LessThanOrEqual:
900       return ReduceFloat64Compare(node);
901     case IrOpcode::kFloat64RoundDown:
902       return ReduceFloat64RoundDown(node);
903     case IrOpcode::kBitcastTaggedToWord:
904     case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: {
905       NodeMatcher m(node->InputAt(0));
906       if (m.IsBitcastWordToTaggedSigned()) {
907         RelaxEffectsAndControls(node);
908         return Replace(m.InputAt(0));
909       }
910       break;
911     }
912     case IrOpcode::kBranch:
913     case IrOpcode::kDeoptimizeIf:
914     case IrOpcode::kDeoptimizeUnless:
915     case IrOpcode::kTrapIf:
916     case IrOpcode::kTrapUnless:
917       return ReduceConditional(node);
918     case IrOpcode::kInt64LessThan: {
919       Int64BinopMatcher m(node);
920       if (m.IsFoldable()) {  // K < K => K  (K stands for arbitrary constants)
921         return ReplaceBool(m.left().ResolvedValue() <
922                            m.right().ResolvedValue());
923       }
924       return ReduceWord64Comparisons(node);
925     }
926     case IrOpcode::kInt64LessThanOrEqual: {
927       Int64BinopMatcher m(node);
928       if (m.IsFoldable()) {  // K <= K => K  (K stands for arbitrary constants)
929         return ReplaceBool(m.left().ResolvedValue() <=
930                            m.right().ResolvedValue());
931       }
932       return ReduceWord64Comparisons(node);
933     }
934     case IrOpcode::kUint64LessThan: {
935       Uint64BinopMatcher m(node);
936       if (m.IsFoldable()) {  // K < K => K  (K stands for arbitrary constants)
937         return ReplaceBool(m.left().ResolvedValue() <
938                            m.right().ResolvedValue());
939       }
940       return ReduceWord64Comparisons(node);
941     }
942     case IrOpcode::kUint64LessThanOrEqual: {
943       Uint64BinopMatcher m(node);
944       if (m.IsFoldable()) {  // K <= K => K  (K stands for arbitrary constants)
945         return ReplaceBool(m.left().ResolvedValue() <=
946                            m.right().ResolvedValue());
947       }
948       return ReduceWord64Comparisons(node);
949     }
950     case IrOpcode::kFloat32Select:
951     case IrOpcode::kFloat64Select:
952     case IrOpcode::kWord32Select:
953     case IrOpcode::kWord64Select: {
954       Int32Matcher match(node->InputAt(0));
955       if (match.HasResolvedValue()) {
956         if (match.Is(0)) {
957           return Replace(node->InputAt(2));
958         } else {
959           return Replace(node->InputAt(1));
960         }
961       }
962       break;
963     }
964     default:
965       break;
966   }
967   return NoChange();
968 }
969 
ReduceTruncateInt64ToInt32(Node * node)970 Reduction MachineOperatorReducer::ReduceTruncateInt64ToInt32(Node* node) {
971   Int64Matcher m(node->InputAt(0));
972   if (m.HasResolvedValue())
973     return ReplaceInt32(static_cast<int32_t>(m.ResolvedValue()));
974   if (m.IsChangeInt32ToInt64()) return Replace(m.node()->InputAt(0));
975   return NoChange();
976 }
977 
ReduceInt32Add(Node * node)978 Reduction MachineOperatorReducer::ReduceInt32Add(Node* node) {
979   DCHECK_EQ(IrOpcode::kInt32Add, node->opcode());
980   Int32BinopMatcher m(node);
981   if (m.right().Is(0)) return Replace(m.left().node());  // x + 0 => x
982   if (m.IsFoldable()) {  // K + K => K  (K stands for arbitrary constants)
983     return ReplaceInt32(base::AddWithWraparound(m.left().ResolvedValue(),
984                                                 m.right().ResolvedValue()));
985   }
986   if (m.left().IsInt32Sub()) {
987     Int32BinopMatcher mleft(m.left().node());
988     if (mleft.left().Is(0)) {  // (0 - x) + y => y - x
989       node->ReplaceInput(0, m.right().node());
990       node->ReplaceInput(1, mleft.right().node());
991       NodeProperties::ChangeOp(node, machine()->Int32Sub());
992       return Changed(node).FollowedBy(ReduceInt32Sub(node));
993     }
994   }
995   if (m.right().IsInt32Sub()) {
996     Int32BinopMatcher mright(m.right().node());
997     if (mright.left().Is(0)) {  // y + (0 - x) => y - x
998       node->ReplaceInput(1, mright.right().node());
999       NodeProperties::ChangeOp(node, machine()->Int32Sub());
1000       return Changed(node).FollowedBy(ReduceInt32Sub(node));
1001     }
1002   }
1003   // (x + Int32Constant(a)) + Int32Constant(b)) => x + Int32Constant(a + b)
1004   if (m.right().HasResolvedValue() && m.left().IsInt32Add()) {
1005     Int32BinopMatcher n(m.left().node());
1006     if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1007       node->ReplaceInput(
1008           1, Int32Constant(base::AddWithWraparound(m.right().ResolvedValue(),
1009                                                    n.right().ResolvedValue())));
1010       node->ReplaceInput(0, n.left().node());
1011       return Changed(node);
1012     }
1013   }
1014 
1015   return NoChange();
1016 }
1017 
ReduceInt64Add(Node * node)1018 Reduction MachineOperatorReducer::ReduceInt64Add(Node* node) {
1019   DCHECK_EQ(IrOpcode::kInt64Add, node->opcode());
1020   Int64BinopMatcher m(node);
1021   if (m.right().Is(0)) return Replace(m.left().node());  // x + 0 => 0
1022   if (m.IsFoldable()) {
1023     return ReplaceInt64(base::AddWithWraparound(m.left().ResolvedValue(),
1024                                                 m.right().ResolvedValue()));
1025   }
1026   // (x + Int64Constant(a)) + Int64Constant(b)) => x + Int64Constant(a + b)
1027   if (m.right().HasResolvedValue() && m.left().IsInt64Add()) {
1028     Int64BinopMatcher n(m.left().node());
1029     if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1030       node->ReplaceInput(
1031           1, Int64Constant(base::AddWithWraparound(m.right().ResolvedValue(),
1032                                                    n.right().ResolvedValue())));
1033       node->ReplaceInput(0, n.left().node());
1034       return Changed(node);
1035     }
1036   }
1037   return NoChange();
1038 }
1039 
ReduceInt32Sub(Node * node)1040 Reduction MachineOperatorReducer::ReduceInt32Sub(Node* node) {
1041   DCHECK_EQ(IrOpcode::kInt32Sub, node->opcode());
1042   Int32BinopMatcher m(node);
1043   if (m.right().Is(0)) return Replace(m.left().node());  // x - 0 => x
1044   if (m.IsFoldable()) {  // K - K => K  (K stands for arbitrary constants)
1045     return ReplaceInt32(base::SubWithWraparound(m.left().ResolvedValue(),
1046                                                 m.right().ResolvedValue()));
1047   }
1048   if (m.LeftEqualsRight()) return ReplaceInt32(0);  // x - x => 0
1049   if (m.right().HasResolvedValue()) {               // x - K => x + -K
1050     node->ReplaceInput(
1051         1,
1052         Int32Constant(base::NegateWithWraparound(m.right().ResolvedValue())));
1053     NodeProperties::ChangeOp(node, machine()->Int32Add());
1054     return Changed(node).FollowedBy(ReduceInt32Add(node));
1055   }
1056   return NoChange();
1057 }
1058 
ReduceInt64Sub(Node * node)1059 Reduction MachineOperatorReducer::ReduceInt64Sub(Node* node) {
1060   DCHECK_EQ(IrOpcode::kInt64Sub, node->opcode());
1061   Int64BinopMatcher m(node);
1062   if (m.right().Is(0)) return Replace(m.left().node());  // x - 0 => x
1063   if (m.IsFoldable()) {  // K - K => K  (K stands for arbitrary constants)
1064     return ReplaceInt64(base::SubWithWraparound(m.left().ResolvedValue(),
1065                                                 m.right().ResolvedValue()));
1066   }
1067   if (m.LeftEqualsRight()) return Replace(Int64Constant(0));  // x - x => 0
1068   if (m.right().HasResolvedValue()) {                         // x - K => x + -K
1069     node->ReplaceInput(
1070         1,
1071         Int64Constant(base::NegateWithWraparound(m.right().ResolvedValue())));
1072     NodeProperties::ChangeOp(node, machine()->Int64Add());
1073     return Changed(node).FollowedBy(ReduceInt64Add(node));
1074   }
1075   return NoChange();
1076 }
1077 
ReduceInt64Mul(Node * node)1078 Reduction MachineOperatorReducer::ReduceInt64Mul(Node* node) {
1079   DCHECK_EQ(IrOpcode::kInt64Mul, node->opcode());
1080   Int64BinopMatcher m(node);
1081   if (m.right().Is(0)) return Replace(m.right().node());  // x * 0 => 0
1082   if (m.right().Is(1)) return Replace(m.left().node());   // x * 1 => x
1083   if (m.IsFoldable()) {  // K * K => K  (K stands for arbitrary constants)
1084     return ReplaceInt64(base::MulWithWraparound(m.left().ResolvedValue(),
1085                                                 m.right().ResolvedValue()));
1086   }
1087   if (m.right().Is(-1)) {  // x * -1 => 0 - x
1088     node->ReplaceInput(0, Int64Constant(0));
1089     node->ReplaceInput(1, m.left().node());
1090     NodeProperties::ChangeOp(node, machine()->Int64Sub());
1091     return Changed(node);
1092   }
1093   if (m.right().IsPowerOf2()) {  // x * 2^n => x << n
1094     node->ReplaceInput(
1095         1,
1096         Int64Constant(base::bits::WhichPowerOfTwo(m.right().ResolvedValue())));
1097     NodeProperties::ChangeOp(node, machine()->Word64Shl());
1098     return Changed(node).FollowedBy(ReduceWord64Shl(node));
1099   }
1100   // (x * Int64Constant(a)) * Int64Constant(b)) => x * Int64Constant(a * b)
1101   if (m.right().HasResolvedValue() && m.left().IsInt64Mul()) {
1102     Int64BinopMatcher n(m.left().node());
1103     if (n.right().HasResolvedValue() && m.OwnsInput(m.left().node())) {
1104       node->ReplaceInput(
1105           1, Int64Constant(base::MulWithWraparound(m.right().ResolvedValue(),
1106                                                    n.right().ResolvedValue())));
1107       node->ReplaceInput(0, n.left().node());
1108       return Changed(node);
1109     }
1110   }
1111   return NoChange();
1112 }
1113 
ReduceInt32Div(Node * node)1114 Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
1115   Int32BinopMatcher m(node);
1116   if (m.left().Is(0)) return Replace(m.left().node());    // 0 / x => 0
1117   if (m.right().Is(0)) return Replace(m.right().node());  // x / 0 => 0
1118   if (m.right().Is(1)) return Replace(m.left().node());   // x / 1 => x
1119   if (m.IsFoldable()) {  // K / K => K  (K stands for arbitrary constants)
1120     return ReplaceInt32(base::bits::SignedDiv32(m.left().ResolvedValue(),
1121                                                 m.right().ResolvedValue()));
1122   }
1123   if (m.LeftEqualsRight()) {  // x / x => x != 0
1124     Node* const zero = Int32Constant(0);
1125     return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1126   }
1127   if (m.right().Is(-1)) {  // x / -1 => 0 - x
1128     node->ReplaceInput(0, Int32Constant(0));
1129     node->ReplaceInput(1, m.left().node());
1130     node->TrimInputCount(2);
1131     NodeProperties::ChangeOp(node, machine()->Int32Sub());
1132     return Changed(node);
1133   }
1134   if (m.right().HasResolvedValue()) {
1135     int32_t const divisor = m.right().ResolvedValue();
1136     Node* const dividend = m.left().node();
1137     Node* quotient = dividend;
1138     if (base::bits::IsPowerOfTwo(Abs(divisor))) {
1139       uint32_t const shift = base::bits::WhichPowerOfTwo(Abs(divisor));
1140       DCHECK_NE(0u, shift);
1141       if (shift > 1) {
1142         quotient = Word32Sar(quotient, 31);
1143       }
1144       quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend);
1145       quotient = Word32Sar(quotient, shift);
1146     } else {
1147       quotient = Int32Div(quotient, Abs(divisor));
1148     }
1149     if (divisor < 0) {
1150       node->ReplaceInput(0, Int32Constant(0));
1151       node->ReplaceInput(1, quotient);
1152       node->TrimInputCount(2);
1153       NodeProperties::ChangeOp(node, machine()->Int32Sub());
1154       return Changed(node);
1155     }
1156     return Replace(quotient);
1157   }
1158   return NoChange();
1159 }
1160 
ReduceUint32Div(Node * node)1161 Reduction MachineOperatorReducer::ReduceUint32Div(Node* node) {
1162   Uint32BinopMatcher m(node);
1163   if (m.left().Is(0)) return Replace(m.left().node());    // 0 / x => 0
1164   if (m.right().Is(0)) return Replace(m.right().node());  // x / 0 => 0
1165   if (m.right().Is(1)) return Replace(m.left().node());   // x / 1 => x
1166   if (m.IsFoldable()) {  // K / K => K  (K stands for arbitrary constants)
1167     return ReplaceUint32(base::bits::UnsignedDiv32(m.left().ResolvedValue(),
1168                                                    m.right().ResolvedValue()));
1169   }
1170   if (m.LeftEqualsRight()) {  // x / x => x != 0
1171     Node* const zero = Int32Constant(0);
1172     return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1173   }
1174   if (m.right().HasResolvedValue()) {
1175     Node* const dividend = m.left().node();
1176     uint32_t const divisor = m.right().ResolvedValue();
1177     if (base::bits::IsPowerOfTwo(divisor)) {  // x / 2^n => x >> n
1178       node->ReplaceInput(1, Uint32Constant(base::bits::WhichPowerOfTwo(
1179                                 m.right().ResolvedValue())));
1180       node->TrimInputCount(2);
1181       NodeProperties::ChangeOp(node, machine()->Word32Shr());
1182       return Changed(node);
1183     } else {
1184       return Replace(Uint32Div(dividend, divisor));
1185     }
1186   }
1187   return NoChange();
1188 }
1189 
ReduceInt32Mod(Node * node)1190 Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) {
1191   Int32BinopMatcher m(node);
1192   if (m.left().Is(0)) return Replace(m.left().node());    // 0 % x  => 0
1193   if (m.right().Is(0)) return Replace(m.right().node());  // x % 0  => 0
1194   if (m.right().Is(1)) return ReplaceInt32(0);            // x % 1  => 0
1195   if (m.right().Is(-1)) return ReplaceInt32(0);           // x % -1 => 0
1196   if (m.LeftEqualsRight()) return ReplaceInt32(0);        // x % x  => 0
1197   if (m.IsFoldable()) {  // K % K => K  (K stands for arbitrary constants)
1198     return ReplaceInt32(base::bits::SignedMod32(m.left().ResolvedValue(),
1199                                                 m.right().ResolvedValue()));
1200   }
1201   if (m.right().HasResolvedValue()) {
1202     Node* const dividend = m.left().node();
1203     uint32_t const divisor = Abs(m.right().ResolvedValue());
1204     if (base::bits::IsPowerOfTwo(divisor)) {
1205       uint32_t const mask = divisor - 1;
1206       Node* const zero = Int32Constant(0);
1207       Diamond d(graph(), common(),
1208                 graph()->NewNode(machine()->Int32LessThan(), dividend, zero),
1209                 BranchHint::kFalse);
1210       return Replace(
1211           d.Phi(MachineRepresentation::kWord32,
1212                 Int32Sub(zero, Word32And(Int32Sub(zero, dividend), mask)),
1213                 Word32And(dividend, mask)));
1214     } else {
1215       Node* quotient = Int32Div(dividend, divisor);
1216       DCHECK_EQ(dividend, node->InputAt(0));
1217       node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor)));
1218       node->TrimInputCount(2);
1219       NodeProperties::ChangeOp(node, machine()->Int32Sub());
1220     }
1221     return Changed(node);
1222   }
1223   return NoChange();
1224 }
1225 
ReduceUint32Mod(Node * node)1226 Reduction MachineOperatorReducer::ReduceUint32Mod(Node* node) {
1227   Uint32BinopMatcher m(node);
1228   if (m.left().Is(0)) return Replace(m.left().node());    // 0 % x => 0
1229   if (m.right().Is(0)) return Replace(m.right().node());  // x % 0 => 0
1230   if (m.right().Is(1)) return ReplaceUint32(0);           // x % 1 => 0
1231   if (m.LeftEqualsRight()) return ReplaceInt32(0);        // x % x  => 0
1232   if (m.IsFoldable()) {  // K % K => K  (K stands for arbitrary constants)
1233     return ReplaceUint32(base::bits::UnsignedMod32(m.left().ResolvedValue(),
1234                                                    m.right().ResolvedValue()));
1235   }
1236   if (m.right().HasResolvedValue()) {
1237     Node* const dividend = m.left().node();
1238     uint32_t const divisor = m.right().ResolvedValue();
1239     if (base::bits::IsPowerOfTwo(divisor)) {  // x % 2^n => x & 2^n-1
1240       node->ReplaceInput(1, Uint32Constant(m.right().ResolvedValue() - 1));
1241       node->TrimInputCount(2);
1242       NodeProperties::ChangeOp(node, machine()->Word32And());
1243     } else {
1244       Node* quotient = Uint32Div(dividend, divisor);
1245       DCHECK_EQ(dividend, node->InputAt(0));
1246       node->ReplaceInput(1, Int32Mul(quotient, Uint32Constant(divisor)));
1247       node->TrimInputCount(2);
1248       NodeProperties::ChangeOp(node, machine()->Int32Sub());
1249     }
1250     return Changed(node);
1251   }
1252   return NoChange();
1253 }
1254 
ReduceStore(Node * node)1255 Reduction MachineOperatorReducer::ReduceStore(Node* node) {
1256   NodeMatcher nm(node);
1257   DCHECK(nm.IsStore() || nm.IsUnalignedStore());
1258   MachineRepresentation rep =
1259       nm.IsStore() ? StoreRepresentationOf(node->op()).representation()
1260                    : UnalignedStoreRepresentationOf(node->op());
1261 
1262   const int value_input = 2;
1263   Node* const value = node->InputAt(value_input);
1264 
1265   switch (value->opcode()) {
1266     case IrOpcode::kWord32And: {
1267       Uint32BinopMatcher m(value);
1268       if (m.right().HasResolvedValue() &&
1269           ((rep == MachineRepresentation::kWord8 &&
1270             (m.right().ResolvedValue() & 0xFF) == 0xFF) ||
1271            (rep == MachineRepresentation::kWord16 &&
1272             (m.right().ResolvedValue() & 0xFFFF) == 0xFFFF))) {
1273         node->ReplaceInput(value_input, m.left().node());
1274         return Changed(node);
1275       }
1276       break;
1277     }
1278     case IrOpcode::kWord32Sar: {
1279       Int32BinopMatcher m(value);
1280       if (m.left().IsWord32Shl() && ((rep == MachineRepresentation::kWord8 &&
1281                                       m.right().IsInRange(1, 24)) ||
1282                                      (rep == MachineRepresentation::kWord16 &&
1283                                       m.right().IsInRange(1, 16)))) {
1284         Int32BinopMatcher mleft(m.left().node());
1285         if (mleft.right().Is(m.right().ResolvedValue())) {
1286           node->ReplaceInput(value_input, mleft.left().node());
1287           return Changed(node);
1288         }
1289       }
1290       break;
1291     }
1292     default:
1293       break;
1294   }
1295   return NoChange();
1296 }
1297 
ReduceProjection(size_t index,Node * node)1298 Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) {
1299   switch (node->opcode()) {
1300     case IrOpcode::kInt32AddWithOverflow: {
1301       DCHECK(index == 0 || index == 1);
1302       Int32BinopMatcher m(node);
1303       if (m.IsFoldable()) {
1304         int32_t val;
1305         bool ovf = base::bits::SignedAddOverflow32(
1306             m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1307         return ReplaceInt32(index == 0 ? val : ovf);
1308       }
1309       if (m.right().Is(0)) {
1310         return Replace(index == 0 ? m.left().node() : m.right().node());
1311       }
1312       break;
1313     }
1314     case IrOpcode::kInt32SubWithOverflow: {
1315       DCHECK(index == 0 || index == 1);
1316       Int32BinopMatcher m(node);
1317       if (m.IsFoldable()) {
1318         int32_t val;
1319         bool ovf = base::bits::SignedSubOverflow32(
1320             m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1321         return ReplaceInt32(index == 0 ? val : ovf);
1322       }
1323       if (m.right().Is(0)) {
1324         return Replace(index == 0 ? m.left().node() : m.right().node());
1325       }
1326       break;
1327     }
1328     case IrOpcode::kInt32MulWithOverflow: {
1329       DCHECK(index == 0 || index == 1);
1330       Int32BinopMatcher m(node);
1331       if (m.IsFoldable()) {
1332         int32_t val;
1333         bool ovf = base::bits::SignedMulOverflow32(
1334             m.left().ResolvedValue(), m.right().ResolvedValue(), &val);
1335         return ReplaceInt32(index == 0 ? val : ovf);
1336       }
1337       if (m.right().Is(0)) {
1338         return Replace(m.right().node());
1339       }
1340       if (m.right().Is(1)) {
1341         return index == 0 ? Replace(m.left().node()) : ReplaceInt32(0);
1342       }
1343       break;
1344     }
1345     default:
1346       break;
1347   }
1348   return NoChange();
1349 }
1350 
ReduceWord32Comparisons(Node * node)1351 Reduction MachineOperatorReducer::ReduceWord32Comparisons(Node* node) {
1352   DCHECK(node->opcode() == IrOpcode::kInt32LessThan ||
1353          node->opcode() == IrOpcode::kInt32LessThanOrEqual ||
1354          node->opcode() == IrOpcode::kUint32LessThan ||
1355          node->opcode() == IrOpcode::kUint32LessThanOrEqual);
1356   Int32BinopMatcher m(node);
1357   // (x >>> K) < (y >>> K) => x < y   if only zeros shifted out
1358   if (m.left().op() == machine()->Word32SarShiftOutZeros() &&
1359       m.right().op() == machine()->Word32SarShiftOutZeros()) {
1360     Int32BinopMatcher mleft(m.left().node());
1361     Int32BinopMatcher mright(m.right().node());
1362     if (mleft.right().HasResolvedValue() &&
1363         mright.right().Is(mleft.right().ResolvedValue())) {
1364       node->ReplaceInput(0, mleft.left().node());
1365       node->ReplaceInput(1, mright.left().node());
1366       return Changed(node);
1367     }
1368   }
1369   return NoChange();
1370 }
1371 
Map64To32Comparison(const Operator * op,bool sign_extended)1372 const Operator* MachineOperatorReducer::Map64To32Comparison(
1373     const Operator* op, bool sign_extended) {
1374   switch (op->opcode()) {
1375     case IrOpcode::kInt64LessThan:
1376       return sign_extended ? machine()->Int32LessThan()
1377                            : machine()->Uint32LessThan();
1378     case IrOpcode::kInt64LessThanOrEqual:
1379       return sign_extended ? machine()->Int32LessThanOrEqual()
1380                            : machine()->Uint32LessThanOrEqual();
1381     case IrOpcode::kUint64LessThan:
1382       return machine()->Uint32LessThan();
1383     case IrOpcode::kUint64LessThanOrEqual:
1384       return machine()->Uint32LessThanOrEqual();
1385     default:
1386       UNREACHABLE();
1387   }
1388 }
1389 
ReduceWord64Comparisons(Node * node)1390 Reduction MachineOperatorReducer::ReduceWord64Comparisons(Node* node) {
1391   DCHECK(node->opcode() == IrOpcode::kInt64LessThan ||
1392          node->opcode() == IrOpcode::kInt64LessThanOrEqual ||
1393          node->opcode() == IrOpcode::kUint64LessThan ||
1394          node->opcode() == IrOpcode::kUint64LessThanOrEqual);
1395   Int64BinopMatcher m(node);
1396 
1397   bool sign_extended =
1398       m.left().IsChangeInt32ToInt64() && m.right().IsChangeInt32ToInt64();
1399   if (sign_extended || (m.left().IsChangeUint32ToUint64() &&
1400                         m.right().IsChangeUint32ToUint64())) {
1401     node->ReplaceInput(0, NodeProperties::GetValueInput(m.left().node(), 0));
1402     node->ReplaceInput(1, NodeProperties::GetValueInput(m.right().node(), 0));
1403     NodeProperties::ChangeOp(node,
1404                              Map64To32Comparison(node->op(), sign_extended));
1405     return Changed(node).FollowedBy(Reduce(node));
1406   }
1407 
1408   // (x >>> K) < (y >>> K) => x < y   if only zeros shifted out
1409   // This is useful for Smi untagging, which results in such a shift.
1410   if (m.left().op() == machine()->Word64SarShiftOutZeros() &&
1411       m.right().op() == machine()->Word64SarShiftOutZeros()) {
1412     Int64BinopMatcher mleft(m.left().node());
1413     Int64BinopMatcher mright(m.right().node());
1414     if (mleft.right().HasResolvedValue() &&
1415         mright.right().Is(mleft.right().ResolvedValue())) {
1416       node->ReplaceInput(0, mleft.left().node());
1417       node->ReplaceInput(1, mright.left().node());
1418       return Changed(node);
1419     }
1420   }
1421 
1422   return NoChange();
1423 }
1424 
ReduceWord32Shifts(Node * node)1425 Reduction MachineOperatorReducer::ReduceWord32Shifts(Node* node) {
1426   DCHECK((node->opcode() == IrOpcode::kWord32Shl) ||
1427          (node->opcode() == IrOpcode::kWord32Shr) ||
1428          (node->opcode() == IrOpcode::kWord32Sar));
1429   if (machine()->Word32ShiftIsSafe()) {
1430     // Remove the explicit 'and' with 0x1F if the shift provided by the machine
1431     // instruction matches that required by JavaScript.
1432     Int32BinopMatcher m(node);
1433     if (m.right().IsWord32And()) {
1434       Int32BinopMatcher mright(m.right().node());
1435       if (mright.right().Is(0x1F)) {
1436         node->ReplaceInput(1, mright.left().node());
1437         return Changed(node);
1438       }
1439     }
1440   }
1441   return NoChange();
1442 }
1443 
ReduceWord32Shl(Node * node)1444 Reduction MachineOperatorReducer::ReduceWord32Shl(Node* node) {
1445   DCHECK_EQ(IrOpcode::kWord32Shl, node->opcode());
1446   Int32BinopMatcher m(node);
1447   if (m.right().Is(0)) return Replace(m.left().node());  // x << 0 => x
1448   if (m.IsFoldable()) {  // K << K => K  (K stands for arbitrary constants)
1449     return ReplaceInt32(base::ShlWithWraparound(m.left().ResolvedValue(),
1450                                                 m.right().ResolvedValue()));
1451   }
1452   if (m.right().IsInRange(1, 31)) {
1453     if (m.left().IsWord32Sar() || m.left().IsWord32Shr()) {
1454       Int32BinopMatcher mleft(m.left().node());
1455 
1456       // If x >> K only shifted out zeros:
1457       // (x >> K) << L => x           if K == L
1458       // (x >> K) << L => x >> (K-L) if K > L
1459       // (x >> K) << L => x << (L-K)  if K < L
1460       // Since this is used for Smi untagging, we currently only need it for
1461       // signed shifts.
1462       if (mleft.op() == machine()->Word32SarShiftOutZeros() &&
1463           mleft.right().IsInRange(1, 31)) {
1464         Node* x = mleft.left().node();
1465         int k = mleft.right().ResolvedValue();
1466         int l = m.right().ResolvedValue();
1467         if (k == l) {
1468           return Replace(x);
1469         } else if (k > l) {
1470           node->ReplaceInput(0, x);
1471           node->ReplaceInput(1, Uint32Constant(k - l));
1472           NodeProperties::ChangeOp(node, machine()->Word32Sar());
1473           return Changed(node).FollowedBy(ReduceWord32Sar(node));
1474         } else {
1475           DCHECK(k < l);
1476           node->ReplaceInput(0, x);
1477           node->ReplaceInput(1, Uint32Constant(l - k));
1478           return Changed(node);
1479         }
1480       }
1481 
1482       // (x >>> K) << K => x & ~(2^K - 1)
1483       // (x >> K) << K => x & ~(2^K - 1)
1484       if (mleft.right().Is(m.right().ResolvedValue())) {
1485         node->ReplaceInput(0, mleft.left().node());
1486         node->ReplaceInput(1,
1487                            Uint32Constant(std::numeric_limits<uint32_t>::max()
1488                                           << m.right().ResolvedValue()));
1489         NodeProperties::ChangeOp(node, machine()->Word32And());
1490         return Changed(node).FollowedBy(ReduceWord32And(node));
1491       }
1492     }
1493   }
1494   return ReduceWord32Shifts(node);
1495 }
1496 
ReduceWord64Shl(Node * node)1497 Reduction MachineOperatorReducer::ReduceWord64Shl(Node* node) {
1498   DCHECK_EQ(IrOpcode::kWord64Shl, node->opcode());
1499   Int64BinopMatcher m(node);
1500   if (m.right().Is(0)) return Replace(m.left().node());  // x << 0 => x
1501   if (m.IsFoldable()) {  // K << K => K  (K stands for arbitrary constants)
1502     return ReplaceInt64(base::ShlWithWraparound(m.left().ResolvedValue(),
1503                                                 m.right().ResolvedValue()));
1504   }
1505   if (m.right().IsInRange(1, 63) &&
1506       (m.left().IsWord64Sar() || m.left().IsWord64Shr())) {
1507     Int64BinopMatcher mleft(m.left().node());
1508 
1509     // If x >> K only shifted out zeros:
1510     // (x >> K) << L => x           if K == L
1511     // (x >> K) << L => x >> (K-L) if K > L
1512     // (x >> K) << L => x << (L-K)  if K < L
1513     // Since this is used for Smi untagging, we currently only need it for
1514     // signed shifts.
1515     if (mleft.op() == machine()->Word64SarShiftOutZeros() &&
1516         mleft.right().IsInRange(1, 63)) {
1517       Node* x = mleft.left().node();
1518       int64_t k = mleft.right().ResolvedValue();
1519       int64_t l = m.right().ResolvedValue();
1520       if (k == l) {
1521         return Replace(x);
1522       } else if (k > l) {
1523         node->ReplaceInput(0, x);
1524         node->ReplaceInput(1, Uint64Constant(k - l));
1525         NodeProperties::ChangeOp(node, machine()->Word64Sar());
1526         return Changed(node).FollowedBy(ReduceWord64Sar(node));
1527       } else {
1528         DCHECK(k < l);
1529         node->ReplaceInput(0, x);
1530         node->ReplaceInput(1, Uint64Constant(l - k));
1531         return Changed(node);
1532       }
1533     }
1534 
1535     // (x >>> K) << K => x & ~(2^K - 1)
1536     // (x >> K) << K => x & ~(2^K - 1)
1537     if (mleft.right().Is(m.right().ResolvedValue())) {
1538       node->ReplaceInput(0, mleft.left().node());
1539       node->ReplaceInput(1, Uint64Constant(std::numeric_limits<uint64_t>::max()
1540                                            << m.right().ResolvedValue()));
1541       NodeProperties::ChangeOp(node, machine()->Word64And());
1542       return Changed(node).FollowedBy(ReduceWord64And(node));
1543     }
1544   }
1545   return NoChange();
1546 }
1547 
ReduceWord32Shr(Node * node)1548 Reduction MachineOperatorReducer::ReduceWord32Shr(Node* node) {
1549   Uint32BinopMatcher m(node);
1550   if (m.right().Is(0)) return Replace(m.left().node());  // x >>> 0 => x
1551   if (m.IsFoldable()) {  // K >>> K => K  (K stands for arbitrary constants)
1552     return ReplaceInt32(m.left().ResolvedValue() >>
1553                         (m.right().ResolvedValue() & 31));
1554   }
1555   if (m.left().IsWord32And() && m.right().HasResolvedValue()) {
1556     Uint32BinopMatcher mleft(m.left().node());
1557     if (mleft.right().HasResolvedValue()) {
1558       uint32_t shift = m.right().ResolvedValue() & 31;
1559       uint32_t mask = mleft.right().ResolvedValue();
1560       if ((mask >> shift) == 0) {
1561         // (m >>> s) == 0 implies ((x & m) >>> s) == 0
1562         return ReplaceInt32(0);
1563       }
1564     }
1565   }
1566   return ReduceWord32Shifts(node);
1567 }
1568 
ReduceWord64Shr(Node * node)1569 Reduction MachineOperatorReducer::ReduceWord64Shr(Node* node) {
1570   DCHECK_EQ(IrOpcode::kWord64Shr, node->opcode());
1571   Uint64BinopMatcher m(node);
1572   if (m.right().Is(0)) return Replace(m.left().node());  // x >>> 0 => x
1573   if (m.IsFoldable()) {  // K >> K => K  (K stands for arbitrary constants)
1574     return ReplaceInt64(m.left().ResolvedValue() >>
1575                         (m.right().ResolvedValue() & 63));
1576   }
1577   return NoChange();
1578 }
1579 
ReduceWord32Sar(Node * node)1580 Reduction MachineOperatorReducer::ReduceWord32Sar(Node* node) {
1581   Int32BinopMatcher m(node);
1582   if (m.right().Is(0)) return Replace(m.left().node());  // x >> 0 => x
1583   if (m.IsFoldable()) {  // K >> K => K  (K stands for arbitrary constants)
1584     return ReplaceInt32(m.left().ResolvedValue() >>
1585                         (m.right().ResolvedValue() & 31));
1586   }
1587   if (m.left().IsWord32Shl()) {
1588     Int32BinopMatcher mleft(m.left().node());
1589     if (mleft.left().IsComparison()) {
1590       if (m.right().Is(31) && mleft.right().Is(31)) {
1591         // Comparison << 31 >> 31 => 0 - Comparison
1592         node->ReplaceInput(0, Int32Constant(0));
1593         node->ReplaceInput(1, mleft.left().node());
1594         NodeProperties::ChangeOp(node, machine()->Int32Sub());
1595         return Changed(node).FollowedBy(ReduceInt32Sub(node));
1596       }
1597     } else if (mleft.left().IsLoad()) {
1598       LoadRepresentation const rep =
1599           LoadRepresentationOf(mleft.left().node()->op());
1600       if (m.right().Is(24) && mleft.right().Is(24) &&
1601           rep == MachineType::Int8()) {
1602         // Load[kMachInt8] << 24 >> 24 => Load[kMachInt8]
1603         return Replace(mleft.left().node());
1604       }
1605       if (m.right().Is(16) && mleft.right().Is(16) &&
1606           rep == MachineType::Int16()) {
1607         // Load[kMachInt16] << 16 >> 16 => Load[kMachInt8]
1608         return Replace(mleft.left().node());
1609       }
1610     }
1611   }
1612   return ReduceWord32Shifts(node);
1613 }
1614 
ReduceWord64Sar(Node * node)1615 Reduction MachineOperatorReducer::ReduceWord64Sar(Node* node) {
1616   Int64BinopMatcher m(node);
1617   if (m.right().Is(0)) return Replace(m.left().node());  // x >> 0 => x
1618   if (m.IsFoldable()) {
1619     return ReplaceInt64(m.left().ResolvedValue() >>
1620                         (m.right().ResolvedValue() & 63));
1621   }
1622   return NoChange();
1623 }
1624 
1625 template <typename WordNAdapter>
ReduceWordNAnd(Node * node)1626 Reduction MachineOperatorReducer::ReduceWordNAnd(Node* node) {
1627   using A = WordNAdapter;
1628   A a(this);
1629 
1630   typename A::IntNBinopMatcher m(node);
1631   if (m.right().Is(0)) return Replace(m.right().node());  // x & 0  => 0
1632   if (m.right().Is(-1)) return Replace(m.left().node());  // x & -1 => x
1633   if (m.left().IsComparison() && m.right().Is(1)) {       // CMP & 1 => CMP
1634     return Replace(m.left().node());
1635   }
1636   if (m.IsFoldable()) {  // K & K  => K  (K stands for arbitrary constants)
1637     return a.ReplaceIntN(m.left().ResolvedValue() & m.right().ResolvedValue());
1638   }
1639   if (m.LeftEqualsRight()) return Replace(m.left().node());  // x & x => x
1640   if (A::IsWordNAnd(m.left()) && m.right().HasResolvedValue()) {
1641     typename A::IntNBinopMatcher mleft(m.left().node());
1642     if (mleft.right().HasResolvedValue()) {  // (x & K) & K => x & K
1643       node->ReplaceInput(0, mleft.left().node());
1644       node->ReplaceInput(1, a.IntNConstant(m.right().ResolvedValue() &
1645                                            mleft.right().ResolvedValue()));
1646       return Changed(node).FollowedBy(a.ReduceWordNAnd(node));
1647     }
1648   }
1649   if (m.right().IsNegativePowerOf2()) {
1650     typename A::intN_t const mask = m.right().ResolvedValue();
1651     typename A::intN_t const neg_mask = base::NegateWithWraparound(mask);
1652     if (A::IsWordNShl(m.left())) {
1653       typename A::UintNBinopMatcher mleft(m.left().node());
1654       if (mleft.right().HasResolvedValue() &&
1655           (mleft.right().ResolvedValue() & (A::WORD_SIZE - 1)) >=
1656               base::bits::CountTrailingZeros(mask)) {
1657         // (x << L) & (-1 << K) => x << L iff L >= K
1658         return Replace(mleft.node());
1659       }
1660     } else if (A::IsIntNAdd(m.left())) {
1661       typename A::IntNBinopMatcher mleft(m.left().node());
1662       if (mleft.right().HasResolvedValue() &&
1663           (mleft.right().ResolvedValue() & mask) ==
1664               mleft.right().ResolvedValue()) {
1665         // (x + (K << L)) & (-1 << L) => (x & (-1 << L)) + (K << L)
1666         node->ReplaceInput(0,
1667                            a.WordNAnd(mleft.left().node(), m.right().node()));
1668         node->ReplaceInput(1, mleft.right().node());
1669         NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1670         return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
1671       }
1672       if (A::IsIntNMul(mleft.left())) {
1673         typename A::IntNBinopMatcher mleftleft(mleft.left().node());
1674         if (mleftleft.right().IsMultipleOf(neg_mask)) {
1675           // (y * (K << L) + x) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
1676           node->ReplaceInput(
1677               0, a.WordNAnd(mleft.right().node(), m.right().node()));
1678           node->ReplaceInput(1, mleftleft.node());
1679           NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1680           return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
1681         }
1682       }
1683       if (A::IsIntNMul(mleft.right())) {
1684         typename A::IntNBinopMatcher mleftright(mleft.right().node());
1685         if (mleftright.right().IsMultipleOf(neg_mask)) {
1686           // (x + y * (K << L)) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
1687           node->ReplaceInput(0,
1688                              a.WordNAnd(mleft.left().node(), m.right().node()));
1689           node->ReplaceInput(1, mleftright.node());
1690           NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1691           return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
1692         }
1693       }
1694       if (A::IsWordNShl(mleft.left())) {
1695         typename A::IntNBinopMatcher mleftleft(mleft.left().node());
1696         if (mleftleft.right().Is(base::bits::CountTrailingZeros(mask))) {
1697           // (y << L + x) & (-1 << L) => (x & (-1 << L)) + y << L
1698           node->ReplaceInput(
1699               0, a.WordNAnd(mleft.right().node(), m.right().node()));
1700           node->ReplaceInput(1, mleftleft.node());
1701           NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1702           return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
1703         }
1704       }
1705       if (A::IsWordNShl(mleft.right())) {
1706         typename A::IntNBinopMatcher mleftright(mleft.right().node());
1707         if (mleftright.right().Is(base::bits::CountTrailingZeros(mask))) {
1708           // (x + y << L) & (-1 << L) => (x & (-1 << L)) + y << L
1709           node->ReplaceInput(0,
1710                              a.WordNAnd(mleft.left().node(), m.right().node()));
1711           node->ReplaceInput(1, mleftright.node());
1712           NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1713           return Changed(node).FollowedBy(a.ReduceIntNAdd(node));
1714         }
1715       }
1716     } else if (A::IsIntNMul(m.left())) {
1717       typename A::IntNBinopMatcher mleft(m.left().node());
1718       if (mleft.right().IsMultipleOf(neg_mask)) {
1719         // (x * (K << L)) & (-1 << L) => x * (K << L)
1720         return Replace(mleft.node());
1721       }
1722     }
1723   }
1724   return NoChange();
1725 }
1726 
1727 namespace {
1728 
1729 // Represents an operation of the form `(source & mask) == masked_value`.
1730 // where each bit set in masked_value also has to be set in mask.
1731 struct BitfieldCheck {
1732   Node* const source;
1733   uint32_t const mask;
1734   uint32_t const masked_value;
1735   bool const truncate_from_64_bit;
1736 
BitfieldCheckv8::internal::compiler::__anon9327b7910311::BitfieldCheck1737   BitfieldCheck(Node* source, uint32_t mask, uint32_t masked_value,
1738                 bool truncate_from_64_bit)
1739       : source(source),
1740         mask(mask),
1741         masked_value(masked_value),
1742         truncate_from_64_bit(truncate_from_64_bit) {
1743     CHECK_EQ(masked_value & ~mask, 0);
1744   }
1745 
Detectv8::internal::compiler::__anon9327b7910311::BitfieldCheck1746   static base::Optional<BitfieldCheck> Detect(Node* node) {
1747     // There are two patterns to check for here:
1748     // 1. Single-bit checks: `(val >> shift) & 1`, where:
1749     //    - the shift may be omitted, and/or
1750     //    - the result may be truncated from 64 to 32
1751     // 2. Equality checks: `(val & mask) == expected`, where:
1752     //    - val may be truncated from 64 to 32 before masking (see
1753     //      ReduceWord32EqualForConstantRhs)
1754     if (node->opcode() == IrOpcode::kWord32Equal) {
1755       Uint32BinopMatcher eq(node);
1756       if (eq.left().IsWord32And()) {
1757         Uint32BinopMatcher mand(eq.left().node());
1758         if (mand.right().HasResolvedValue() && eq.right().HasResolvedValue()) {
1759           uint32_t mask = mand.right().ResolvedValue();
1760           uint32_t masked_value = eq.right().ResolvedValue();
1761           if ((masked_value & ~mask) != 0) return {};
1762           if (mand.left().IsTruncateInt64ToInt32()) {
1763             return BitfieldCheck(
1764                 NodeProperties::GetValueInput(mand.left().node(), 0), mask,
1765                 masked_value, true);
1766           } else {
1767             return BitfieldCheck(mand.left().node(), mask, masked_value, false);
1768           }
1769         }
1770       }
1771     } else {
1772       if (node->opcode() == IrOpcode::kTruncateInt64ToInt32) {
1773         return TryDetectShiftAndMaskOneBit<Word64Adapter>(
1774             NodeProperties::GetValueInput(node, 0));
1775       } else {
1776         return TryDetectShiftAndMaskOneBit<Word32Adapter>(node);
1777       }
1778     }
1779     return {};
1780   }
1781 
TryCombinev8::internal::compiler::__anon9327b7910311::BitfieldCheck1782   base::Optional<BitfieldCheck> TryCombine(const BitfieldCheck& other) {
1783     if (source != other.source ||
1784         truncate_from_64_bit != other.truncate_from_64_bit)
1785       return {};
1786     uint32_t overlapping_bits = mask & other.mask;
1787     // It would be kind of strange to have any overlapping bits, but they can be
1788     // allowed as long as they don't require opposite values in the same
1789     // positions.
1790     if ((masked_value & overlapping_bits) !=
1791         (other.masked_value & overlapping_bits))
1792       return {};
1793     return BitfieldCheck{source, mask | other.mask,
1794                          masked_value | other.masked_value,
1795                          truncate_from_64_bit};
1796   }
1797 
1798  private:
1799   template <typename WordNAdapter>
TryDetectShiftAndMaskOneBitv8::internal::compiler::__anon9327b7910311::BitfieldCheck1800   static base::Optional<BitfieldCheck> TryDetectShiftAndMaskOneBit(Node* node) {
1801     // Look for the pattern `(val >> shift) & 1`. The shift may be omitted.
1802     if (WordNAdapter::IsWordNAnd(NodeMatcher(node))) {
1803       typename WordNAdapter::IntNBinopMatcher mand(node);
1804       if (mand.right().HasResolvedValue() &&
1805           mand.right().ResolvedValue() == 1) {
1806         if (WordNAdapter::IsWordNShr(mand.left()) ||
1807             WordNAdapter::IsWordNSar(mand.left())) {
1808           typename WordNAdapter::UintNBinopMatcher shift(mand.left().node());
1809           if (shift.right().HasResolvedValue() &&
1810               shift.right().ResolvedValue() < 32u) {
1811             uint32_t mask = 1 << shift.right().ResolvedValue();
1812             return BitfieldCheck{shift.left().node(), mask, mask,
1813                                  WordNAdapter::WORD_SIZE == 64};
1814           }
1815         }
1816         return BitfieldCheck{mand.left().node(), 1, 1,
1817                              WordNAdapter::WORD_SIZE == 64};
1818       }
1819     }
1820     return {};
1821   }
1822 };
1823 
1824 }  // namespace
1825 
ReduceWord32And(Node * node)1826 Reduction MachineOperatorReducer::ReduceWord32And(Node* node) {
1827   DCHECK_EQ(IrOpcode::kWord32And, node->opcode());
1828   Reduction reduction = ReduceWordNAnd<Word32Adapter>(node);
1829   if (reduction.Changed()) {
1830     return reduction;
1831   }
1832 
1833   // Attempt to detect multiple bitfield checks from the same bitfield struct
1834   // and fold them into a single check.
1835   Int32BinopMatcher m(node);
1836   if (auto right_bitfield = BitfieldCheck::Detect(m.right().node())) {
1837     if (auto left_bitfield = BitfieldCheck::Detect(m.left().node())) {
1838       if (auto combined_bitfield = left_bitfield->TryCombine(*right_bitfield)) {
1839         Node* source = combined_bitfield->source;
1840         if (combined_bitfield->truncate_from_64_bit) {
1841           source = TruncateInt64ToInt32(source);
1842         }
1843         node->ReplaceInput(0, Word32And(source, combined_bitfield->mask));
1844         node->ReplaceInput(1, Int32Constant(combined_bitfield->masked_value));
1845         NodeProperties::ChangeOp(node, machine()->Word32Equal());
1846         return Changed(node).FollowedBy(ReduceWord32Equal(node));
1847       }
1848     }
1849   }
1850 
1851   return NoChange();
1852 }
1853 
ReduceWord64And(Node * node)1854 Reduction MachineOperatorReducer::ReduceWord64And(Node* node) {
1855   DCHECK_EQ(IrOpcode::kWord64And, node->opcode());
1856   return ReduceWordNAnd<Word64Adapter>(node);
1857 }
1858 
TryMatchWord32Ror(Node * node)1859 Reduction MachineOperatorReducer::TryMatchWord32Ror(Node* node) {
1860   // Recognize rotation, we are matching and transforming as follows:
1861   //   x << y         |  x >>> (32 - y)    =>  x ror (32 - y)
1862   //   x << (32 - y)  |  x >>> y           =>  x ror y
1863   //   x << y         ^  x >>> (32 - y)    =>  x ror (32 - y)   if y & 31 != 0
1864   //   x << (32 - y)  ^  x >>> y           =>  x ror y          if y & 31 != 0
1865   // (As well as the commuted forms.)
1866   // Note the side condition for XOR: the optimization doesn't hold for
1867   // multiples of 32.
1868 
1869   DCHECK(IrOpcode::kWord32Or == node->opcode() ||
1870          IrOpcode::kWord32Xor == node->opcode());
1871   Int32BinopMatcher m(node);
1872   Node* shl = nullptr;
1873   Node* shr = nullptr;
1874   if (m.left().IsWord32Shl() && m.right().IsWord32Shr()) {
1875     shl = m.left().node();
1876     shr = m.right().node();
1877   } else if (m.left().IsWord32Shr() && m.right().IsWord32Shl()) {
1878     shl = m.right().node();
1879     shr = m.left().node();
1880   } else {
1881     return NoChange();
1882   }
1883 
1884   Int32BinopMatcher mshl(shl);
1885   Int32BinopMatcher mshr(shr);
1886   if (mshl.left().node() != mshr.left().node()) return NoChange();
1887 
1888   if (mshl.right().HasResolvedValue() && mshr.right().HasResolvedValue()) {
1889     // Case where y is a constant.
1890     if (mshl.right().ResolvedValue() + mshr.right().ResolvedValue() != 32) {
1891       return NoChange();
1892     }
1893     if (node->opcode() == IrOpcode::kWord32Xor &&
1894         (mshl.right().ResolvedValue() & 31) == 0) {
1895       return NoChange();
1896     }
1897   } else {
1898     Node* sub = nullptr;
1899     Node* y = nullptr;
1900     if (mshl.right().IsInt32Sub()) {
1901       sub = mshl.right().node();
1902       y = mshr.right().node();
1903     } else if (mshr.right().IsInt32Sub()) {
1904       sub = mshr.right().node();
1905       y = mshl.right().node();
1906     } else {
1907       return NoChange();
1908     }
1909 
1910     Int32BinopMatcher msub(sub);
1911     if (!msub.left().Is(32) || msub.right().node() != y) return NoChange();
1912     if (node->opcode() == IrOpcode::kWord32Xor) {
1913       return NoChange();  // Can't guarantee y & 31 != 0.
1914     }
1915   }
1916 
1917   node->ReplaceInput(0, mshl.left().node());
1918   node->ReplaceInput(1, mshr.right().node());
1919   NodeProperties::ChangeOp(node, machine()->Word32Ror());
1920   return Changed(node);
1921 }
1922 
1923 template <typename WordNAdapter>
ReduceWordNOr(Node * node)1924 Reduction MachineOperatorReducer::ReduceWordNOr(Node* node) {
1925   using A = WordNAdapter;
1926   A a(this);
1927 
1928   typename A::IntNBinopMatcher m(node);
1929   if (m.right().Is(0)) return Replace(m.left().node());    // x | 0  => x
1930   if (m.right().Is(-1)) return Replace(m.right().node());  // x | -1 => -1
1931   if (m.IsFoldable()) {  // K | K  => K  (K stands for arbitrary constants)
1932     return a.ReplaceIntN(m.left().ResolvedValue() | m.right().ResolvedValue());
1933   }
1934   if (m.LeftEqualsRight()) return Replace(m.left().node());  // x | x => x
1935 
1936   // (x & K1) | K2 => x | K2 if K2 has ones for every zero bit in K1.
1937   // This case can be constructed by UpdateWord and UpdateWord32 in CSA.
1938   if (m.right().HasResolvedValue()) {
1939     if (A::IsWordNAnd(m.left())) {
1940       typename A::IntNBinopMatcher mand(m.left().node());
1941       if (mand.right().HasResolvedValue()) {
1942         if ((m.right().ResolvedValue() | mand.right().ResolvedValue()) == -1) {
1943           node->ReplaceInput(0, mand.left().node());
1944           return Changed(node);
1945         }
1946       }
1947     }
1948   }
1949 
1950   return a.TryMatchWordNRor(node);
1951 }
1952 
ReduceWord32Or(Node * node)1953 Reduction MachineOperatorReducer::ReduceWord32Or(Node* node) {
1954   DCHECK_EQ(IrOpcode::kWord32Or, node->opcode());
1955   return ReduceWordNOr<Word32Adapter>(node);
1956 }
1957 
ReduceWord64Or(Node * node)1958 Reduction MachineOperatorReducer::ReduceWord64Or(Node* node) {
1959   DCHECK_EQ(IrOpcode::kWord64Or, node->opcode());
1960   return ReduceWordNOr<Word64Adapter>(node);
1961 }
1962 
1963 template <typename WordNAdapter>
ReduceWordNXor(Node * node)1964 Reduction MachineOperatorReducer::ReduceWordNXor(Node* node) {
1965   using A = WordNAdapter;
1966   A a(this);
1967 
1968   typename A::IntNBinopMatcher m(node);
1969   if (m.right().Is(0)) return Replace(m.left().node());  // x ^ 0 => x
1970   if (m.IsFoldable()) {  // K ^ K => K  (K stands for arbitrary constants)
1971     return a.ReplaceIntN(m.left().ResolvedValue() ^ m.right().ResolvedValue());
1972   }
1973   if (m.LeftEqualsRight()) return ReplaceInt32(0);  // x ^ x => 0
1974   if (A::IsWordNXor(m.left()) && m.right().Is(-1)) {
1975     typename A::IntNBinopMatcher mleft(m.left().node());
1976     if (mleft.right().Is(-1)) {  // (x ^ -1) ^ -1 => x
1977       return Replace(mleft.left().node());
1978     }
1979   }
1980 
1981   return a.TryMatchWordNRor(node);
1982 }
1983 
ReduceWord32Xor(Node * node)1984 Reduction MachineOperatorReducer::ReduceWord32Xor(Node* node) {
1985   DCHECK_EQ(IrOpcode::kWord32Xor, node->opcode());
1986   Int32BinopMatcher m(node);
1987   if (m.right().IsWord32Equal() && m.left().Is(1)) {
1988     return Replace(Word32Equal(m.right().node(), Int32Constant(0)));
1989   }
1990   return ReduceWordNXor<Word32Adapter>(node);
1991 }
1992 
ReduceWord64Xor(Node * node)1993 Reduction MachineOperatorReducer::ReduceWord64Xor(Node* node) {
1994   DCHECK_EQ(IrOpcode::kWord64Xor, node->opcode());
1995   return ReduceWordNXor<Word64Adapter>(node);
1996 }
1997 
ReduceWord32Equal(Node * node)1998 Reduction MachineOperatorReducer::ReduceWord32Equal(Node* node) {
1999   Int32BinopMatcher m(node);
2000   if (m.IsFoldable()) {  // K == K => K  (K stands for arbitrary constants)
2001     return ReplaceBool(m.left().ResolvedValue() == m.right().ResolvedValue());
2002   }
2003   if (m.left().IsInt32Sub() && m.right().Is(0)) {  // x - y == 0 => x == y
2004     Int32BinopMatcher msub(m.left().node());
2005     node->ReplaceInput(0, msub.left().node());
2006     node->ReplaceInput(1, msub.right().node());
2007     return Changed(node);
2008   }
2009   // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
2010   if (m.LeftEqualsRight()) return ReplaceBool(true);  // x == x => true
2011   if (m.right().HasResolvedValue()) {
2012     base::Optional<std::pair<Node*, uint32_t>> replacements;
2013     if (m.left().IsTruncateInt64ToInt32()) {
2014       replacements = ReduceWord32EqualForConstantRhs<Word64Adapter>(
2015           NodeProperties::GetValueInput(m.left().node(), 0),
2016           static_cast<uint32_t>(m.right().ResolvedValue()));
2017     } else {
2018       replacements = ReduceWord32EqualForConstantRhs<Word32Adapter>(
2019           m.left().node(), static_cast<uint32_t>(m.right().ResolvedValue()));
2020     }
2021     if (replacements) {
2022       node->ReplaceInput(0, replacements->first);
2023       node->ReplaceInput(1, Uint32Constant(replacements->second));
2024       return Changed(node);
2025     }
2026   }
2027   // This is a workaround for not having escape analysis for wasm
2028   // (machine-level) turbofan graphs.
2029   if (!ObjectsMayAlias(m.left().node(), m.right().node())) {
2030     return ReplaceBool(false);
2031   }
2032 
2033   return NoChange();
2034 }
2035 
ReduceFloat64InsertLowWord32(Node * node)2036 Reduction MachineOperatorReducer::ReduceFloat64InsertLowWord32(Node* node) {
2037   DCHECK_EQ(IrOpcode::kFloat64InsertLowWord32, node->opcode());
2038   Float64Matcher mlhs(node->InputAt(0));
2039   Uint32Matcher mrhs(node->InputAt(1));
2040   if (mlhs.HasResolvedValue() && mrhs.HasResolvedValue()) {
2041     return ReplaceFloat64(
2042         bit_cast<double>((bit_cast<uint64_t>(mlhs.ResolvedValue()) &
2043                           uint64_t{0xFFFFFFFF00000000}) |
2044                          mrhs.ResolvedValue()));
2045   }
2046   return NoChange();
2047 }
2048 
ReduceFloat64InsertHighWord32(Node * node)2049 Reduction MachineOperatorReducer::ReduceFloat64InsertHighWord32(Node* node) {
2050   DCHECK_EQ(IrOpcode::kFloat64InsertHighWord32, node->opcode());
2051   Float64Matcher mlhs(node->InputAt(0));
2052   Uint32Matcher mrhs(node->InputAt(1));
2053   if (mlhs.HasResolvedValue() && mrhs.HasResolvedValue()) {
2054     return ReplaceFloat64(bit_cast<double>(
2055         (bit_cast<uint64_t>(mlhs.ResolvedValue()) & uint64_t{0xFFFFFFFF}) |
2056         (static_cast<uint64_t>(mrhs.ResolvedValue()) << 32)));
2057   }
2058   return NoChange();
2059 }
2060 
2061 namespace {
2062 
IsFloat64RepresentableAsFloat32(const Float64Matcher & m)2063 bool IsFloat64RepresentableAsFloat32(const Float64Matcher& m) {
2064   if (m.HasResolvedValue()) {
2065     double v = m.ResolvedValue();
2066     return DoubleToFloat32(v) == v;
2067   }
2068   return false;
2069 }
2070 
2071 }  // namespace
2072 
ReduceFloat64Compare(Node * node)2073 Reduction MachineOperatorReducer::ReduceFloat64Compare(Node* node) {
2074   DCHECK(IrOpcode::kFloat64Equal == node->opcode() ||
2075          IrOpcode::kFloat64LessThan == node->opcode() ||
2076          IrOpcode::kFloat64LessThanOrEqual == node->opcode());
2077   Float64BinopMatcher m(node);
2078   if (m.IsFoldable()) {
2079     switch (node->opcode()) {
2080       case IrOpcode::kFloat64Equal:
2081         return ReplaceBool(m.left().ResolvedValue() ==
2082                            m.right().ResolvedValue());
2083       case IrOpcode::kFloat64LessThan:
2084         return ReplaceBool(m.left().ResolvedValue() <
2085                            m.right().ResolvedValue());
2086       case IrOpcode::kFloat64LessThanOrEqual:
2087         return ReplaceBool(m.left().ResolvedValue() <=
2088                            m.right().ResolvedValue());
2089       default:
2090         UNREACHABLE();
2091     }
2092   } else if ((m.left().IsChangeFloat32ToFloat64() &&
2093               m.right().IsChangeFloat32ToFloat64()) ||
2094              (m.left().IsChangeFloat32ToFloat64() &&
2095               IsFloat64RepresentableAsFloat32(m.right())) ||
2096              (IsFloat64RepresentableAsFloat32(m.left()) &&
2097               m.right().IsChangeFloat32ToFloat64())) {
2098     // As all Float32 values have an exact representation in Float64, comparing
2099     // two Float64 values both converted from Float32 is equivalent to comparing
2100     // the original Float32s, so we can ignore the conversions. We can also
2101     // reduce comparisons of converted Float64 values against constants that
2102     // can be represented exactly as Float32.
2103     switch (node->opcode()) {
2104       case IrOpcode::kFloat64Equal:
2105         NodeProperties::ChangeOp(node, machine()->Float32Equal());
2106         break;
2107       case IrOpcode::kFloat64LessThan:
2108         NodeProperties::ChangeOp(node, machine()->Float32LessThan());
2109         break;
2110       case IrOpcode::kFloat64LessThanOrEqual:
2111         NodeProperties::ChangeOp(node, machine()->Float32LessThanOrEqual());
2112         break;
2113       default:
2114         UNREACHABLE();
2115     }
2116     node->ReplaceInput(
2117         0, m.left().HasResolvedValue()
2118                ? Float32Constant(static_cast<float>(m.left().ResolvedValue()))
2119                : m.left().InputAt(0));
2120     node->ReplaceInput(
2121         1, m.right().HasResolvedValue()
2122                ? Float32Constant(static_cast<float>(m.right().ResolvedValue()))
2123                : m.right().InputAt(0));
2124     return Changed(node);
2125   }
2126   return NoChange();
2127 }
2128 
ReduceFloat64RoundDown(Node * node)2129 Reduction MachineOperatorReducer::ReduceFloat64RoundDown(Node* node) {
2130   DCHECK_EQ(IrOpcode::kFloat64RoundDown, node->opcode());
2131   Float64Matcher m(node->InputAt(0));
2132   if (m.HasResolvedValue()) {
2133     return ReplaceFloat64(std::floor(m.ResolvedValue()));
2134   }
2135   return NoChange();
2136 }
2137 
ReduceConditional(Node * node)2138 Reduction MachineOperatorReducer::ReduceConditional(Node* node) {
2139   DCHECK(node->opcode() == IrOpcode::kBranch ||
2140          node->opcode() == IrOpcode::kDeoptimizeIf ||
2141          node->opcode() == IrOpcode::kDeoptimizeUnless ||
2142          node->opcode() == IrOpcode::kTrapIf ||
2143          node->opcode() == IrOpcode::kTrapUnless);
2144   // This reducer only applies operator reductions to the branch condition.
2145   // Reductions involving control flow happen elsewhere. Non-zero inputs are
2146   // considered true in all conditional ops.
2147   NodeMatcher condition(NodeProperties::GetValueInput(node, 0));
2148   if (condition.IsTruncateInt64ToInt32()) {
2149     if (auto replacement =
2150             ReduceConditionalN<Word64Adapter>(condition.node())) {
2151       NodeProperties::ReplaceValueInput(node, *replacement, 0);
2152       return Changed(node);
2153     }
2154   } else if (auto replacement = ReduceConditionalN<Word32Adapter>(node)) {
2155     NodeProperties::ReplaceValueInput(node, *replacement, 0);
2156     return Changed(node);
2157   }
2158   return NoChange();
2159 }
2160 
2161 template <typename WordNAdapter>
ReduceConditionalN(Node * node)2162 base::Optional<Node*> MachineOperatorReducer::ReduceConditionalN(Node* node) {
2163   NodeMatcher condition(NodeProperties::GetValueInput(node, 0));
2164   // Branch conditions are 32-bit comparisons against zero, so they are the
2165   // opposite of a 32-bit `x == 0` node. To avoid repetition, we can reuse logic
2166   // for Word32Equal: if `x == 0` can reduce to `y == 0`, then branch(x) can
2167   // reduce to branch(y).
2168   auto replacements =
2169       ReduceWord32EqualForConstantRhs<WordNAdapter>(condition.node(), 0);
2170   if (replacements && replacements->second == 0) return replacements->first;
2171   return {};
2172 }
2173 
2174 template <typename WordNAdapter>
2175 base::Optional<std::pair<Node*, uint32_t>>
ReduceWord32EqualForConstantRhs(Node * lhs,uint32_t rhs)2176 MachineOperatorReducer::ReduceWord32EqualForConstantRhs(Node* lhs,
2177                                                         uint32_t rhs) {
2178   if (WordNAdapter::IsWordNAnd(NodeMatcher(lhs))) {
2179     typename WordNAdapter::UintNBinopMatcher mand(lhs);
2180     if ((WordNAdapter::IsWordNShr(mand.left()) ||
2181          WordNAdapter::IsWordNSar(mand.left())) &&
2182         mand.right().HasResolvedValue()) {
2183       typename WordNAdapter::UintNBinopMatcher mshift(mand.left().node());
2184       // ((x >> K1) & K2) == K3 => (x & (K2 << K1)) == (K3 << K1)
2185       if (mshift.right().HasResolvedValue()) {
2186         auto shift_bits = mshift.right().ResolvedValue();
2187         auto mask = mand.right().ResolvedValue();
2188         // Make sure that we won't shift data off the end, and that all of the
2189         // data ends up in the lower 32 bits for 64-bit mode.
2190         if (shift_bits <= base::bits::CountLeadingZeros(mask) &&
2191             shift_bits <= base::bits::CountLeadingZeros(rhs) &&
2192             mask << shift_bits <= std::numeric_limits<uint32_t>::max()) {
2193           Node* new_input = mshift.left().node();
2194           uint32_t new_mask = static_cast<uint32_t>(mask << shift_bits);
2195           uint32_t new_rhs = rhs << shift_bits;
2196           if (WordNAdapter::WORD_SIZE == 64) {
2197             // We can truncate before performing the And.
2198             new_input = TruncateInt64ToInt32(new_input);
2199           }
2200           return std::make_pair(Word32And(new_input, new_mask), new_rhs);
2201         }
2202       }
2203     }
2204   }
2205   return {};
2206 }
2207 
common() const2208 CommonOperatorBuilder* MachineOperatorReducer::common() const {
2209   return mcgraph()->common();
2210 }
2211 
machine() const2212 MachineOperatorBuilder* MachineOperatorReducer::machine() const {
2213   return mcgraph()->machine();
2214 }
2215 
graph() const2216 Graph* MachineOperatorReducer::graph() const { return mcgraph()->graph(); }
2217 
2218 }  // namespace compiler
2219 }  // namespace internal
2220 }  // namespace v8
2221