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