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