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
8 #include "src/base/bits.h"
9 #include "src/base/division-by-constant.h"
10 #include "src/base/ieee754.h"
11 #include "src/base/overflowing-math.h"
12 #include "src/compiler/diamond.h"
13 #include "src/compiler/graph.h"
14 #include "src/compiler/machine-graph.h"
15 #include "src/compiler/node-matchers.h"
16 #include "src/compiler/node-properties.h"
17 #include "src/numbers/conversions-inl.h"
18
19 namespace v8 {
20 namespace internal {
21 namespace compiler {
22
23 // Some optimizations performed by the MachineOperatorReducer can be applied
24 // to both Word32 and Word64 operations. Those are implemented in a generic
25 // way to be reused for different word sizes.
26 // This class adapts a generic algorithm to Word32 operations.
27 class Word32Adapter {
28 public:
29 using IntNBinopMatcher = Int32BinopMatcher;
30 using UintNBinopMatcher = Uint32BinopMatcher;
31 using intN_t = int32_t;
32 // WORD_SIZE refers to the N for which this adapter specializes.
33 static constexpr std::size_t WORD_SIZE = 32;
34
Word32Adapter(MachineOperatorReducer * reducer)35 explicit Word32Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
36
37 template <typename T>
IsWordNAnd(const T & x)38 static bool IsWordNAnd(const T& x) {
39 return x.IsWord32And();
40 }
41 template <typename T>
IsWordNShl(const T & x)42 static bool IsWordNShl(const T& x) {
43 return x.IsWord32Shl();
44 }
45 template <typename T>
IsWordNXor(const T & x)46 static bool IsWordNXor(const T& x) {
47 return x.IsWord32Xor();
48 }
49 template <typename T>
IsIntNAdd(const T & x)50 static bool IsIntNAdd(const T& x) {
51 return x.IsInt32Add();
52 }
53 template <typename T>
IsIntNMul(const T & x)54 static bool IsIntNMul(const T& x) {
55 return x.IsInt32Mul();
56 }
57
IntNAdd(MachineOperatorBuilder * machine)58 const Operator* IntNAdd(MachineOperatorBuilder* machine) {
59 return machine->Int32Add();
60 }
61
ReplaceIntN(int32_t value)62 Reduction ReplaceIntN(int32_t value) { return r_->ReplaceInt32(value); }
ReduceWordNAnd(Node * node)63 Reduction ReduceWordNAnd(Node* node) { return r_->ReduceWord32And(node); }
ReduceIntNAdd(Node * node)64 Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt32Add(node); }
TryMatchWordNRor(Node * node)65 Reduction TryMatchWordNRor(Node* node) { return r_->TryMatchWord32Ror(node); }
66
IntNConstant(int32_t value)67 Node* IntNConstant(int32_t value) { return r_->Int32Constant(value); }
WordNAnd(Node * lhs,Node * rhs)68 Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word32And(lhs, rhs); }
69
70 private:
71 MachineOperatorReducer* r_;
72 };
73
74 // Some optimizations performed by the MachineOperatorReducer can be applied
75 // to both Word32 and Word64 operations. Those are implemented in a generic
76 // way to be reused for different word sizes.
77 // This class adapts a generic algorithm to Word64 operations.
78 class Word64Adapter {
79 public:
80 using IntNBinopMatcher = Int64BinopMatcher;
81 using UintNBinopMatcher = Uint64BinopMatcher;
82 using intN_t = int64_t;
83 // WORD_SIZE refers to the N for which this adapter specializes.
84 static constexpr std::size_t WORD_SIZE = 64;
85
Word64Adapter(MachineOperatorReducer * reducer)86 explicit Word64Adapter(MachineOperatorReducer* reducer) : r_(reducer) {}
87
88 template <typename T>
IsWordNAnd(const T & x)89 static bool IsWordNAnd(const T& x) {
90 return x.IsWord64And();
91 }
92 template <typename T>
IsWordNShl(const T & x)93 static bool IsWordNShl(const T& x) {
94 return x.IsWord64Shl();
95 }
96 template <typename T>
IsWordNXor(const T & x)97 static bool IsWordNXor(const T& x) {
98 return x.IsWord64Xor();
99 }
100 template <typename T>
IsIntNAdd(const T & x)101 static bool IsIntNAdd(const T& x) {
102 return x.IsInt64Add();
103 }
104 template <typename T>
IsIntNMul(const T & x)105 static bool IsIntNMul(const T& x) {
106 return x.IsInt64Mul();
107 }
108
IntNAdd(MachineOperatorBuilder * machine)109 static const Operator* IntNAdd(MachineOperatorBuilder* machine) {
110 return machine->Int64Add();
111 }
112
ReplaceIntN(int64_t value)113 Reduction ReplaceIntN(int64_t value) { return r_->ReplaceInt64(value); }
ReduceWordNAnd(Node * node)114 Reduction ReduceWordNAnd(Node* node) { return r_->ReduceWord64And(node); }
ReduceIntNAdd(Node * node)115 Reduction ReduceIntNAdd(Node* node) { return r_->ReduceInt64Add(node); }
TryMatchWordNRor(Node * node)116 Reduction TryMatchWordNRor(Node* node) {
117 // TODO(nicohartmann@): Add a MachineOperatorReducer::TryMatchWord64Ror.
118 return r_->NoChange();
119 }
120
IntNConstant(int64_t value)121 Node* IntNConstant(int64_t value) { return r_->Int64Constant(value); }
WordNAnd(Node * lhs,Node * rhs)122 Node* WordNAnd(Node* lhs, Node* rhs) { return r_->Word64And(lhs, rhs); }
123
124 private:
125 MachineOperatorReducer* r_;
126 };
127
MachineOperatorReducer(Editor * editor,MachineGraph * mcgraph,bool allow_signalling_nan)128 MachineOperatorReducer::MachineOperatorReducer(Editor* editor,
129 MachineGraph* mcgraph,
130 bool allow_signalling_nan)
131 : AdvancedReducer(editor),
132 mcgraph_(mcgraph),
133 allow_signalling_nan_(allow_signalling_nan) {}
134
135 MachineOperatorReducer::~MachineOperatorReducer() = default;
136
137
Float32Constant(volatile float value)138 Node* MachineOperatorReducer::Float32Constant(volatile float value) {
139 return graph()->NewNode(common()->Float32Constant(value));
140 }
141
Float64Constant(volatile double value)142 Node* MachineOperatorReducer::Float64Constant(volatile double value) {
143 return mcgraph()->Float64Constant(value);
144 }
145
Int32Constant(int32_t value)146 Node* MachineOperatorReducer::Int32Constant(int32_t value) {
147 return mcgraph()->Int32Constant(value);
148 }
149
Int64Constant(int64_t value)150 Node* MachineOperatorReducer::Int64Constant(int64_t value) {
151 return graph()->NewNode(common()->Int64Constant(value));
152 }
153
Float64Mul(Node * lhs,Node * rhs)154 Node* MachineOperatorReducer::Float64Mul(Node* lhs, Node* rhs) {
155 return graph()->NewNode(machine()->Float64Mul(), lhs, rhs);
156 }
157
Float64PowHalf(Node * value)158 Node* MachineOperatorReducer::Float64PowHalf(Node* value) {
159 value =
160 graph()->NewNode(machine()->Float64Add(), Float64Constant(0.0), value);
161 Diamond d(graph(), common(),
162 graph()->NewNode(machine()->Float64LessThanOrEqual(), value,
163 Float64Constant(-V8_INFINITY)),
164 BranchHint::kFalse);
165 return d.Phi(MachineRepresentation::kFloat64, Float64Constant(V8_INFINITY),
166 graph()->NewNode(machine()->Float64Sqrt(), value));
167 }
168
Word32And(Node * lhs,Node * rhs)169 Node* MachineOperatorReducer::Word32And(Node* lhs, Node* rhs) {
170 Node* const node = graph()->NewNode(machine()->Word32And(), lhs, rhs);
171 Reduction const reduction = ReduceWord32And(node);
172 return reduction.Changed() ? reduction.replacement() : node;
173 }
174
Word32Sar(Node * lhs,uint32_t rhs)175 Node* MachineOperatorReducer::Word32Sar(Node* lhs, uint32_t rhs) {
176 if (rhs == 0) return lhs;
177 return graph()->NewNode(machine()->Word32Sar(), lhs, Uint32Constant(rhs));
178 }
179
Word32Shr(Node * lhs,uint32_t rhs)180 Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) {
181 if (rhs == 0) return lhs;
182 return graph()->NewNode(machine()->Word32Shr(), lhs, Uint32Constant(rhs));
183 }
184
Word32Equal(Node * lhs,Node * rhs)185 Node* MachineOperatorReducer::Word32Equal(Node* lhs, Node* rhs) {
186 return graph()->NewNode(machine()->Word32Equal(), lhs, rhs);
187 }
188
Word64And(Node * lhs,Node * rhs)189 Node* MachineOperatorReducer::Word64And(Node* lhs, Node* rhs) {
190 Node* const node = graph()->NewNode(machine()->Word64And(), lhs, rhs);
191 Reduction const reduction = ReduceWord64And(node);
192 return reduction.Changed() ? reduction.replacement() : node;
193 }
194
Int32Add(Node * lhs,Node * rhs)195 Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) {
196 Node* const node = graph()->NewNode(machine()->Int32Add(), lhs, rhs);
197 Reduction const reduction = ReduceInt32Add(node);
198 return reduction.Changed() ? reduction.replacement() : node;
199 }
200
Int32Sub(Node * lhs,Node * rhs)201 Node* MachineOperatorReducer::Int32Sub(Node* lhs, Node* rhs) {
202 Node* const node = graph()->NewNode(machine()->Int32Sub(), lhs, rhs);
203 Reduction const reduction = ReduceInt32Sub(node);
204 return reduction.Changed() ? reduction.replacement() : node;
205 }
206
Int32Mul(Node * lhs,Node * rhs)207 Node* MachineOperatorReducer::Int32Mul(Node* lhs, Node* rhs) {
208 return graph()->NewNode(machine()->Int32Mul(), lhs, rhs);
209 }
210
Int32Div(Node * dividend,int32_t divisor)211 Node* MachineOperatorReducer::Int32Div(Node* dividend, int32_t divisor) {
212 DCHECK_NE(0, divisor);
213 DCHECK_NE(std::numeric_limits<int32_t>::min(), divisor);
214 base::MagicNumbersForDivision<uint32_t> const mag =
215 base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
216 Node* quotient = graph()->NewNode(machine()->Int32MulHigh(), dividend,
217 Uint32Constant(mag.multiplier));
218 if (divisor > 0 && bit_cast<int32_t>(mag.multiplier) < 0) {
219 quotient = Int32Add(quotient, dividend);
220 } else if (divisor < 0 && bit_cast<int32_t>(mag.multiplier) > 0) {
221 quotient = Int32Sub(quotient, dividend);
222 }
223 return Int32Add(Word32Sar(quotient, mag.shift), Word32Shr(dividend, 31));
224 }
225
Uint32Div(Node * dividend,uint32_t divisor)226 Node* MachineOperatorReducer::Uint32Div(Node* dividend, uint32_t divisor) {
227 DCHECK_LT(0u, divisor);
228 // If the divisor is even, we can avoid using the expensive fixup by shifting
229 // the dividend upfront.
230 unsigned const shift = base::bits::CountTrailingZeros(divisor);
231 dividend = Word32Shr(dividend, shift);
232 divisor >>= shift;
233 // Compute the magic number for the (shifted) divisor.
234 base::MagicNumbersForDivision<uint32_t> const mag =
235 base::UnsignedDivisionByConstant(divisor, shift);
236 Node* quotient = graph()->NewNode(machine()->Uint32MulHigh(), dividend,
237 Uint32Constant(mag.multiplier));
238 if (mag.add) {
239 DCHECK_LE(1u, mag.shift);
240 quotient = Word32Shr(
241 Int32Add(Word32Shr(Int32Sub(dividend, quotient), 1), quotient),
242 mag.shift - 1);
243 } else {
244 quotient = Word32Shr(quotient, mag.shift);
245 }
246 return quotient;
247 }
248
249 // Perform constant folding and strength reduction on machine operators.
Reduce(Node * node)250 Reduction MachineOperatorReducer::Reduce(Node* node) {
251 switch (node->opcode()) {
252 case IrOpcode::kProjection:
253 return ReduceProjection(ProjectionIndexOf(node->op()), node->InputAt(0));
254 case IrOpcode::kWord32And:
255 return ReduceWord32And(node);
256 case IrOpcode::kWord64And:
257 return ReduceWord64And(node);
258 case IrOpcode::kWord32Or:
259 return ReduceWord32Or(node);
260 case IrOpcode::kWord64Or:
261 return ReduceWord64Or(node);
262 case IrOpcode::kWord32Xor:
263 return ReduceWord32Xor(node);
264 case IrOpcode::kWord64Xor:
265 return ReduceWord64Xor(node);
266 case IrOpcode::kWord32Shl:
267 return ReduceWord32Shl(node);
268 case IrOpcode::kWord64Shl:
269 return ReduceWord64Shl(node);
270 case IrOpcode::kWord32Shr:
271 return ReduceWord32Shr(node);
272 case IrOpcode::kWord64Shr:
273 return ReduceWord64Shr(node);
274 case IrOpcode::kWord32Sar:
275 return ReduceWord32Sar(node);
276 case IrOpcode::kWord64Sar:
277 return ReduceWord64Sar(node);
278 case IrOpcode::kWord32Ror: {
279 Int32BinopMatcher m(node);
280 if (m.right().Is(0)) return Replace(m.left().node()); // x ror 0 => x
281 if (m.IsFoldable()) { // K ror K => K
282 return ReplaceInt32(base::bits::RotateRight32(m.left().Value(),
283 m.right().Value() & 31));
284 }
285 break;
286 }
287 case IrOpcode::kWord32Equal: {
288 Int32BinopMatcher m(node);
289 if (m.IsFoldable()) { // K == K => K
290 return ReplaceBool(m.left().Value() == m.right().Value());
291 }
292 if (m.left().IsInt32Sub() && m.right().Is(0)) { // x - y == 0 => x == y
293 Int32BinopMatcher msub(m.left().node());
294 node->ReplaceInput(0, msub.left().node());
295 node->ReplaceInput(1, msub.right().node());
296 return Changed(node);
297 }
298 // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
299 if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
300 if (m.left().IsWord32And() && m.right().HasValue()) {
301 Uint32BinopMatcher mand(m.left().node());
302 if ((mand.left().IsWord32Shr() || mand.left().IsWord32Sar()) &&
303 mand.right().HasValue()) {
304 Uint32BinopMatcher mshift(mand.left().node());
305 // ((x >> K1) & K2) == K3 => (x & (K2 << K1)) == (K3 << K1)
306 if (mshift.right().HasValue()) {
307 auto shift_bits = mshift.right().Value();
308 auto mask = mand.right().Value();
309 auto rhs = static_cast<uint32_t>(m.right().Value());
310 // Make sure that we won't shift data off the end.
311 if (shift_bits <= base::bits::CountLeadingZeros(mask) &&
312 shift_bits <= base::bits::CountLeadingZeros(rhs)) {
313 node->ReplaceInput(
314 0, Word32And(mshift.left().node(), mask << shift_bits));
315 node->ReplaceInput(1, Int32Constant(rhs << shift_bits));
316 return Changed(node);
317 }
318 }
319 }
320 }
321 break;
322 }
323 case IrOpcode::kWord64Equal: {
324 Int64BinopMatcher m(node);
325 if (m.IsFoldable()) { // K == K => K
326 return ReplaceBool(m.left().Value() == m.right().Value());
327 }
328 if (m.left().IsInt64Sub() && m.right().Is(0)) { // x - y == 0 => x == y
329 Int64BinopMatcher msub(m.left().node());
330 node->ReplaceInput(0, msub.left().node());
331 node->ReplaceInput(1, msub.right().node());
332 return Changed(node);
333 }
334 // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
335 if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
336 break;
337 }
338 case IrOpcode::kInt32Add:
339 return ReduceInt32Add(node);
340 case IrOpcode::kInt64Add:
341 return ReduceInt64Add(node);
342 case IrOpcode::kInt32Sub:
343 return ReduceInt32Sub(node);
344 case IrOpcode::kInt64Sub:
345 return ReduceInt64Sub(node);
346 case IrOpcode::kInt32Mul: {
347 Int32BinopMatcher m(node);
348 if (m.right().Is(0)) return Replace(m.right().node()); // x * 0 => 0
349 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1 => x
350 if (m.IsFoldable()) { // K * K => K
351 return ReplaceInt32(
352 base::MulWithWraparound(m.left().Value(), m.right().Value()));
353 }
354 if (m.right().Is(-1)) { // x * -1 => 0 - x
355 node->ReplaceInput(0, Int32Constant(0));
356 node->ReplaceInput(1, m.left().node());
357 NodeProperties::ChangeOp(node, machine()->Int32Sub());
358 return Changed(node);
359 }
360 if (m.right().IsPowerOf2()) { // x * 2^n => x << n
361 node->ReplaceInput(
362 1, Int32Constant(base::bits::WhichPowerOfTwo(m.right().Value())));
363 NodeProperties::ChangeOp(node, machine()->Word32Shl());
364 Reduction reduction = ReduceWord32Shl(node);
365 return reduction.Changed() ? reduction : Changed(node);
366 }
367 break;
368 }
369 case IrOpcode::kInt32MulWithOverflow: {
370 Int32BinopMatcher m(node);
371 if (m.right().Is(2)) {
372 node->ReplaceInput(1, m.left().node());
373 NodeProperties::ChangeOp(node, machine()->Int32AddWithOverflow());
374 return Changed(node);
375 }
376 if (m.right().Is(-1)) {
377 node->ReplaceInput(0, Int32Constant(0));
378 node->ReplaceInput(1, m.left().node());
379 NodeProperties::ChangeOp(node, machine()->Int32SubWithOverflow());
380 return Changed(node);
381 }
382 break;
383 }
384 case IrOpcode::kInt64Mul:
385 return ReduceInt64Mul(node);
386 case IrOpcode::kInt32Div:
387 return ReduceInt32Div(node);
388 case IrOpcode::kUint32Div:
389 return ReduceUint32Div(node);
390 case IrOpcode::kInt32Mod:
391 return ReduceInt32Mod(node);
392 case IrOpcode::kUint32Mod:
393 return ReduceUint32Mod(node);
394 case IrOpcode::kInt32LessThan: {
395 Int32BinopMatcher m(node);
396 if (m.IsFoldable()) { // K < K => K
397 return ReplaceBool(m.left().Value() < m.right().Value());
398 }
399 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
400 if (m.left().IsWord32Or() && m.right().Is(0)) {
401 // (x | K) < 0 => true or (K | x) < 0 => true iff K < 0
402 Int32BinopMatcher mleftmatcher(m.left().node());
403 if (mleftmatcher.left().IsNegative() ||
404 mleftmatcher.right().IsNegative()) {
405 return ReplaceBool(true);
406 }
407 }
408 break;
409 }
410 case IrOpcode::kInt32LessThanOrEqual: {
411 Int32BinopMatcher m(node);
412 if (m.IsFoldable()) { // K <= K => K
413 return ReplaceBool(m.left().Value() <= m.right().Value());
414 }
415 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
416 break;
417 }
418 case IrOpcode::kUint32LessThan: {
419 Uint32BinopMatcher m(node);
420 if (m.left().Is(kMaxUInt32)) return ReplaceBool(false); // M < x => false
421 if (m.right().Is(0)) return ReplaceBool(false); // x < 0 => false
422 if (m.IsFoldable()) { // K < K => K
423 return ReplaceBool(m.left().Value() < m.right().Value());
424 }
425 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
426 if (m.left().IsWord32Sar() && m.right().HasValue()) {
427 Int32BinopMatcher mleft(m.left().node());
428 if (mleft.right().HasValue()) {
429 // (x >> K) < C => x < (C << K)
430 // when C < (M >> K)
431 const uint32_t c = m.right().Value();
432 const uint32_t k = mleft.right().Value() & 0x1F;
433 if (c < static_cast<uint32_t>(kMaxInt >> k)) {
434 node->ReplaceInput(0, mleft.left().node());
435 node->ReplaceInput(1, Uint32Constant(c << k));
436 return Changed(node);
437 }
438 // TODO(turbofan): else the comparison is always true.
439 }
440 }
441 break;
442 }
443 case IrOpcode::kUint32LessThanOrEqual: {
444 Uint32BinopMatcher m(node);
445 if (m.left().Is(0)) return ReplaceBool(true); // 0 <= x => true
446 if (m.right().Is(kMaxUInt32)) return ReplaceBool(true); // x <= M => true
447 if (m.IsFoldable()) { // K <= K => K
448 return ReplaceBool(m.left().Value() <= m.right().Value());
449 }
450 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
451 break;
452 }
453 case IrOpcode::kFloat32Sub: {
454 Float32BinopMatcher m(node);
455 if (allow_signalling_nan_ && m.right().Is(0) &&
456 (std::copysign(1.0, m.right().Value()) > 0)) {
457 return Replace(m.left().node()); // x - 0 => x
458 }
459 if (m.right().IsNaN()) { // x - NaN => NaN
460 // Do some calculation to make a signalling NaN quiet.
461 return ReplaceFloat32(m.right().Value() - m.right().Value());
462 }
463 if (m.left().IsNaN()) { // NaN - x => NaN
464 // Do some calculation to make a signalling NaN quiet.
465 return ReplaceFloat32(m.left().Value() - m.left().Value());
466 }
467 if (m.IsFoldable()) { // L - R => (L - R)
468 return ReplaceFloat32(m.left().Value() - m.right().Value());
469 }
470 if (allow_signalling_nan_ && m.left().IsMinusZero()) {
471 // -0.0 - round_down(-0.0 - R) => round_up(R)
472 if (machine()->Float32RoundUp().IsSupported() &&
473 m.right().IsFloat32RoundDown()) {
474 if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat32Sub) {
475 Float32BinopMatcher mright0(m.right().InputAt(0));
476 if (mright0.left().IsMinusZero()) {
477 return Replace(graph()->NewNode(machine()->Float32RoundUp().op(),
478 mright0.right().node()));
479 }
480 }
481 }
482 // -0.0 - R => -R
483 node->RemoveInput(0);
484 NodeProperties::ChangeOp(node, machine()->Float32Neg());
485 return Changed(node);
486 }
487 break;
488 }
489 case IrOpcode::kFloat64Add: {
490 Float64BinopMatcher m(node);
491 if (m.IsFoldable()) { // K + K => K
492 return ReplaceFloat64(m.left().Value() + m.right().Value());
493 }
494 break;
495 }
496 case IrOpcode::kFloat64Sub: {
497 Float64BinopMatcher m(node);
498 if (allow_signalling_nan_ && m.right().Is(0) &&
499 (Double(m.right().Value()).Sign() > 0)) {
500 return Replace(m.left().node()); // x - 0 => x
501 }
502 if (m.right().IsNaN()) { // x - NaN => NaN
503 // Do some calculation to make a signalling NaN quiet.
504 return ReplaceFloat64(m.right().Value() - m.right().Value());
505 }
506 if (m.left().IsNaN()) { // NaN - x => NaN
507 // Do some calculation to make a signalling NaN quiet.
508 return ReplaceFloat64(m.left().Value() - m.left().Value());
509 }
510 if (m.IsFoldable()) { // L - R => (L - R)
511 return ReplaceFloat64(m.left().Value() - m.right().Value());
512 }
513 if (allow_signalling_nan_ && m.left().IsMinusZero()) {
514 // -0.0 - round_down(-0.0 - R) => round_up(R)
515 if (machine()->Float64RoundUp().IsSupported() &&
516 m.right().IsFloat64RoundDown()) {
517 if (m.right().InputAt(0)->opcode() == IrOpcode::kFloat64Sub) {
518 Float64BinopMatcher mright0(m.right().InputAt(0));
519 if (mright0.left().IsMinusZero()) {
520 return Replace(graph()->NewNode(machine()->Float64RoundUp().op(),
521 mright0.right().node()));
522 }
523 }
524 }
525 // -0.0 - R => -R
526 node->RemoveInput(0);
527 NodeProperties::ChangeOp(node, machine()->Float64Neg());
528 return Changed(node);
529 }
530 break;
531 }
532 case IrOpcode::kFloat64Mul: {
533 Float64BinopMatcher m(node);
534 if (allow_signalling_nan_ && m.right().Is(1))
535 return Replace(m.left().node()); // x * 1.0 => x
536 if (m.right().Is(-1)) { // x * -1.0 => -0.0 - x
537 node->ReplaceInput(0, Float64Constant(-0.0));
538 node->ReplaceInput(1, m.left().node());
539 NodeProperties::ChangeOp(node, machine()->Float64Sub());
540 return Changed(node);
541 }
542 if (m.right().IsNaN()) { // x * NaN => NaN
543 // Do some calculation to make a signalling NaN quiet.
544 return ReplaceFloat64(m.right().Value() - m.right().Value());
545 }
546 if (m.IsFoldable()) { // K * K => K
547 return ReplaceFloat64(m.left().Value() * m.right().Value());
548 }
549 if (m.right().Is(2)) { // x * 2.0 => x + x
550 node->ReplaceInput(1, m.left().node());
551 NodeProperties::ChangeOp(node, machine()->Float64Add());
552 return Changed(node);
553 }
554 break;
555 }
556 case IrOpcode::kFloat64Div: {
557 Float64BinopMatcher m(node);
558 if (allow_signalling_nan_ && m.right().Is(1))
559 return Replace(m.left().node()); // x / 1.0 => x
560 // TODO(ahaas): We could do x / 1.0 = x if we knew that x is not an sNaN.
561 if (m.right().IsNaN()) { // x / NaN => NaN
562 // Do some calculation to make a signalling NaN quiet.
563 return ReplaceFloat64(m.right().Value() - m.right().Value());
564 }
565 if (m.left().IsNaN()) { // NaN / x => NaN
566 // Do some calculation to make a signalling NaN quiet.
567 return ReplaceFloat64(m.left().Value() - m.left().Value());
568 }
569 if (m.IsFoldable()) { // K / K => K
570 return ReplaceFloat64(
571 base::Divide(m.left().Value(), m.right().Value()));
572 }
573 if (allow_signalling_nan_ && m.right().Is(-1)) { // x / -1.0 => -x
574 node->RemoveInput(1);
575 NodeProperties::ChangeOp(node, machine()->Float64Neg());
576 return Changed(node);
577 }
578 if (m.right().IsNormal() && m.right().IsPositiveOrNegativePowerOf2()) {
579 // All reciprocals of non-denormal powers of two can be represented
580 // exactly, so division by power of two can be reduced to
581 // multiplication by reciprocal, with the same result.
582 node->ReplaceInput(1, Float64Constant(1.0 / m.right().Value()));
583 NodeProperties::ChangeOp(node, machine()->Float64Mul());
584 return Changed(node);
585 }
586 break;
587 }
588 case IrOpcode::kFloat64Mod: {
589 Float64BinopMatcher m(node);
590 if (m.right().Is(0)) { // x % 0 => NaN
591 return ReplaceFloat64(std::numeric_limits<double>::quiet_NaN());
592 }
593 if (m.right().IsNaN()) { // x % NaN => NaN
594 return Replace(m.right().node());
595 }
596 if (m.left().IsNaN()) { // NaN % x => NaN
597 return Replace(m.left().node());
598 }
599 if (m.IsFoldable()) { // K % K => K
600 return ReplaceFloat64(Modulo(m.left().Value(), m.right().Value()));
601 }
602 break;
603 }
604 case IrOpcode::kFloat64Acos: {
605 Float64Matcher m(node->InputAt(0));
606 if (m.HasValue()) return ReplaceFloat64(base::ieee754::acos(m.Value()));
607 break;
608 }
609 case IrOpcode::kFloat64Acosh: {
610 Float64Matcher m(node->InputAt(0));
611 if (m.HasValue()) return ReplaceFloat64(base::ieee754::acosh(m.Value()));
612 break;
613 }
614 case IrOpcode::kFloat64Asin: {
615 Float64Matcher m(node->InputAt(0));
616 if (m.HasValue()) return ReplaceFloat64(base::ieee754::asin(m.Value()));
617 break;
618 }
619 case IrOpcode::kFloat64Asinh: {
620 Float64Matcher m(node->InputAt(0));
621 if (m.HasValue()) return ReplaceFloat64(base::ieee754::asinh(m.Value()));
622 break;
623 }
624 case IrOpcode::kFloat64Atan: {
625 Float64Matcher m(node->InputAt(0));
626 if (m.HasValue()) return ReplaceFloat64(base::ieee754::atan(m.Value()));
627 break;
628 }
629 case IrOpcode::kFloat64Atanh: {
630 Float64Matcher m(node->InputAt(0));
631 if (m.HasValue()) return ReplaceFloat64(base::ieee754::atanh(m.Value()));
632 break;
633 }
634 case IrOpcode::kFloat64Atan2: {
635 Float64BinopMatcher m(node);
636 if (m.right().IsNaN()) {
637 return Replace(m.right().node());
638 }
639 if (m.left().IsNaN()) {
640 return Replace(m.left().node());
641 }
642 if (m.IsFoldable()) {
643 return ReplaceFloat64(
644 base::ieee754::atan2(m.left().Value(), m.right().Value()));
645 }
646 break;
647 }
648 case IrOpcode::kFloat64Cbrt: {
649 Float64Matcher m(node->InputAt(0));
650 if (m.HasValue()) return ReplaceFloat64(base::ieee754::cbrt(m.Value()));
651 break;
652 }
653 case IrOpcode::kFloat64Cos: {
654 Float64Matcher m(node->InputAt(0));
655 if (m.HasValue()) return ReplaceFloat64(base::ieee754::cos(m.Value()));
656 break;
657 }
658 case IrOpcode::kFloat64Cosh: {
659 Float64Matcher m(node->InputAt(0));
660 if (m.HasValue()) return ReplaceFloat64(base::ieee754::cosh(m.Value()));
661 break;
662 }
663 case IrOpcode::kFloat64Exp: {
664 Float64Matcher m(node->InputAt(0));
665 if (m.HasValue()) return ReplaceFloat64(base::ieee754::exp(m.Value()));
666 break;
667 }
668 case IrOpcode::kFloat64Expm1: {
669 Float64Matcher m(node->InputAt(0));
670 if (m.HasValue()) return ReplaceFloat64(base::ieee754::expm1(m.Value()));
671 break;
672 }
673 case IrOpcode::kFloat64Log: {
674 Float64Matcher m(node->InputAt(0));
675 if (m.HasValue()) return ReplaceFloat64(base::ieee754::log(m.Value()));
676 break;
677 }
678 case IrOpcode::kFloat64Log1p: {
679 Float64Matcher m(node->InputAt(0));
680 if (m.HasValue()) return ReplaceFloat64(base::ieee754::log1p(m.Value()));
681 break;
682 }
683 case IrOpcode::kFloat64Log10: {
684 Float64Matcher m(node->InputAt(0));
685 if (m.HasValue()) return ReplaceFloat64(base::ieee754::log10(m.Value()));
686 break;
687 }
688 case IrOpcode::kFloat64Log2: {
689 Float64Matcher m(node->InputAt(0));
690 if (m.HasValue()) return ReplaceFloat64(base::ieee754::log2(m.Value()));
691 break;
692 }
693 case IrOpcode::kFloat64Pow: {
694 Float64BinopMatcher m(node);
695 if (m.IsFoldable()) {
696 return ReplaceFloat64(
697 base::ieee754::pow(m.left().Value(), m.right().Value()));
698 } else if (m.right().Is(0.0)) { // x ** +-0.0 => 1.0
699 return ReplaceFloat64(1.0);
700 } else if (m.right().Is(-2.0)) { // x ** -2.0 => 1 / (x * x)
701 node->ReplaceInput(0, Float64Constant(1.0));
702 node->ReplaceInput(1, Float64Mul(m.left().node(), m.left().node()));
703 NodeProperties::ChangeOp(node, machine()->Float64Div());
704 return Changed(node);
705 } else if (m.right().Is(2.0)) { // x ** 2.0 => x * x
706 node->ReplaceInput(1, m.left().node());
707 NodeProperties::ChangeOp(node, machine()->Float64Mul());
708 return Changed(node);
709 } else if (m.right().Is(-0.5)) {
710 // x ** 0.5 => 1 / (if x <= -Infinity then Infinity else sqrt(0.0 + x))
711 node->ReplaceInput(0, Float64Constant(1.0));
712 node->ReplaceInput(1, Float64PowHalf(m.left().node()));
713 NodeProperties::ChangeOp(node, machine()->Float64Div());
714 return Changed(node);
715 } else if (m.right().Is(0.5)) {
716 // x ** 0.5 => if x <= -Infinity then Infinity else sqrt(0.0 + x)
717 return Replace(Float64PowHalf(m.left().node()));
718 }
719 break;
720 }
721 case IrOpcode::kFloat64Sin: {
722 Float64Matcher m(node->InputAt(0));
723 if (m.HasValue()) return ReplaceFloat64(base::ieee754::sin(m.Value()));
724 break;
725 }
726 case IrOpcode::kFloat64Sinh: {
727 Float64Matcher m(node->InputAt(0));
728 if (m.HasValue()) return ReplaceFloat64(base::ieee754::sinh(m.Value()));
729 break;
730 }
731 case IrOpcode::kFloat64Tan: {
732 Float64Matcher m(node->InputAt(0));
733 if (m.HasValue()) return ReplaceFloat64(base::ieee754::tan(m.Value()));
734 break;
735 }
736 case IrOpcode::kFloat64Tanh: {
737 Float64Matcher m(node->InputAt(0));
738 if (m.HasValue()) return ReplaceFloat64(base::ieee754::tanh(m.Value()));
739 break;
740 }
741 case IrOpcode::kChangeFloat32ToFloat64: {
742 Float32Matcher m(node->InputAt(0));
743 if (m.HasValue()) {
744 if (!allow_signalling_nan_ && std::isnan(m.Value())) {
745 // Do some calculation to make guarantee the value is a quiet NaN.
746 return ReplaceFloat64(m.Value() + m.Value());
747 }
748 return ReplaceFloat64(m.Value());
749 }
750 break;
751 }
752 case IrOpcode::kChangeFloat64ToInt32: {
753 Float64Matcher m(node->InputAt(0));
754 if (m.HasValue()) return ReplaceInt32(FastD2IChecked(m.Value()));
755 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
756 break;
757 }
758 case IrOpcode::kChangeFloat64ToInt64: {
759 Float64Matcher m(node->InputAt(0));
760 if (m.HasValue()) return ReplaceInt64(static_cast<int64_t>(m.Value()));
761 if (m.IsChangeInt64ToFloat64()) return Replace(m.node()->InputAt(0));
762 break;
763 }
764 case IrOpcode::kChangeFloat64ToUint32: {
765 Float64Matcher m(node->InputAt(0));
766 if (m.HasValue()) return ReplaceInt32(FastD2UI(m.Value()));
767 if (m.IsChangeUint32ToFloat64()) return Replace(m.node()->InputAt(0));
768 break;
769 }
770 case IrOpcode::kChangeInt32ToFloat64: {
771 Int32Matcher m(node->InputAt(0));
772 if (m.HasValue()) return ReplaceFloat64(FastI2D(m.Value()));
773 break;
774 }
775 case IrOpcode::kBitcastWord32ToWord64: {
776 Int32Matcher m(node->InputAt(0));
777 if (m.HasValue()) return ReplaceInt64(m.Value());
778 break;
779 }
780 case IrOpcode::kChangeInt32ToInt64: {
781 Int32Matcher m(node->InputAt(0));
782 if (m.HasValue()) return ReplaceInt64(m.Value());
783 break;
784 }
785 case IrOpcode::kChangeInt64ToFloat64: {
786 Int64Matcher m(node->InputAt(0));
787 if (m.HasValue()) return ReplaceFloat64(static_cast<double>(m.Value()));
788 if (m.IsChangeFloat64ToInt64()) return Replace(m.node()->InputAt(0));
789 break;
790 }
791 case IrOpcode::kChangeUint32ToFloat64: {
792 Uint32Matcher m(node->InputAt(0));
793 if (m.HasValue()) return ReplaceFloat64(FastUI2D(m.Value()));
794 break;
795 }
796 case IrOpcode::kChangeUint32ToUint64: {
797 Uint32Matcher m(node->InputAt(0));
798 if (m.HasValue()) return ReplaceInt64(static_cast<uint64_t>(m.Value()));
799 break;
800 }
801 case IrOpcode::kTruncateFloat64ToWord32: {
802 Float64Matcher m(node->InputAt(0));
803 if (m.HasValue()) return ReplaceInt32(DoubleToInt32(m.Value()));
804 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
805 return NoChange();
806 }
807 case IrOpcode::kTruncateInt64ToInt32: {
808 Int64Matcher m(node->InputAt(0));
809 if (m.HasValue()) return ReplaceInt32(static_cast<int32_t>(m.Value()));
810 if (m.IsChangeInt32ToInt64()) return Replace(m.node()->InputAt(0));
811 break;
812 }
813 case IrOpcode::kTruncateFloat64ToFloat32: {
814 Float64Matcher m(node->InputAt(0));
815 if (m.HasValue()) {
816 if (!allow_signalling_nan_ && std::isnan(m.Value())) {
817 // Do some calculation to make guarantee the value is a quiet NaN.
818 return ReplaceFloat32(DoubleToFloat32(m.Value() + m.Value()));
819 }
820 return ReplaceFloat32(DoubleToFloat32(m.Value()));
821 }
822 if (allow_signalling_nan_ && m.IsChangeFloat32ToFloat64())
823 return Replace(m.node()->InputAt(0));
824 break;
825 }
826 case IrOpcode::kRoundFloat64ToInt32: {
827 Float64Matcher m(node->InputAt(0));
828 if (m.HasValue()) {
829 return ReplaceInt32(DoubleToInt32(m.Value()));
830 }
831 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
832 break;
833 }
834 case IrOpcode::kFloat64InsertLowWord32:
835 return ReduceFloat64InsertLowWord32(node);
836 case IrOpcode::kFloat64InsertHighWord32:
837 return ReduceFloat64InsertHighWord32(node);
838 case IrOpcode::kStore:
839 case IrOpcode::kUnalignedStore:
840 return ReduceStore(node);
841 case IrOpcode::kFloat64Equal:
842 case IrOpcode::kFloat64LessThan:
843 case IrOpcode::kFloat64LessThanOrEqual:
844 return ReduceFloat64Compare(node);
845 case IrOpcode::kFloat64RoundDown:
846 return ReduceFloat64RoundDown(node);
847 case IrOpcode::kBitcastTaggedToWord:
848 case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits: {
849 NodeMatcher m(node->InputAt(0));
850 if (m.IsBitcastWordToTaggedSigned()) {
851 RelaxEffectsAndControls(node);
852 return Replace(m.InputAt(0));
853 }
854 break;
855 }
856 case IrOpcode::kBranch:
857 case IrOpcode::kDeoptimizeIf:
858 case IrOpcode::kDeoptimizeUnless:
859 case IrOpcode::kTrapIf:
860 case IrOpcode::kTrapUnless:
861 return ReduceConditional(node);
862 default:
863 break;
864 }
865 return NoChange();
866 }
867
ReduceInt32Add(Node * node)868 Reduction MachineOperatorReducer::ReduceInt32Add(Node* node) {
869 DCHECK_EQ(IrOpcode::kInt32Add, node->opcode());
870 Int32BinopMatcher m(node);
871 if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => x
872 if (m.IsFoldable()) { // K + K => K
873 return ReplaceInt32(
874 base::AddWithWraparound(m.left().Value(), m.right().Value()));
875 }
876 if (m.left().IsInt32Sub()) {
877 Int32BinopMatcher mleft(m.left().node());
878 if (mleft.left().Is(0)) { // (0 - x) + y => y - x
879 node->ReplaceInput(0, m.right().node());
880 node->ReplaceInput(1, mleft.right().node());
881 NodeProperties::ChangeOp(node, machine()->Int32Sub());
882 Reduction const reduction = ReduceInt32Sub(node);
883 return reduction.Changed() ? reduction : Changed(node);
884 }
885 }
886 if (m.right().IsInt32Sub()) {
887 Int32BinopMatcher mright(m.right().node());
888 if (mright.left().Is(0)) { // y + (0 - x) => y - x
889 node->ReplaceInput(1, mright.right().node());
890 NodeProperties::ChangeOp(node, machine()->Int32Sub());
891 Reduction const reduction = ReduceInt32Sub(node);
892 return reduction.Changed() ? reduction : Changed(node);
893 }
894 }
895 // (x + Int32Constant(a)) + Int32Constant(b)) => x + Int32Constant(a + b)
896 if (m.right().HasValue() && m.left().IsInt32Add()) {
897 Int32BinopMatcher n(m.left().node());
898 if (n.right().HasValue() && m.OwnsInput(m.left().node())) {
899 node->ReplaceInput(1, Int32Constant(base::AddWithWraparound(
900 m.right().Value(), n.right().Value())));
901 node->ReplaceInput(0, n.left().node());
902 return Changed(node);
903 }
904 }
905
906 return NoChange();
907 }
908
ReduceInt64Add(Node * node)909 Reduction MachineOperatorReducer::ReduceInt64Add(Node* node) {
910 DCHECK_EQ(IrOpcode::kInt64Add, node->opcode());
911 Int64BinopMatcher m(node);
912 if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => 0
913 if (m.IsFoldable()) {
914 return ReplaceInt64(
915 base::AddWithWraparound(m.left().Value(), m.right().Value()));
916 }
917 // (x + Int64Constant(a)) + Int64Constant(b)) => x + Int64Constant(a + b)
918 if (m.right().HasValue() && m.left().IsInt64Add()) {
919 Int64BinopMatcher n(m.left().node());
920 if (n.right().HasValue() && m.OwnsInput(m.left().node())) {
921 node->ReplaceInput(1, Int64Constant(base::AddWithWraparound(
922 m.right().Value(), n.right().Value())));
923 node->ReplaceInput(0, n.left().node());
924 return Changed(node);
925 }
926 }
927 return NoChange();
928 }
929
ReduceInt32Sub(Node * node)930 Reduction MachineOperatorReducer::ReduceInt32Sub(Node* node) {
931 DCHECK_EQ(IrOpcode::kInt32Sub, node->opcode());
932 Int32BinopMatcher m(node);
933 if (m.right().Is(0)) return Replace(m.left().node()); // x - 0 => x
934 if (m.IsFoldable()) { // K - K => K
935 return ReplaceInt32(
936 base::SubWithWraparound(m.left().Value(), m.right().Value()));
937 }
938 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x - x => 0
939 if (m.right().HasValue()) { // x - K => x + -K
940 node->ReplaceInput(
941 1, Int32Constant(base::NegateWithWraparound(m.right().Value())));
942 NodeProperties::ChangeOp(node, machine()->Int32Add());
943 Reduction const reduction = ReduceInt32Add(node);
944 return reduction.Changed() ? reduction : Changed(node);
945 }
946 return NoChange();
947 }
948
ReduceInt64Sub(Node * node)949 Reduction MachineOperatorReducer::ReduceInt64Sub(Node* node) {
950 DCHECK_EQ(IrOpcode::kInt64Sub, node->opcode());
951 Int64BinopMatcher m(node);
952 if (m.right().Is(0)) return Replace(m.left().node()); // x - 0 => x
953 if (m.IsFoldable()) { // K - K => K
954 return ReplaceInt64(
955 base::SubWithWraparound(m.left().Value(), m.right().Value()));
956 }
957 if (m.LeftEqualsRight()) return Replace(Int64Constant(0)); // x - x => 0
958 if (m.right().HasValue()) { // x - K => x + -K
959 node->ReplaceInput(
960 1, Int64Constant(base::NegateWithWraparound(m.right().Value())));
961 NodeProperties::ChangeOp(node, machine()->Int64Add());
962 Reduction const reduction = ReduceInt64Add(node);
963 return reduction.Changed() ? reduction : Changed(node);
964 }
965 return NoChange();
966 }
967
ReduceInt64Mul(Node * node)968 Reduction MachineOperatorReducer::ReduceInt64Mul(Node* node) {
969 DCHECK_EQ(IrOpcode::kInt64Mul, node->opcode());
970 Int64BinopMatcher m(node);
971 if (m.right().Is(0)) return Replace(m.right().node()); // x * 0 => 0
972 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1 => x
973 if (m.IsFoldable()) { // K * K => K
974 return ReplaceInt64(
975 base::MulWithWraparound(m.left().Value(), m.right().Value()));
976 }
977 if (m.right().Is(-1)) { // x * -1 => 0 - x
978 node->ReplaceInput(0, Int64Constant(0));
979 node->ReplaceInput(1, m.left().node());
980 NodeProperties::ChangeOp(node, machine()->Int64Sub());
981 return Changed(node);
982 }
983 if (m.right().IsPowerOf2()) { // x * 2^n => x << n
984 node->ReplaceInput(
985 1, Int64Constant(base::bits::WhichPowerOfTwo(m.right().Value())));
986 NodeProperties::ChangeOp(node, machine()->Word64Shl());
987 Reduction reduction = ReduceWord64Shl(node);
988 return reduction.Changed() ? reduction : Changed(node);
989 }
990 return NoChange();
991 }
992
ReduceInt32Div(Node * node)993 Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
994 Int32BinopMatcher m(node);
995 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
996 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
997 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
998 if (m.IsFoldable()) { // K / K => K
999 return ReplaceInt32(
1000 base::bits::SignedDiv32(m.left().Value(), m.right().Value()));
1001 }
1002 if (m.LeftEqualsRight()) { // x / x => x != 0
1003 Node* const zero = Int32Constant(0);
1004 return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1005 }
1006 if (m.right().Is(-1)) { // x / -1 => 0 - x
1007 node->ReplaceInput(0, Int32Constant(0));
1008 node->ReplaceInput(1, m.left().node());
1009 node->TrimInputCount(2);
1010 NodeProperties::ChangeOp(node, machine()->Int32Sub());
1011 return Changed(node);
1012 }
1013 if (m.right().HasValue()) {
1014 int32_t const divisor = m.right().Value();
1015 Node* const dividend = m.left().node();
1016 Node* quotient = dividend;
1017 if (base::bits::IsPowerOfTwo(Abs(divisor))) {
1018 uint32_t const shift = base::bits::WhichPowerOfTwo(Abs(divisor));
1019 DCHECK_NE(0u, shift);
1020 if (shift > 1) {
1021 quotient = Word32Sar(quotient, 31);
1022 }
1023 quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend);
1024 quotient = Word32Sar(quotient, shift);
1025 } else {
1026 quotient = Int32Div(quotient, Abs(divisor));
1027 }
1028 if (divisor < 0) {
1029 node->ReplaceInput(0, Int32Constant(0));
1030 node->ReplaceInput(1, quotient);
1031 node->TrimInputCount(2);
1032 NodeProperties::ChangeOp(node, machine()->Int32Sub());
1033 return Changed(node);
1034 }
1035 return Replace(quotient);
1036 }
1037 return NoChange();
1038 }
1039
ReduceUint32Div(Node * node)1040 Reduction MachineOperatorReducer::ReduceUint32Div(Node* node) {
1041 Uint32BinopMatcher m(node);
1042 if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0
1043 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
1044 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
1045 if (m.IsFoldable()) { // K / K => K
1046 return ReplaceUint32(
1047 base::bits::UnsignedDiv32(m.left().Value(), m.right().Value()));
1048 }
1049 if (m.LeftEqualsRight()) { // x / x => x != 0
1050 Node* const zero = Int32Constant(0);
1051 return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero));
1052 }
1053 if (m.right().HasValue()) {
1054 Node* const dividend = m.left().node();
1055 uint32_t const divisor = m.right().Value();
1056 if (base::bits::IsPowerOfTwo(divisor)) { // x / 2^n => x >> n
1057 node->ReplaceInput(
1058 1, Uint32Constant(base::bits::WhichPowerOfTwo(m.right().Value())));
1059 node->TrimInputCount(2);
1060 NodeProperties::ChangeOp(node, machine()->Word32Shr());
1061 return Changed(node);
1062 } else {
1063 return Replace(Uint32Div(dividend, divisor));
1064 }
1065 }
1066 return NoChange();
1067 }
1068
ReduceInt32Mod(Node * node)1069 Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) {
1070 Int32BinopMatcher m(node);
1071 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1072 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1073 if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
1074 if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
1075 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0
1076 if (m.IsFoldable()) { // K % K => K
1077 return ReplaceInt32(
1078 base::bits::SignedMod32(m.left().Value(), m.right().Value()));
1079 }
1080 if (m.right().HasValue()) {
1081 Node* const dividend = m.left().node();
1082 uint32_t const divisor = Abs(m.right().Value());
1083 if (base::bits::IsPowerOfTwo(divisor)) {
1084 uint32_t const mask = divisor - 1;
1085 Node* const zero = Int32Constant(0);
1086 Diamond d(graph(), common(),
1087 graph()->NewNode(machine()->Int32LessThan(), dividend, zero),
1088 BranchHint::kFalse);
1089 return Replace(
1090 d.Phi(MachineRepresentation::kWord32,
1091 Int32Sub(zero, Word32And(Int32Sub(zero, dividend), mask)),
1092 Word32And(dividend, mask)));
1093 } else {
1094 Node* quotient = Int32Div(dividend, divisor);
1095 DCHECK_EQ(dividend, node->InputAt(0));
1096 node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor)));
1097 node->TrimInputCount(2);
1098 NodeProperties::ChangeOp(node, machine()->Int32Sub());
1099 }
1100 return Changed(node);
1101 }
1102 return NoChange();
1103 }
1104
ReduceUint32Mod(Node * node)1105 Reduction MachineOperatorReducer::ReduceUint32Mod(Node* node) {
1106 Uint32BinopMatcher m(node);
1107 if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0
1108 if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0
1109 if (m.right().Is(1)) return ReplaceUint32(0); // x % 1 => 0
1110 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0
1111 if (m.IsFoldable()) { // K % K => K
1112 return ReplaceUint32(
1113 base::bits::UnsignedMod32(m.left().Value(), m.right().Value()));
1114 }
1115 if (m.right().HasValue()) {
1116 Node* const dividend = m.left().node();
1117 uint32_t const divisor = m.right().Value();
1118 if (base::bits::IsPowerOfTwo(divisor)) { // x % 2^n => x & 2^n-1
1119 node->ReplaceInput(1, Uint32Constant(m.right().Value() - 1));
1120 node->TrimInputCount(2);
1121 NodeProperties::ChangeOp(node, machine()->Word32And());
1122 } else {
1123 Node* quotient = Uint32Div(dividend, divisor);
1124 DCHECK_EQ(dividend, node->InputAt(0));
1125 node->ReplaceInput(1, Int32Mul(quotient, Uint32Constant(divisor)));
1126 node->TrimInputCount(2);
1127 NodeProperties::ChangeOp(node, machine()->Int32Sub());
1128 }
1129 return Changed(node);
1130 }
1131 return NoChange();
1132 }
1133
ReduceStore(Node * node)1134 Reduction MachineOperatorReducer::ReduceStore(Node* node) {
1135 NodeMatcher nm(node);
1136 MachineRepresentation rep;
1137 int value_input;
1138 if (nm.IsStore()) {
1139 rep = StoreRepresentationOf(node->op()).representation();
1140 value_input = 2;
1141 } else {
1142 DCHECK(nm.IsUnalignedStore());
1143 rep = UnalignedStoreRepresentationOf(node->op());
1144 value_input = 2;
1145 }
1146
1147 Node* const value = node->InputAt(value_input);
1148
1149 switch (value->opcode()) {
1150 case IrOpcode::kWord32And: {
1151 Uint32BinopMatcher m(value);
1152 if (m.right().HasValue() && ((rep == MachineRepresentation::kWord8 &&
1153 (m.right().Value() & 0xFF) == 0xFF) ||
1154 (rep == MachineRepresentation::kWord16 &&
1155 (m.right().Value() & 0xFFFF) == 0xFFFF))) {
1156 node->ReplaceInput(value_input, m.left().node());
1157 return Changed(node);
1158 }
1159 break;
1160 }
1161 case IrOpcode::kWord32Sar: {
1162 Int32BinopMatcher m(value);
1163 if (m.left().IsWord32Shl() && ((rep == MachineRepresentation::kWord8 &&
1164 m.right().IsInRange(1, 24)) ||
1165 (rep == MachineRepresentation::kWord16 &&
1166 m.right().IsInRange(1, 16)))) {
1167 Int32BinopMatcher mleft(m.left().node());
1168 if (mleft.right().Is(m.right().Value())) {
1169 node->ReplaceInput(value_input, mleft.left().node());
1170 return Changed(node);
1171 }
1172 }
1173 break;
1174 }
1175 default:
1176 break;
1177 }
1178 return NoChange();
1179 }
1180
ReduceProjection(size_t index,Node * node)1181 Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) {
1182 switch (node->opcode()) {
1183 case IrOpcode::kInt32AddWithOverflow: {
1184 DCHECK(index == 0 || index == 1);
1185 Int32BinopMatcher m(node);
1186 if (m.IsFoldable()) {
1187 int32_t val;
1188 bool ovf = base::bits::SignedAddOverflow32(m.left().Value(),
1189 m.right().Value(), &val);
1190 return ReplaceInt32(index == 0 ? val : ovf);
1191 }
1192 if (m.right().Is(0)) {
1193 return Replace(index == 0 ? m.left().node() : m.right().node());
1194 }
1195 break;
1196 }
1197 case IrOpcode::kInt32SubWithOverflow: {
1198 DCHECK(index == 0 || index == 1);
1199 Int32BinopMatcher m(node);
1200 if (m.IsFoldable()) {
1201 int32_t val;
1202 bool ovf = base::bits::SignedSubOverflow32(m.left().Value(),
1203 m.right().Value(), &val);
1204 return ReplaceInt32(index == 0 ? val : ovf);
1205 }
1206 if (m.right().Is(0)) {
1207 return Replace(index == 0 ? m.left().node() : m.right().node());
1208 }
1209 break;
1210 }
1211 case IrOpcode::kInt32MulWithOverflow: {
1212 DCHECK(index == 0 || index == 1);
1213 Int32BinopMatcher m(node);
1214 if (m.IsFoldable()) {
1215 int32_t val;
1216 bool ovf = base::bits::SignedMulOverflow32(m.left().Value(),
1217 m.right().Value(), &val);
1218 return ReplaceInt32(index == 0 ? val : ovf);
1219 }
1220 if (m.right().Is(0)) {
1221 return Replace(m.right().node());
1222 }
1223 if (m.right().Is(1)) {
1224 return index == 0 ? Replace(m.left().node()) : ReplaceInt32(0);
1225 }
1226 break;
1227 }
1228 default:
1229 break;
1230 }
1231 return NoChange();
1232 }
1233
ReduceWord32Shifts(Node * node)1234 Reduction MachineOperatorReducer::ReduceWord32Shifts(Node* node) {
1235 DCHECK((node->opcode() == IrOpcode::kWord32Shl) ||
1236 (node->opcode() == IrOpcode::kWord32Shr) ||
1237 (node->opcode() == IrOpcode::kWord32Sar));
1238 if (machine()->Word32ShiftIsSafe()) {
1239 // Remove the explicit 'and' with 0x1F if the shift provided by the machine
1240 // instruction matches that required by JavaScript.
1241 Int32BinopMatcher m(node);
1242 if (m.right().IsWord32And()) {
1243 Int32BinopMatcher mright(m.right().node());
1244 if (mright.right().Is(0x1F)) {
1245 node->ReplaceInput(1, mright.left().node());
1246 return Changed(node);
1247 }
1248 }
1249 }
1250 return NoChange();
1251 }
1252
ReduceWord32Shl(Node * node)1253 Reduction MachineOperatorReducer::ReduceWord32Shl(Node* node) {
1254 DCHECK_EQ(IrOpcode::kWord32Shl, node->opcode());
1255 Int32BinopMatcher m(node);
1256 if (m.right().Is(0)) return Replace(m.left().node()); // x << 0 => x
1257 if (m.IsFoldable()) { // K << K => K
1258 return ReplaceInt32(
1259 base::ShlWithWraparound(m.left().Value(), m.right().Value()));
1260 }
1261 if (m.right().IsInRange(1, 31)) {
1262 // (x >>> K) << K => x & ~(2^K - 1)
1263 // (x >> K) << K => x & ~(2^K - 1)
1264 if (m.left().IsWord32Sar() || m.left().IsWord32Shr()) {
1265 Int32BinopMatcher mleft(m.left().node());
1266 if (mleft.right().Is(m.right().Value())) {
1267 node->ReplaceInput(0, mleft.left().node());
1268 node->ReplaceInput(1,
1269 Uint32Constant(~((1U << m.right().Value()) - 1U)));
1270 NodeProperties::ChangeOp(node, machine()->Word32And());
1271 Reduction reduction = ReduceWord32And(node);
1272 return reduction.Changed() ? reduction : Changed(node);
1273 }
1274 }
1275 }
1276 return ReduceWord32Shifts(node);
1277 }
1278
ReduceWord64Shl(Node * node)1279 Reduction MachineOperatorReducer::ReduceWord64Shl(Node* node) {
1280 DCHECK_EQ(IrOpcode::kWord64Shl, node->opcode());
1281 Int64BinopMatcher m(node);
1282 if (m.right().Is(0)) return Replace(m.left().node()); // x << 0 => x
1283 if (m.IsFoldable()) { // K << K => K
1284 return ReplaceInt64(
1285 base::ShlWithWraparound(m.left().Value(), m.right().Value()));
1286 }
1287 return NoChange();
1288 }
1289
ReduceWord32Shr(Node * node)1290 Reduction MachineOperatorReducer::ReduceWord32Shr(Node* node) {
1291 Uint32BinopMatcher m(node);
1292 if (m.right().Is(0)) return Replace(m.left().node()); // x >>> 0 => x
1293 if (m.IsFoldable()) { // K >>> K => K
1294 return ReplaceInt32(m.left().Value() >> (m.right().Value() & 31));
1295 }
1296 if (m.left().IsWord32And() && m.right().HasValue()) {
1297 Uint32BinopMatcher mleft(m.left().node());
1298 if (mleft.right().HasValue()) {
1299 uint32_t shift = m.right().Value() & 31;
1300 uint32_t mask = mleft.right().Value();
1301 if ((mask >> shift) == 0) {
1302 // (m >>> s) == 0 implies ((x & m) >>> s) == 0
1303 return ReplaceInt32(0);
1304 }
1305 }
1306 }
1307 return ReduceWord32Shifts(node);
1308 }
1309
ReduceWord64Shr(Node * node)1310 Reduction MachineOperatorReducer::ReduceWord64Shr(Node* node) {
1311 DCHECK_EQ(IrOpcode::kWord64Shr, node->opcode());
1312 Uint64BinopMatcher m(node);
1313 if (m.right().Is(0)) return Replace(m.left().node()); // x >>> 0 => x
1314 if (m.IsFoldable()) { // K >> K => K
1315 return ReplaceInt64(m.left().Value() >> (m.right().Value() & 63));
1316 }
1317 return NoChange();
1318 }
1319
ReduceWord32Sar(Node * node)1320 Reduction MachineOperatorReducer::ReduceWord32Sar(Node* node) {
1321 Int32BinopMatcher m(node);
1322 if (m.right().Is(0)) return Replace(m.left().node()); // x >> 0 => x
1323 if (m.IsFoldable()) { // K >> K => K
1324 return ReplaceInt32(m.left().Value() >> (m.right().Value() & 31));
1325 }
1326 if (m.left().IsWord32Shl()) {
1327 Int32BinopMatcher mleft(m.left().node());
1328 if (mleft.left().IsComparison()) {
1329 if (m.right().Is(31) && mleft.right().Is(31)) {
1330 // Comparison << 31 >> 31 => 0 - Comparison
1331 node->ReplaceInput(0, Int32Constant(0));
1332 node->ReplaceInput(1, mleft.left().node());
1333 NodeProperties::ChangeOp(node, machine()->Int32Sub());
1334 Reduction const reduction = ReduceInt32Sub(node);
1335 return reduction.Changed() ? reduction : Changed(node);
1336 }
1337 } else if (mleft.left().IsLoad()) {
1338 LoadRepresentation const rep =
1339 LoadRepresentationOf(mleft.left().node()->op());
1340 if (m.right().Is(24) && mleft.right().Is(24) &&
1341 rep == MachineType::Int8()) {
1342 // Load[kMachInt8] << 24 >> 24 => Load[kMachInt8]
1343 return Replace(mleft.left().node());
1344 }
1345 if (m.right().Is(16) && mleft.right().Is(16) &&
1346 rep == MachineType::Int16()) {
1347 // Load[kMachInt16] << 16 >> 16 => Load[kMachInt8]
1348 return Replace(mleft.left().node());
1349 }
1350 }
1351 }
1352 return ReduceWord32Shifts(node);
1353 }
1354
ReduceWord64Sar(Node * node)1355 Reduction MachineOperatorReducer::ReduceWord64Sar(Node* node) {
1356 Int64BinopMatcher m(node);
1357 if (m.right().Is(0)) return Replace(m.left().node()); // x >> 0 => x
1358 if (m.IsFoldable()) {
1359 return ReplaceInt64(m.left().Value() >> (m.right().Value() & 63));
1360 }
1361 return NoChange();
1362 }
1363
1364 template <typename WordNAdapter>
ReduceWordNAnd(Node * node)1365 Reduction MachineOperatorReducer::ReduceWordNAnd(Node* node) {
1366 using A = WordNAdapter;
1367 A a(this);
1368
1369 typename A::IntNBinopMatcher m(node);
1370 if (m.right().Is(0)) return Replace(m.right().node()); // x & 0 => 0
1371 if (m.right().Is(-1)) return Replace(m.left().node()); // x & -1 => x
1372 if (m.left().IsComparison() && m.right().Is(1)) { // CMP & 1 => CMP
1373 return Replace(m.left().node());
1374 }
1375 if (m.IsFoldable()) { // K & K => K
1376 return a.ReplaceIntN(m.left().Value() & m.right().Value());
1377 }
1378 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x & x => x
1379 if (A::IsWordNAnd(m.left()) && m.right().HasValue()) {
1380 typename A::IntNBinopMatcher mleft(m.left().node());
1381 if (mleft.right().HasValue()) { // (x & K) & K => x & K
1382 node->ReplaceInput(0, mleft.left().node());
1383 node->ReplaceInput(
1384 1, a.IntNConstant(m.right().Value() & mleft.right().Value()));
1385 Reduction const reduction = a.ReduceWordNAnd(node);
1386 return reduction.Changed() ? reduction : Changed(node);
1387 }
1388 }
1389 if (m.right().IsNegativePowerOf2()) {
1390 typename A::intN_t const mask = m.right().Value();
1391 typename A::intN_t const neg_mask = base::NegateWithWraparound(mask);
1392 if (A::IsWordNShl(m.left())) {
1393 typename A::UintNBinopMatcher mleft(m.left().node());
1394 if (mleft.right().HasValue() &&
1395 (mleft.right().Value() & (A::WORD_SIZE - 1)) >=
1396 base::bits::CountTrailingZeros(mask)) {
1397 // (x << L) & (-1 << K) => x << L iff L >= K
1398 return Replace(mleft.node());
1399 }
1400 } else if (A::IsIntNAdd(m.left())) {
1401 typename A::IntNBinopMatcher mleft(m.left().node());
1402 if (mleft.right().HasValue() &&
1403 (mleft.right().Value() & mask) == mleft.right().Value()) {
1404 // (x + (K << L)) & (-1 << L) => (x & (-1 << L)) + (K << L)
1405 node->ReplaceInput(0,
1406 a.WordNAnd(mleft.left().node(), m.right().node()));
1407 node->ReplaceInput(1, mleft.right().node());
1408 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1409 Reduction const reduction = a.ReduceIntNAdd(node);
1410 return reduction.Changed() ? reduction : Changed(node);
1411 }
1412 if (A::IsIntNMul(mleft.left())) {
1413 typename A::IntNBinopMatcher mleftleft(mleft.left().node());
1414 if (mleftleft.right().IsMultipleOf(neg_mask)) {
1415 // (y * (K << L) + x) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
1416 node->ReplaceInput(
1417 0, a.WordNAnd(mleft.right().node(), m.right().node()));
1418 node->ReplaceInput(1, mleftleft.node());
1419 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1420 Reduction const reduction = a.ReduceIntNAdd(node);
1421 return reduction.Changed() ? reduction : Changed(node);
1422 }
1423 }
1424 if (A::IsIntNMul(mleft.right())) {
1425 typename A::IntNBinopMatcher mleftright(mleft.right().node());
1426 if (mleftright.right().IsMultipleOf(neg_mask)) {
1427 // (x + y * (K << L)) & (-1 << L) => (x & (-1 << L)) + y * (K << L)
1428 node->ReplaceInput(0,
1429 a.WordNAnd(mleft.left().node(), m.right().node()));
1430 node->ReplaceInput(1, mleftright.node());
1431 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1432 Reduction const reduction = a.ReduceIntNAdd(node);
1433 return reduction.Changed() ? reduction : Changed(node);
1434 }
1435 }
1436 if (A::IsWordNShl(mleft.left())) {
1437 typename A::IntNBinopMatcher mleftleft(mleft.left().node());
1438 if (mleftleft.right().Is(base::bits::CountTrailingZeros(mask))) {
1439 // (y << L + x) & (-1 << L) => (x & (-1 << L)) + y << L
1440 node->ReplaceInput(
1441 0, a.WordNAnd(mleft.right().node(), m.right().node()));
1442 node->ReplaceInput(1, mleftleft.node());
1443 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1444 Reduction const reduction = a.ReduceIntNAdd(node);
1445 return reduction.Changed() ? reduction : Changed(node);
1446 }
1447 }
1448 if (A::IsWordNShl(mleft.right())) {
1449 typename A::IntNBinopMatcher mleftright(mleft.right().node());
1450 if (mleftright.right().Is(base::bits::CountTrailingZeros(mask))) {
1451 // (x + y << L) & (-1 << L) => (x & (-1 << L)) + y << L
1452 node->ReplaceInput(0,
1453 a.WordNAnd(mleft.left().node(), m.right().node()));
1454 node->ReplaceInput(1, mleftright.node());
1455 NodeProperties::ChangeOp(node, a.IntNAdd(machine()));
1456 Reduction const reduction = a.ReduceIntNAdd(node);
1457 return reduction.Changed() ? reduction : Changed(node);
1458 }
1459 }
1460 } else if (A::IsIntNMul(m.left())) {
1461 typename A::IntNBinopMatcher mleft(m.left().node());
1462 if (mleft.right().IsMultipleOf(neg_mask)) {
1463 // (x * (K << L)) & (-1 << L) => x * (K << L)
1464 return Replace(mleft.node());
1465 }
1466 }
1467 }
1468 return NoChange();
1469 }
1470
ReduceWord32And(Node * node)1471 Reduction MachineOperatorReducer::ReduceWord32And(Node* node) {
1472 DCHECK_EQ(IrOpcode::kWord32And, node->opcode());
1473 return ReduceWordNAnd<Word32Adapter>(node);
1474 }
1475
ReduceWord64And(Node * node)1476 Reduction MachineOperatorReducer::ReduceWord64And(Node* node) {
1477 DCHECK_EQ(IrOpcode::kWord64And, node->opcode());
1478 return ReduceWordNAnd<Word64Adapter>(node);
1479 }
1480
TryMatchWord32Ror(Node * node)1481 Reduction MachineOperatorReducer::TryMatchWord32Ror(Node* node) {
1482 DCHECK(IrOpcode::kWord32Or == node->opcode() ||
1483 IrOpcode::kWord32Xor == node->opcode());
1484 Int32BinopMatcher m(node);
1485 Node* shl = nullptr;
1486 Node* shr = nullptr;
1487 // Recognize rotation, we are matching:
1488 // * x << y | x >>> (32 - y) => x ror (32 - y), i.e x rol y
1489 // * x << (32 - y) | x >>> y => x ror y
1490 // * x << y ^ x >>> (32 - y) => x ror (32 - y), i.e. x rol y
1491 // * x << (32 - y) ^ x >>> y => x ror y
1492 // as well as their commuted form.
1493 if (m.left().IsWord32Shl() && m.right().IsWord32Shr()) {
1494 shl = m.left().node();
1495 shr = m.right().node();
1496 } else if (m.left().IsWord32Shr() && m.right().IsWord32Shl()) {
1497 shl = m.right().node();
1498 shr = m.left().node();
1499 } else {
1500 return NoChange();
1501 }
1502
1503 Int32BinopMatcher mshl(shl);
1504 Int32BinopMatcher mshr(shr);
1505 if (mshl.left().node() != mshr.left().node()) return NoChange();
1506
1507 if (mshl.right().HasValue() && mshr.right().HasValue()) {
1508 // Case where y is a constant.
1509 if (mshl.right().Value() + mshr.right().Value() != 32) return NoChange();
1510 } else {
1511 Node* sub = nullptr;
1512 Node* y = nullptr;
1513 if (mshl.right().IsInt32Sub()) {
1514 sub = mshl.right().node();
1515 y = mshr.right().node();
1516 } else if (mshr.right().IsInt32Sub()) {
1517 sub = mshr.right().node();
1518 y = mshl.right().node();
1519 } else {
1520 return NoChange();
1521 }
1522
1523 Int32BinopMatcher msub(sub);
1524 if (!msub.left().Is(32) || msub.right().node() != y) return NoChange();
1525 }
1526
1527 node->ReplaceInput(0, mshl.left().node());
1528 node->ReplaceInput(1, mshr.right().node());
1529 NodeProperties::ChangeOp(node, machine()->Word32Ror());
1530 return Changed(node);
1531 }
1532
1533 template <typename WordNAdapter>
ReduceWordNOr(Node * node)1534 Reduction MachineOperatorReducer::ReduceWordNOr(Node* node) {
1535 using A = WordNAdapter;
1536 A a(this);
1537
1538 typename A::IntNBinopMatcher m(node);
1539 if (m.right().Is(0)) return Replace(m.left().node()); // x | 0 => x
1540 if (m.right().Is(-1)) return Replace(m.right().node()); // x | -1 => -1
1541 if (m.IsFoldable()) { // K | K => K
1542 return a.ReplaceIntN(m.left().Value() | m.right().Value());
1543 }
1544 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x | x => x
1545
1546 return a.TryMatchWordNRor(node);
1547 }
1548
ReduceWord32Or(Node * node)1549 Reduction MachineOperatorReducer::ReduceWord32Or(Node* node) {
1550 DCHECK_EQ(IrOpcode::kWord32Or, node->opcode());
1551 return ReduceWordNOr<Word32Adapter>(node);
1552 }
1553
ReduceWord64Or(Node * node)1554 Reduction MachineOperatorReducer::ReduceWord64Or(Node* node) {
1555 DCHECK_EQ(IrOpcode::kWord64Or, node->opcode());
1556 return ReduceWordNOr<Word64Adapter>(node);
1557 }
1558
1559 template <typename WordNAdapter>
ReduceWordNXor(Node * node)1560 Reduction MachineOperatorReducer::ReduceWordNXor(Node* node) {
1561 using A = WordNAdapter;
1562 A a(this);
1563
1564 typename A::IntNBinopMatcher m(node);
1565 if (m.right().Is(0)) return Replace(m.left().node()); // x ^ 0 => x
1566 if (m.IsFoldable()) { // K ^ K => K
1567 return a.ReplaceIntN(m.left().Value() ^ m.right().Value());
1568 }
1569 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x ^ x => 0
1570 if (A::IsWordNXor(m.left()) && m.right().Is(-1)) {
1571 typename A::IntNBinopMatcher mleft(m.left().node());
1572 if (mleft.right().Is(-1)) { // (x ^ -1) ^ -1 => x
1573 return Replace(mleft.left().node());
1574 }
1575 }
1576
1577 return a.TryMatchWordNRor(node);
1578 }
1579
ReduceWord32Xor(Node * node)1580 Reduction MachineOperatorReducer::ReduceWord32Xor(Node* node) {
1581 DCHECK_EQ(IrOpcode::kWord32Xor, node->opcode());
1582 return ReduceWordNXor<Word32Adapter>(node);
1583 }
1584
ReduceWord64Xor(Node * node)1585 Reduction MachineOperatorReducer::ReduceWord64Xor(Node* node) {
1586 DCHECK_EQ(IrOpcode::kWord64Xor, node->opcode());
1587 return ReduceWordNXor<Word64Adapter>(node);
1588 }
1589
ReduceFloat64InsertLowWord32(Node * node)1590 Reduction MachineOperatorReducer::ReduceFloat64InsertLowWord32(Node* node) {
1591 DCHECK_EQ(IrOpcode::kFloat64InsertLowWord32, node->opcode());
1592 Float64Matcher mlhs(node->InputAt(0));
1593 Uint32Matcher mrhs(node->InputAt(1));
1594 if (mlhs.HasValue() && mrhs.HasValue()) {
1595 return ReplaceFloat64(bit_cast<double>(
1596 (bit_cast<uint64_t>(mlhs.Value()) & uint64_t{0xFFFFFFFF00000000}) |
1597 mrhs.Value()));
1598 }
1599 return NoChange();
1600 }
1601
ReduceFloat64InsertHighWord32(Node * node)1602 Reduction MachineOperatorReducer::ReduceFloat64InsertHighWord32(Node* node) {
1603 DCHECK_EQ(IrOpcode::kFloat64InsertHighWord32, node->opcode());
1604 Float64Matcher mlhs(node->InputAt(0));
1605 Uint32Matcher mrhs(node->InputAt(1));
1606 if (mlhs.HasValue() && mrhs.HasValue()) {
1607 return ReplaceFloat64(bit_cast<double>(
1608 (bit_cast<uint64_t>(mlhs.Value()) & uint64_t{0xFFFFFFFF}) |
1609 (static_cast<uint64_t>(mrhs.Value()) << 32)));
1610 }
1611 return NoChange();
1612 }
1613
1614 namespace {
1615
IsFloat64RepresentableAsFloat32(const Float64Matcher & m)1616 bool IsFloat64RepresentableAsFloat32(const Float64Matcher& m) {
1617 if (m.HasValue()) {
1618 double v = m.Value();
1619 return DoubleToFloat32(v) == v;
1620 }
1621 return false;
1622 }
1623
1624 } // namespace
1625
1626
ReduceFloat64Compare(Node * node)1627 Reduction MachineOperatorReducer::ReduceFloat64Compare(Node* node) {
1628 DCHECK(IrOpcode::kFloat64Equal == node->opcode() ||
1629 IrOpcode::kFloat64LessThan == node->opcode() ||
1630 IrOpcode::kFloat64LessThanOrEqual == node->opcode());
1631 Float64BinopMatcher m(node);
1632 if (m.IsFoldable()) {
1633 switch (node->opcode()) {
1634 case IrOpcode::kFloat64Equal:
1635 return ReplaceBool(m.left().Value() == m.right().Value());
1636 case IrOpcode::kFloat64LessThan:
1637 return ReplaceBool(m.left().Value() < m.right().Value());
1638 case IrOpcode::kFloat64LessThanOrEqual:
1639 return ReplaceBool(m.left().Value() <= m.right().Value());
1640 default:
1641 UNREACHABLE();
1642 }
1643 } else if ((m.left().IsChangeFloat32ToFloat64() &&
1644 m.right().IsChangeFloat32ToFloat64()) ||
1645 (m.left().IsChangeFloat32ToFloat64() &&
1646 IsFloat64RepresentableAsFloat32(m.right())) ||
1647 (IsFloat64RepresentableAsFloat32(m.left()) &&
1648 m.right().IsChangeFloat32ToFloat64())) {
1649 // As all Float32 values have an exact representation in Float64, comparing
1650 // two Float64 values both converted from Float32 is equivalent to comparing
1651 // the original Float32s, so we can ignore the conversions. We can also
1652 // reduce comparisons of converted Float64 values against constants that
1653 // can be represented exactly as Float32.
1654 switch (node->opcode()) {
1655 case IrOpcode::kFloat64Equal:
1656 NodeProperties::ChangeOp(node, machine()->Float32Equal());
1657 break;
1658 case IrOpcode::kFloat64LessThan:
1659 NodeProperties::ChangeOp(node, machine()->Float32LessThan());
1660 break;
1661 case IrOpcode::kFloat64LessThanOrEqual:
1662 NodeProperties::ChangeOp(node, machine()->Float32LessThanOrEqual());
1663 break;
1664 default:
1665 UNREACHABLE();
1666 }
1667 node->ReplaceInput(
1668 0, m.left().HasValue()
1669 ? Float32Constant(static_cast<float>(m.left().Value()))
1670 : m.left().InputAt(0));
1671 node->ReplaceInput(
1672 1, m.right().HasValue()
1673 ? Float32Constant(static_cast<float>(m.right().Value()))
1674 : m.right().InputAt(0));
1675 return Changed(node);
1676 }
1677 return NoChange();
1678 }
1679
ReduceFloat64RoundDown(Node * node)1680 Reduction MachineOperatorReducer::ReduceFloat64RoundDown(Node* node) {
1681 DCHECK_EQ(IrOpcode::kFloat64RoundDown, node->opcode());
1682 Float64Matcher m(node->InputAt(0));
1683 if (m.HasValue()) {
1684 return ReplaceFloat64(std::floor(m.Value()));
1685 }
1686 return NoChange();
1687 }
1688
ReduceConditional(Node * node)1689 Reduction MachineOperatorReducer::ReduceConditional(Node* node) {
1690 DCHECK(node->opcode() == IrOpcode::kBranch ||
1691 node->opcode() == IrOpcode::kDeoptimizeIf ||
1692 node->opcode() == IrOpcode::kDeoptimizeUnless ||
1693 node->opcode() == IrOpcode::kTrapIf ||
1694 node->opcode() == IrOpcode::kTrapUnless);
1695 // This reducer only applies operator reductions to the branch condition.
1696 // Reductions involving control flow happen elsewhere. Non-zero inputs are
1697 // considered true in all conditional ops.
1698 NodeMatcher condition(NodeProperties::GetValueInput(node, 0));
1699 if (condition.IsWord32And()) {
1700 Uint32BinopMatcher mand(condition.node());
1701 if ((mand.left().IsWord32Shr() || mand.left().IsWord32Sar()) &&
1702 mand.right().HasValue()) {
1703 Uint32BinopMatcher mshift(mand.left().node());
1704 // Branch condition (x >> K1) & K2 => x & (K2 << K1)
1705 if (mshift.right().HasValue()) {
1706 auto shift_bits = mshift.right().Value();
1707 auto mask = mand.right().Value();
1708 // Make sure that we won't shift data off the end.
1709 if (shift_bits <= base::bits::CountLeadingZeros(mask)) {
1710 NodeProperties::ReplaceValueInput(
1711 node, Word32And(mshift.left().node(), mask << shift_bits), 0);
1712 return Changed(node);
1713 }
1714 }
1715 }
1716 }
1717 return NoChange();
1718 }
1719
common() const1720 CommonOperatorBuilder* MachineOperatorReducer::common() const {
1721 return mcgraph()->common();
1722 }
1723
machine() const1724 MachineOperatorBuilder* MachineOperatorReducer::machine() const {
1725 return mcgraph()->machine();
1726 }
1727
graph() const1728 Graph* MachineOperatorReducer::graph() const { return mcgraph()->graph(); }
1729
1730 } // namespace compiler
1731 } // namespace internal
1732 } // namespace v8
1733