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.
5 #include "src/compiler/machine-operator-reducer.h"
7 #include "src/base/bits.h"
8 #include "src/compiler/generic-node-inl.h"
9 #include "src/compiler/graph.h"
10 #include "src/compiler/js-graph.h"
11 #include "src/compiler/node-matchers.h"
17 MachineOperatorReducer::MachineOperatorReducer(JSGraph* jsgraph)
18 : jsgraph_(jsgraph) {}
21 MachineOperatorReducer::~MachineOperatorReducer() {}
24 Node* MachineOperatorReducer::Float32Constant(volatile float value) {
25 return graph()->NewNode(common()->Float32Constant(value));
29 Node* MachineOperatorReducer::Float64Constant(volatile double value) {
30 return jsgraph()->Float64Constant(value);
34 Node* MachineOperatorReducer::Int32Constant(int32_t value) {
35 return jsgraph()->Int32Constant(value);
39 Node* MachineOperatorReducer::Int64Constant(int64_t value) {
40 return graph()->NewNode(common()->Int64Constant(value));
44 // Perform constant folding and strength reduction on machine operators.
45 Reduction MachineOperatorReducer::Reduce(Node* node) {
46 switch (node->opcode()) {
47 case IrOpcode::kProjection:
48 return ReduceProjection(OpParameter<size_t>(node), node->InputAt(0));
49 case IrOpcode::kWord32And: {
50 Int32BinopMatcher m(node);
51 if (m.right().Is(0)) return Replace(m.right().node()); // x & 0 => 0
52 if (m.right().Is(-1)) return Replace(m.left().node()); // x & -1 => x
53 if (m.IsFoldable()) { // K & K => K
54 return ReplaceInt32(m.left().Value() & m.right().Value());
56 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x & x => x
59 case IrOpcode::kWord32Or: {
60 Int32BinopMatcher m(node);
61 if (m.right().Is(0)) return Replace(m.left().node()); // x | 0 => x
62 if (m.right().Is(-1)) return Replace(m.right().node()); // x | -1 => -1
63 if (m.IsFoldable()) { // K | K => K
64 return ReplaceInt32(m.left().Value() | m.right().Value());
66 if (m.LeftEqualsRight()) return Replace(m.left().node()); // x | x => x
67 if (m.left().IsWord32Shl() && m.right().IsWord32Shr()) {
68 Int32BinopMatcher mleft(m.left().node());
69 Int32BinopMatcher mright(m.right().node());
70 if (mleft.left().node() == mright.left().node()) {
71 // (x << y) | (x >> (32 - y)) => x ror y
72 if (mright.right().IsInt32Sub()) {
73 Int32BinopMatcher mrightright(mright.right().node());
74 if (mrightright.left().Is(32) &&
75 mrightright.right().node() == mleft.right().node()) {
76 node->set_op(machine()->Word32Ror());
77 node->ReplaceInput(0, mleft.left().node());
78 node->ReplaceInput(1, mleft.right().node());
82 // (x << K) | (x >> (32 - K)) => x ror K
83 if (mleft.right().IsInRange(0, 31) &&
84 mright.right().Is(32 - mleft.right().Value())) {
85 node->set_op(machine()->Word32Ror());
86 node->ReplaceInput(0, mleft.left().node());
87 node->ReplaceInput(1, mleft.right().node());
92 if (m.left().IsWord32Shr() && m.right().IsWord32Shl()) {
93 // (x >> (32 - y)) | (x << y) => x ror y
94 Int32BinopMatcher mleft(m.left().node());
95 Int32BinopMatcher mright(m.right().node());
96 if (mleft.left().node() == mright.left().node()) {
97 if (mleft.right().IsInt32Sub()) {
98 Int32BinopMatcher mleftright(mleft.right().node());
99 if (mleftright.left().Is(32) &&
100 mleftright.right().node() == mright.right().node()) {
101 node->set_op(machine()->Word32Ror());
102 node->ReplaceInput(0, mright.left().node());
103 node->ReplaceInput(1, mright.right().node());
104 return Changed(node);
107 // (x >> (32 - K)) | (x << K) => x ror K
108 if (mright.right().IsInRange(0, 31) &&
109 mleft.right().Is(32 - mright.right().Value())) {
110 node->set_op(machine()->Word32Ror());
111 node->ReplaceInput(0, mright.left().node());
112 node->ReplaceInput(1, mright.right().node());
113 return Changed(node);
119 case IrOpcode::kWord32Xor: {
120 Int32BinopMatcher m(node);
121 if (m.right().Is(0)) return Replace(m.left().node()); // x ^ 0 => x
122 if (m.IsFoldable()) { // K ^ K => K
123 return ReplaceInt32(m.left().Value() ^ m.right().Value());
125 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x ^ x => 0
128 case IrOpcode::kWord32Shl: {
129 Int32BinopMatcher m(node);
130 if (m.right().Is(0)) return Replace(m.left().node()); // x << 0 => x
131 if (m.IsFoldable()) { // K << K => K
132 return ReplaceInt32(m.left().Value() << m.right().Value());
136 case IrOpcode::kWord32Shr: {
137 Uint32BinopMatcher m(node);
138 if (m.right().Is(0)) return Replace(m.left().node()); // x >>> 0 => x
139 if (m.IsFoldable()) { // K >>> K => K
140 return ReplaceInt32(m.left().Value() >> m.right().Value());
144 case IrOpcode::kWord32Sar: {
145 Int32BinopMatcher m(node);
146 if (m.right().Is(0)) return Replace(m.left().node()); // x >> 0 => x
147 if (m.IsFoldable()) { // K >> K => K
148 return ReplaceInt32(m.left().Value() >> m.right().Value());
152 case IrOpcode::kWord32Ror: {
153 Int32BinopMatcher m(node);
154 if (m.right().Is(0)) return Replace(m.left().node()); // x ror 0 => x
155 if (m.IsFoldable()) { // K ror K => K
157 base::bits::RotateRight32(m.left().Value(), m.right().Value()));
161 case IrOpcode::kWord32Equal: {
162 Int32BinopMatcher m(node);
163 if (m.IsFoldable()) { // K == K => K
164 return ReplaceBool(m.left().Value() == m.right().Value());
166 if (m.left().IsInt32Sub() && m.right().Is(0)) { // x - y == 0 => x == y
167 Int32BinopMatcher msub(m.left().node());
168 node->ReplaceInput(0, msub.left().node());
169 node->ReplaceInput(1, msub.right().node());
170 return Changed(node);
172 // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
173 if (m.LeftEqualsRight()) return ReplaceBool(true); // x == x => true
176 case IrOpcode::kInt32Add: {
177 Int32BinopMatcher m(node);
178 if (m.right().Is(0)) return Replace(m.left().node()); // x + 0 => x
179 if (m.IsFoldable()) { // K + K => K
180 return ReplaceInt32(static_cast<uint32_t>(m.left().Value()) +
181 static_cast<uint32_t>(m.right().Value()));
185 case IrOpcode::kInt32Sub: {
186 Int32BinopMatcher m(node);
187 if (m.right().Is(0)) return Replace(m.left().node()); // x - 0 => x
188 if (m.IsFoldable()) { // K - K => K
189 return ReplaceInt32(static_cast<uint32_t>(m.left().Value()) -
190 static_cast<uint32_t>(m.right().Value()));
192 if (m.LeftEqualsRight()) return ReplaceInt32(0); // x - x => 0
195 case IrOpcode::kInt32Mul: {
196 Int32BinopMatcher m(node);
197 if (m.right().Is(0)) return Replace(m.right().node()); // x * 0 => 0
198 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1 => x
199 if (m.IsFoldable()) { // K * K => K
200 return ReplaceInt32(m.left().Value() * m.right().Value());
202 if (m.right().Is(-1)) { // x * -1 => 0 - x
203 node->set_op(machine()->Int32Sub());
204 node->ReplaceInput(0, Int32Constant(0));
205 node->ReplaceInput(1, m.left().node());
206 return Changed(node);
208 if (m.right().IsPowerOf2()) { // x * 2^n => x << n
209 node->set_op(machine()->Word32Shl());
210 node->ReplaceInput(1, Int32Constant(WhichPowerOf2(m.right().Value())));
211 return Changed(node);
215 case IrOpcode::kInt32Div: {
216 Int32BinopMatcher m(node);
217 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
218 // TODO(turbofan): if (m.left().Is(0))
219 // TODO(turbofan): if (m.right().IsPowerOf2())
220 // TODO(turbofan): if (m.right().Is(0))
221 // TODO(turbofan): if (m.LeftEqualsRight())
222 if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
223 if (m.right().Is(-1)) return ReplaceInt32(-m.left().Value());
224 return ReplaceInt32(m.left().Value() / m.right().Value());
226 if (m.right().Is(-1)) { // x / -1 => 0 - x
227 node->set_op(machine()->Int32Sub());
228 node->ReplaceInput(0, Int32Constant(0));
229 node->ReplaceInput(1, m.left().node());
230 return Changed(node);
234 case IrOpcode::kInt32UDiv: {
235 Uint32BinopMatcher m(node);
236 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
237 // TODO(turbofan): if (m.left().Is(0))
238 // TODO(turbofan): if (m.right().Is(0))
239 // TODO(turbofan): if (m.LeftEqualsRight())
240 if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
241 return ReplaceInt32(m.left().Value() / m.right().Value());
243 if (m.right().IsPowerOf2()) { // x / 2^n => x >> n
244 node->set_op(machine()->Word32Shr());
245 node->ReplaceInput(1, Int32Constant(WhichPowerOf2(m.right().Value())));
246 return Changed(node);
250 case IrOpcode::kInt32Mod: {
251 Int32BinopMatcher m(node);
252 if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
253 if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
254 // TODO(turbofan): if (m.left().Is(0))
255 // TODO(turbofan): if (m.right().IsPowerOf2())
256 // TODO(turbofan): if (m.right().Is(0))
257 // TODO(turbofan): if (m.LeftEqualsRight())
258 if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
259 return ReplaceInt32(m.left().Value() % m.right().Value());
263 case IrOpcode::kInt32UMod: {
264 Uint32BinopMatcher m(node);
265 if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
266 // TODO(turbofan): if (m.left().Is(0))
267 // TODO(turbofan): if (m.right().Is(0))
268 // TODO(turbofan): if (m.LeftEqualsRight())
269 if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
270 return ReplaceInt32(m.left().Value() % m.right().Value());
272 if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1
273 node->set_op(machine()->Word32And());
274 node->ReplaceInput(1, Int32Constant(m.right().Value() - 1));
275 return Changed(node);
279 case IrOpcode::kInt32LessThan: {
280 Int32BinopMatcher m(node);
281 if (m.IsFoldable()) { // K < K => K
282 return ReplaceBool(m.left().Value() < m.right().Value());
284 if (m.left().IsInt32Sub() && m.right().Is(0)) { // x - y < 0 => x < y
285 Int32BinopMatcher msub(m.left().node());
286 node->ReplaceInput(0, msub.left().node());
287 node->ReplaceInput(1, msub.right().node());
288 return Changed(node);
290 if (m.left().Is(0) && m.right().IsInt32Sub()) { // 0 < x - y => y < x
291 Int32BinopMatcher msub(m.right().node());
292 node->ReplaceInput(0, msub.right().node());
293 node->ReplaceInput(1, msub.left().node());
294 return Changed(node);
296 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
299 case IrOpcode::kInt32LessThanOrEqual: {
300 Int32BinopMatcher m(node);
301 if (m.IsFoldable()) { // K <= K => K
302 return ReplaceBool(m.left().Value() <= m.right().Value());
304 if (m.left().IsInt32Sub() && m.right().Is(0)) { // x - y <= 0 => x <= y
305 Int32BinopMatcher msub(m.left().node());
306 node->ReplaceInput(0, msub.left().node());
307 node->ReplaceInput(1, msub.right().node());
308 return Changed(node);
310 if (m.left().Is(0) && m.right().IsInt32Sub()) { // 0 <= x - y => y <= x
311 Int32BinopMatcher msub(m.right().node());
312 node->ReplaceInput(0, msub.right().node());
313 node->ReplaceInput(1, msub.left().node());
314 return Changed(node);
316 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
319 case IrOpcode::kUint32LessThan: {
320 Uint32BinopMatcher m(node);
321 if (m.left().Is(kMaxUInt32)) return ReplaceBool(false); // M < x => false
322 if (m.right().Is(0)) return ReplaceBool(false); // x < 0 => false
323 if (m.IsFoldable()) { // K < K => K
324 return ReplaceBool(m.left().Value() < m.right().Value());
326 if (m.LeftEqualsRight()) return ReplaceBool(false); // x < x => false
329 case IrOpcode::kUint32LessThanOrEqual: {
330 Uint32BinopMatcher m(node);
331 if (m.left().Is(0)) return ReplaceBool(true); // 0 <= x => true
332 if (m.right().Is(kMaxUInt32)) return ReplaceBool(true); // x <= M => true
333 if (m.IsFoldable()) { // K <= K => K
334 return ReplaceBool(m.left().Value() <= m.right().Value());
336 if (m.LeftEqualsRight()) return ReplaceBool(true); // x <= x => true
339 case IrOpcode::kFloat64Add: {
340 Float64BinopMatcher m(node);
341 if (m.IsFoldable()) { // K + K => K
342 return ReplaceFloat64(m.left().Value() + m.right().Value());
346 case IrOpcode::kFloat64Sub: {
347 Float64BinopMatcher m(node);
348 if (m.IsFoldable()) { // K - K => K
349 return ReplaceFloat64(m.left().Value() - m.right().Value());
353 case IrOpcode::kFloat64Mul: {
354 Float64BinopMatcher m(node);
355 if (m.right().Is(1)) return Replace(m.left().node()); // x * 1.0 => x
356 if (m.right().IsNaN()) { // x * NaN => NaN
357 return Replace(m.right().node());
359 if (m.IsFoldable()) { // K * K => K
360 return ReplaceFloat64(m.left().Value() * m.right().Value());
364 case IrOpcode::kFloat64Div: {
365 Float64BinopMatcher m(node);
366 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1.0 => x
367 if (m.right().IsNaN()) { // x / NaN => NaN
368 return Replace(m.right().node());
370 if (m.left().IsNaN()) { // NaN / x => NaN
371 return Replace(m.left().node());
373 if (m.IsFoldable()) { // K / K => K
374 return ReplaceFloat64(m.left().Value() / m.right().Value());
378 case IrOpcode::kFloat64Mod: {
379 Float64BinopMatcher m(node);
380 if (m.right().IsNaN()) { // x % NaN => NaN
381 return Replace(m.right().node());
383 if (m.left().IsNaN()) { // NaN % x => NaN
384 return Replace(m.left().node());
386 if (m.IsFoldable()) { // K % K => K
387 return ReplaceFloat64(modulo(m.left().Value(), m.right().Value()));
391 case IrOpcode::kChangeFloat32ToFloat64: {
392 Float32Matcher m(node->InputAt(0));
393 if (m.HasValue()) return ReplaceFloat64(m.Value());
396 case IrOpcode::kChangeFloat64ToInt32: {
397 Float64Matcher m(node->InputAt(0));
398 if (m.HasValue()) return ReplaceInt32(FastD2I(m.Value()));
399 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
402 case IrOpcode::kChangeFloat64ToUint32: {
403 Float64Matcher m(node->InputAt(0));
404 if (m.HasValue()) return ReplaceInt32(FastD2UI(m.Value()));
405 if (m.IsChangeUint32ToFloat64()) return Replace(m.node()->InputAt(0));
408 case IrOpcode::kChangeInt32ToFloat64: {
409 Int32Matcher m(node->InputAt(0));
410 if (m.HasValue()) return ReplaceFloat64(FastI2D(m.Value()));
413 case IrOpcode::kChangeInt32ToInt64: {
414 Int32Matcher m(node->InputAt(0));
415 if (m.HasValue()) return ReplaceInt64(m.Value());
418 case IrOpcode::kChangeUint32ToFloat64: {
419 Uint32Matcher m(node->InputAt(0));
420 if (m.HasValue()) return ReplaceFloat64(FastUI2D(m.Value()));
423 case IrOpcode::kChangeUint32ToUint64: {
424 Uint32Matcher m(node->InputAt(0));
425 if (m.HasValue()) return ReplaceInt64(static_cast<uint64_t>(m.Value()));
428 case IrOpcode::kTruncateFloat64ToInt32: {
429 Float64Matcher m(node->InputAt(0));
430 if (m.HasValue()) return ReplaceInt32(DoubleToInt32(m.Value()));
431 if (m.IsChangeInt32ToFloat64()) return Replace(m.node()->InputAt(0));
434 case IrOpcode::kTruncateInt64ToInt32: {
435 Int64Matcher m(node->InputAt(0));
436 if (m.HasValue()) return ReplaceInt32(static_cast<int32_t>(m.Value()));
437 if (m.IsChangeInt32ToInt64()) return Replace(m.node()->InputAt(0));
440 case IrOpcode::kTruncateFloat64ToFloat32: {
441 Float64Matcher m(node->InputAt(0));
442 if (m.HasValue()) return ReplaceFloat32(DoubleToFloat32(m.Value()));
443 if (m.IsChangeFloat32ToFloat64()) return Replace(m.node()->InputAt(0));
453 Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) {
454 switch (node->opcode()) {
455 case IrOpcode::kInt32AddWithOverflow: {
456 DCHECK(index == 0 || index == 1);
457 Int32BinopMatcher m(node);
458 if (m.IsFoldable()) {
460 bool ovf = base::bits::SignedAddOverflow32(m.left().Value(),
461 m.right().Value(), &val);
462 return ReplaceInt32((index == 0) ? val : ovf);
464 if (m.right().Is(0)) {
465 return (index == 0) ? Replace(m.left().node()) : ReplaceInt32(0);
469 case IrOpcode::kInt32SubWithOverflow: {
470 DCHECK(index == 0 || index == 1);
471 Int32BinopMatcher m(node);
472 if (m.IsFoldable()) {
474 bool ovf = base::bits::SignedSubOverflow32(m.left().Value(),
475 m.right().Value(), &val);
476 return ReplaceInt32((index == 0) ? val : ovf);
478 if (m.right().Is(0)) {
479 return (index == 0) ? Replace(m.left().node()) : ReplaceInt32(0);
490 CommonOperatorBuilder* MachineOperatorReducer::common() const {
491 return jsgraph()->common();
495 MachineOperatorBuilder* MachineOperatorReducer::machine() const {
496 return jsgraph()->machine();
500 Graph* MachineOperatorReducer::graph() const { return jsgraph()->graph(); }
502 } // namespace compiler
503 } // namespace internal