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/js-graph.h"
6 #include "src/compiler/js-typed-lowering.h"
7 #include "src/compiler/machine-operator.h"
8 #include "src/compiler/node-properties.h"
9 #include "src/compiler/opcodes.h"
10 #include "src/compiler/operator-properties.h"
11 #include "src/compiler/typer.h"
12 #include "test/cctest/cctest.h"
14 using namespace v8::internal;
15 using namespace v8::internal::compiler;
17 class JSTypedLoweringTester : public HandleAndZoneScope {
19 explicit JSTypedLoweringTester(int num_parameters = 0)
20 : isolate(main_isolate()),
23 javascript(main_zone()),
25 simplified(main_zone()),
28 typer(main_isolate(), &graph, MaybeHandle<Context>()),
30 graph.SetStart(graph.NewNode(common.Start(num_parameters)));
31 graph.SetEnd(graph.NewNode(common.End()));
36 const Operator* binop;
38 JSOperatorBuilder javascript;
39 MachineOperatorBuilder machine;
40 SimplifiedOperatorBuilder simplified;
41 CommonOperatorBuilder common;
46 Node* Parameter(Type* t, int32_t index = 0) {
47 Node* n = graph.NewNode(common.Parameter(index), graph.start());
48 NodeProperties::SetBounds(n, Bounds(Type::None(), t));
52 Node* UndefinedConstant() {
53 Unique<HeapObject> unique = Unique<HeapObject>::CreateImmovable(
54 isolate->factory()->undefined_value());
55 return graph.NewNode(common.HeapConstant(unique));
58 Node* HeapConstant(Handle<HeapObject> constant) {
59 Unique<HeapObject> unique =
60 Unique<HeapObject>::CreateUninitialized(constant);
61 return graph.NewNode(common.HeapConstant(unique));
64 Node* EmptyFrameState(Node* context) {
65 Node* parameters = graph.NewNode(common.StateValues(0));
66 Node* locals = graph.NewNode(common.StateValues(0));
67 Node* stack = graph.NewNode(common.StateValues(0));
70 graph.NewNode(common.FrameState(JS_FRAME, BailoutId::None(),
71 OutputFrameStateCombine::Ignore()),
72 parameters, locals, stack, context, UndefinedConstant());
77 Node* reduce(Node* node) {
78 JSGraph jsgraph(main_isolate(), &graph, &common, &javascript, &machine);
79 JSTypedLowering reducer(&jsgraph, main_zone());
80 Reduction reduction = reducer.Reduce(node);
81 if (reduction.Changed()) return reduction.replacement();
85 Node* start() { return graph.start(); }
88 if (context_node == NULL) {
89 context_node = graph.NewNode(common.Parameter(-1), graph.start());
94 Node* control() { return start(); }
96 void CheckPureBinop(IrOpcode::Value expected, Node* node) {
97 CHECK_EQ(expected, node->opcode());
98 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
101 void CheckPureBinop(const Operator* expected, Node* node) {
102 CHECK_EQ(expected->opcode(), node->op()->opcode());
103 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
106 Node* ReduceUnop(const Operator* op, Type* input_type) {
107 return reduce(Unop(op, Parameter(input_type)));
110 Node* ReduceBinop(const Operator* op, Type* left_type, Type* right_type) {
111 return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
114 Node* Binop(const Operator* op, Node* left, Node* right) {
115 // JS binops also require context, effect, and control
116 if (OperatorProperties::GetFrameStateInputCount(op) == 1) {
117 return graph.NewNode(op, left, right, context(),
118 EmptyFrameState(context()), start(), control());
119 } else if (OperatorProperties::GetFrameStateInputCount(op) == 2) {
120 return graph.NewNode(op, left, right, context(),
121 EmptyFrameState(context()),
122 EmptyFrameState(context()), start(), control());
124 return graph.NewNode(op, left, right, context(), start(), control());
128 Node* Unop(const Operator* op, Node* input) {
129 // JS unops also require context, effect, and control
130 if (OperatorProperties::GetFrameStateInputCount(op) > 0) {
131 DCHECK(OperatorProperties::GetFrameStateInputCount(op) == 1);
132 return graph.NewNode(op, input, context(), EmptyFrameState(context()),
135 return graph.NewNode(op, input, context(), start(), control());
139 Node* UseForEffect(Node* node) {
140 // TODO(titzer): use EffectPhi after fixing EffectCount
141 if (OperatorProperties::GetFrameStateInputCount(javascript.ToNumber()) >
143 DCHECK(OperatorProperties::GetFrameStateInputCount(
144 javascript.ToNumber()) == 1);
145 return graph.NewNode(javascript.ToNumber(), node, context(),
146 EmptyFrameState(context()), node, control());
148 return graph.NewNode(javascript.ToNumber(), node, context(), node,
153 void CheckEffectInput(Node* effect, Node* use) {
154 CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
157 void CheckInt32Constant(int32_t expected, Node* result) {
158 CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
159 CHECK_EQ(expected, OpParameter<int32_t>(result));
162 void CheckNumberConstant(double expected, Node* result) {
163 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
164 CHECK_EQ(expected, OpParameter<double>(result));
167 void CheckNaN(Node* result) {
168 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
169 double value = OpParameter<double>(result);
170 CHECK(std::isnan(value));
173 void CheckTrue(Node* result) {
174 CheckHandle(isolate->factory()->true_value(), result);
177 void CheckFalse(Node* result) {
178 CheckHandle(isolate->factory()->false_value(), result);
181 void CheckHandle(Handle<Object> expected, Node* result) {
182 CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
183 Handle<Object> value = OpParameter<Unique<Object> >(result).handle();
184 CHECK_EQ(*expected, *value);
188 static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
192 static Type* kInt32Types[] = {Type::UnsignedSmall(), Type::Negative32(),
193 Type::Unsigned31(), Type::SignedSmall(),
194 Type::Signed32(), Type::Unsigned32(),
198 static Type* kNumberTypes[] = {
199 Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(),
200 Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(),
201 Type::Integral32(), Type::MinusZero(), Type::NaN(),
202 Type::OrderedNumber(), Type::PlainNumber(), Type::Number()};
205 static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
206 Type::Number(), Type::String(), Type::Object()};
209 static Type* I32Type(bool is_signed) {
210 return is_signed ? Type::Signed32() : Type::Unsigned32();
214 static IrOpcode::Value NumberToI32(bool is_signed) {
215 return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
219 // TODO(turbofan): Lowering of StringAdd is disabled for now.
222 JSTypedLoweringTester R;
224 for (size_t i = 0; i < arraysize(kStringTypes); ++i) {
225 Node* p0 = R.Parameter(kStringTypes[i], 0);
227 for (size_t j = 0; j < arraysize(kStringTypes); ++j) {
228 Node* p1 = R.Parameter(kStringTypes[j], 1);
230 Node* add = R.Binop(R.javascript.Add(), p0, p1);
231 Node* r = R.reduce(add);
233 R.CheckPureBinop(IrOpcode::kStringAdd, r);
234 CHECK_EQ(p0, r->InputAt(0));
235 CHECK_EQ(p1, r->InputAt(1));
243 JSTypedLoweringTester R;
244 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
245 Node* p0 = R.Parameter(kNumberTypes[i], 0);
246 Node* p1 = R.Parameter(kNumberTypes[i], 1);
247 Node* add = R.Binop(R.javascript.Add(), p0, p1);
248 Node* r = R.reduce(add);
250 R.CheckPureBinop(IrOpcode::kNumberAdd, r);
251 CHECK_EQ(p0, r->InputAt(0));
252 CHECK_EQ(p1, r->InputAt(1));
258 JSTypedLoweringTester R;
259 const Operator* ops[] = {
260 R.javascript.Add(), R.simplified.NumberAdd(),
261 R.javascript.Subtract(), R.simplified.NumberSubtract(),
262 R.javascript.Multiply(), R.simplified.NumberMultiply(),
263 R.javascript.Divide(), R.simplified.NumberDivide(),
264 R.javascript.Modulus(), R.simplified.NumberModulus(),
267 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
268 Node* p0 = R.Parameter(kNumberTypes[i], 0);
270 for (size_t j = 0; j < arraysize(kNumberTypes); ++j) {
271 Node* p1 = R.Parameter(kNumberTypes[j], 1);
273 for (size_t k = 0; k < arraysize(ops); k += 2) {
274 Node* add = R.Binop(ops[k], p0, p1);
275 Node* r = R.reduce(add);
277 R.CheckPureBinop(ops[k + 1], r);
278 CHECK_EQ(p0, r->InputAt(0));
279 CHECK_EQ(p1, r->InputAt(1));
286 static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
287 Type* old_type = NodeProperties::GetBounds(old_input).upper;
288 Type* new_type = NodeProperties::GetBounds(new_input).upper;
289 Type* expected_type = I32Type(is_signed);
290 CHECK(new_type->Is(expected_type));
291 if (old_type->Is(expected_type)) {
292 CHECK_EQ(old_input, new_input);
293 } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
294 double v = OpParameter<double>(new_input);
295 double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
301 // A helper class for testing lowering of bitwise shift operators.
302 class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
304 static const int kNumberOps = 6;
305 const Operator* ops[kNumberOps];
306 bool signedness[kNumberOps];
308 JSBitwiseShiftTypedLoweringTester() {
310 set(i++, javascript.ShiftLeft(), true);
311 set(i++, machine.Word32Shl(), false);
312 set(i++, javascript.ShiftRight(), true);
313 set(i++, machine.Word32Sar(), false);
314 set(i++, javascript.ShiftRightLogical(), false);
315 set(i++, machine.Word32Shr(), false);
319 void set(int idx, const Operator* op, bool s) {
326 TEST(Int32BitwiseShifts) {
327 JSBitwiseShiftTypedLoweringTester R;
330 Type::SignedSmall(), Type::UnsignedSmall(), Type::Negative32(),
331 Type::Unsigned31(), Type::Unsigned32(), Type::Signed32(),
332 Type::MinusZero(), Type::NaN(), Type::Undefined(),
333 Type::Null(), Type::Boolean(), Type::Number(),
334 Type::PlainNumber(), Type::String()};
336 for (size_t i = 0; i < arraysize(types); ++i) {
337 Node* p0 = R.Parameter(types[i], 0);
339 for (size_t j = 0; j < arraysize(types); ++j) {
340 Node* p1 = R.Parameter(types[j], 1);
342 for (int k = 0; k < R.kNumberOps; k += 2) {
343 Node* add = R.Binop(R.ops[k], p0, p1);
344 Node* r = R.reduce(add);
346 R.CheckPureBinop(R.ops[k + 1], r);
347 Node* r0 = r->InputAt(0);
348 Node* r1 = r->InputAt(1);
350 CheckToI32(p0, r0, R.signedness[k]);
352 if (r1->opcode() == IrOpcode::kWord32And) {
353 R.CheckPureBinop(IrOpcode::kWord32And, r1);
354 CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
355 R.CheckInt32Constant(0x1F, r1->InputAt(1));
357 CheckToI32(p1, r1, R.signedness[k]);
365 // A helper class for testing lowering of bitwise operators.
366 class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
368 static const int kNumberOps = 6;
369 const Operator* ops[kNumberOps];
370 bool signedness[kNumberOps];
372 JSBitwiseTypedLoweringTester() {
374 set(i++, javascript.BitwiseOr(), true);
375 set(i++, machine.Word32Or(), true);
376 set(i++, javascript.BitwiseXor(), true);
377 set(i++, machine.Word32Xor(), true);
378 set(i++, javascript.BitwiseAnd(), true);
379 set(i++, machine.Word32And(), true);
383 void set(int idx, const Operator* op, bool s) {
390 TEST(Int32BitwiseBinops) {
391 JSBitwiseTypedLoweringTester R;
394 Type::SignedSmall(), Type::UnsignedSmall(), Type::Unsigned32(),
395 Type::Signed32(), Type::MinusZero(), Type::NaN(),
396 Type::OrderedNumber(), Type::PlainNumber(), Type::Undefined(),
397 Type::Null(), Type::Boolean(), Type::Number(),
400 for (size_t i = 0; i < arraysize(types); ++i) {
401 Node* p0 = R.Parameter(types[i], 0);
403 for (size_t j = 0; j < arraysize(types); ++j) {
404 Node* p1 = R.Parameter(types[j], 1);
406 for (int k = 0; k < R.kNumberOps; k += 2) {
407 Node* add = R.Binop(R.ops[k], p0, p1);
408 Node* r = R.reduce(add);
410 R.CheckPureBinop(R.ops[k + 1], r);
412 CheckToI32(p0, r->InputAt(0), R.signedness[k]);
413 CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
421 JSTypedLoweringTester R;
422 const Operator* ton = R.javascript.ToNumber();
424 for (size_t i = 0; i < arraysize(kNumberTypes); i++) { // ToNumber(number)
425 Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
426 CHECK_EQ(IrOpcode::kParameter, r->opcode());
429 { // ToNumber(undefined)
430 Node* r = R.ReduceUnop(ton, Type::Undefined());
435 Node* r = R.ReduceUnop(ton, Type::Null());
436 R.CheckNumberConstant(0.0, r);
441 TEST(JSToNumber_replacement) {
442 JSTypedLoweringTester R;
444 Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()};
446 for (size_t i = 0; i < arraysize(types); i++) {
447 Node* n = R.Parameter(types[i]);
448 Node* c = R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
449 R.start(), R.start());
450 Node* effect_use = R.UseForEffect(c);
451 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
453 R.CheckEffectInput(c, effect_use);
454 Node* r = R.reduce(c);
456 if (types[i]->Is(Type::Number())) {
459 CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
462 CHECK_EQ(n, add->InputAt(0));
463 CHECK_EQ(r, add->InputAt(1));
464 R.CheckEffectInput(R.start(), effect_use);
469 TEST(JSToNumberOfConstant) {
470 JSTypedLoweringTester R;
472 const Operator* ops[] = {
473 R.common.NumberConstant(0), R.common.NumberConstant(-1),
474 R.common.NumberConstant(0.1), R.common.Int32Constant(1177),
475 R.common.Float64Constant(0.99)};
477 for (size_t i = 0; i < arraysize(ops); i++) {
478 Node* n = R.graph.NewNode(ops[i]);
479 Node* convert = R.Unop(R.javascript.ToNumber(), n);
480 Node* r = R.reduce(convert);
481 // Note that either outcome below is correct. It only depends on whether
482 // the types of constants are eagerly computed or only computed by the
484 if (NodeProperties::GetBounds(n).upper->Is(Type::Number())) {
485 // If number constants are eagerly typed, then reduction should
486 // remove the ToNumber.
489 // Otherwise, type-based lowering should only look at the type, and
490 // *not* try to constant fold.
491 CHECK_EQ(convert, r);
497 TEST(JSToNumberOfNumberOrOtherPrimitive) {
498 JSTypedLoweringTester R;
499 Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
502 for (size_t i = 0; i < arraysize(others); i++) {
503 Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
504 Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
505 CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
511 JSTypedLoweringTester R;
513 for (size_t i = 0; i < arraysize(kStringTypes); i++) {
514 Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
515 CHECK_EQ(IrOpcode::kParameter, r->opcode());
518 const Operator* op = R.javascript.ToString();
520 { // ToString(undefined) => "undefined"
521 Node* r = R.ReduceUnop(op, Type::Undefined());
522 R.CheckHandle(R.isolate->factory()->undefined_string(), r);
525 { // ToString(null) => "null"
526 Node* r = R.ReduceUnop(op, Type::Null());
527 R.CheckHandle(R.isolate->factory()->null_string(), r);
530 { // ToString(boolean)
531 Node* r = R.ReduceUnop(op, Type::Boolean());
532 // TODO(titzer): could be a branch
533 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
536 { // ToString(number)
537 Node* r = R.ReduceUnop(op, Type::Number());
538 // TODO(titzer): could remove effects
539 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
542 { // ToString(string)
543 Node* r = R.ReduceUnop(op, Type::String());
544 CHECK_EQ(IrOpcode::kParameter, r->opcode()); // No-op
547 { // ToString(object)
548 Node* r = R.ReduceUnop(op, Type::Object());
549 CHECK_EQ(IrOpcode::kJSToString, r->opcode()); // No reduction.
554 TEST(JSToString_replacement) {
555 JSTypedLoweringTester R;
557 Type* types[] = {Type::Null(), Type::Undefined(), Type::String()};
559 for (size_t i = 0; i < arraysize(types); i++) {
560 Node* n = R.Parameter(types[i]);
561 Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
562 R.start(), R.start());
563 Node* effect_use = R.UseForEffect(c);
564 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
566 R.CheckEffectInput(c, effect_use);
567 Node* r = R.reduce(c);
569 if (types[i]->Is(Type::String())) {
572 CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
575 CHECK_EQ(n, add->InputAt(0));
576 CHECK_EQ(r, add->InputAt(1));
577 R.CheckEffectInput(R.start(), effect_use);
582 TEST(StringComparison) {
583 JSTypedLoweringTester R;
585 const Operator* ops[] = {
586 R.javascript.LessThan(), R.simplified.StringLessThan(),
587 R.javascript.LessThanOrEqual(), R.simplified.StringLessThanOrEqual(),
588 R.javascript.GreaterThan(), R.simplified.StringLessThan(),
589 R.javascript.GreaterThanOrEqual(), R.simplified.StringLessThanOrEqual()};
591 for (size_t i = 0; i < arraysize(kStringTypes); i++) {
592 Node* p0 = R.Parameter(kStringTypes[i], 0);
593 for (size_t j = 0; j < arraysize(kStringTypes); j++) {
594 Node* p1 = R.Parameter(kStringTypes[j], 1);
596 for (size_t k = 0; k < arraysize(ops); k += 2) {
597 Node* cmp = R.Binop(ops[k], p0, p1);
598 Node* r = R.reduce(cmp);
600 R.CheckPureBinop(ops[k + 1], r);
602 // GreaterThan and GreaterThanOrEqual commute the inputs
603 // and use the LessThan and LessThanOrEqual operators.
604 CHECK_EQ(p1, r->InputAt(0));
605 CHECK_EQ(p0, r->InputAt(1));
607 CHECK_EQ(p0, r->InputAt(0));
608 CHECK_EQ(p1, r->InputAt(1));
616 static void CheckIsConvertedToNumber(Node* val, Node* converted) {
617 if (NodeProperties::GetBounds(val).upper->Is(Type::Number())) {
618 CHECK_EQ(val, converted);
619 } else if (NodeProperties::GetBounds(val).upper->Is(Type::Boolean())) {
620 CHECK_EQ(IrOpcode::kBooleanToNumber, converted->opcode());
621 CHECK_EQ(val, converted->InputAt(0));
623 if (converted->opcode() == IrOpcode::kNumberConstant) return;
624 CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
625 CHECK_EQ(val, converted->InputAt(0));
630 TEST(NumberComparison) {
631 JSTypedLoweringTester R;
633 const Operator* ops[] = {
634 R.javascript.LessThan(), R.simplified.NumberLessThan(),
635 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
636 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
637 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
639 Node* const p0 = R.Parameter(Type::Number(), 0);
640 Node* const p1 = R.Parameter(Type::Number(), 1);
642 for (size_t k = 0; k < arraysize(ops); k += 2) {
643 Node* cmp = R.Binop(ops[k], p0, p1);
644 Node* r = R.reduce(cmp);
646 R.CheckPureBinop(ops[k + 1], r);
648 // GreaterThan and GreaterThanOrEqual commute the inputs
649 // and use the LessThan and LessThanOrEqual operators.
650 CheckIsConvertedToNumber(p1, r->InputAt(0));
651 CheckIsConvertedToNumber(p0, r->InputAt(1));
653 CheckIsConvertedToNumber(p0, r->InputAt(0));
654 CheckIsConvertedToNumber(p1, r->InputAt(1));
660 TEST(MixedComparison1) {
661 JSTypedLoweringTester R;
663 Type* types[] = {Type::Number(), Type::String(),
664 Type::Union(Type::Number(), Type::String(), R.main_zone())};
666 for (size_t i = 0; i < arraysize(types); i++) {
667 Node* p0 = R.Parameter(types[i], 0);
669 for (size_t j = 0; j < arraysize(types); j++) {
670 Node* p1 = R.Parameter(types[j], 1);
672 Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
673 Node* r = R.reduce(cmp);
675 if (!types[i]->Maybe(Type::String()) ||
676 !types[j]->Maybe(Type::String())) {
677 if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
678 R.CheckPureBinop(R.simplified.StringLessThan(), r);
680 R.CheckPureBinop(R.simplified.NumberLessThan(), r);
683 CHECK_EQ(cmp, r); // No reduction of mixed types.
691 TEST(RemoveToNumberEffects) {
692 FLAG_turbo_deoptimization = true;
694 JSTypedLoweringTester R;
696 Node* effect_use = NULL;
697 for (int i = 0; i < 10; i++) {
698 Node* p0 = R.Parameter(Type::Number());
699 Node* ton = R.Unop(R.javascript.ToNumber(), p0);
700 Node* frame_state = R.EmptyFrameState(R.context());
705 if (FLAG_turbo_deoptimization) {
706 DCHECK(OperatorProperties::GetFrameStateInputCount(
707 R.javascript.ToNumber()) == 1);
708 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
709 frame_state, ton, R.start());
711 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
716 if (FLAG_turbo_deoptimization) {
717 DCHECK(OperatorProperties::GetFrameStateInputCount(
718 R.javascript.ToNumber()) == 1);
720 R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
721 frame_state, ton, R.start());
723 effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton,
724 R.context(), ton, R.start());
728 effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
730 effect_use = R.graph.NewNode(R.javascript.Add(), ton, ton, R.context(),
731 frame_state, frame_state, ton, R.start());
734 effect_use = R.graph.NewNode(R.javascript.Add(), p0, p0, R.context(),
735 frame_state, frame_state, ton, R.start());
738 effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start());
741 effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start());
744 R.CheckEffectInput(R.start(), ton);
745 if (effect_use != NULL) R.CheckEffectInput(ton, effect_use);
747 Node* r = R.reduce(ton);
749 CHECK_NE(R.start(), r);
751 if (effect_use != NULL) {
752 R.CheckEffectInput(R.start(), effect_use);
753 // Check that value uses of ToNumber() do not go to start().
754 for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) {
755 CHECK_NE(R.start(), effect_use->InputAt(i));
760 CHECK(!effect_use); // should have done all cases above.
764 // Helper class for testing the reduction of a single binop.
765 class BinopEffectsTester {
767 explicit BinopEffectsTester(const Operator* op, Type* t0, Type* t1)
769 p0(R.Parameter(t0, 0)),
770 p1(R.Parameter(t1, 1)),
771 binop(R.Binop(op, p0, p1)),
772 effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
773 // Effects should be ordered start -> binop -> effect_use
774 R.CheckEffectInput(R.start(), binop);
775 R.CheckEffectInput(binop, effect_use);
776 result = R.reduce(binop);
779 JSTypedLoweringTester R;
786 void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
788 void CheckEffectOrdering(Node* n0) {
789 R.CheckEffectInput(R.start(), n0);
790 R.CheckEffectInput(n0, effect_use);
793 void CheckEffectOrdering(Node* n0, Node* n1) {
794 R.CheckEffectInput(R.start(), n0);
795 R.CheckEffectInput(n0, n1);
796 R.CheckEffectInput(n1, effect_use);
799 Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
800 return CheckConverted(opcode, result->InputAt(which), effects);
803 Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
804 CHECK_EQ(opcode, node->opcode());
806 CHECK_LT(0, node->op()->EffectInputCount());
808 CHECK_EQ(0, node->op()->EffectInputCount());
813 Node* CheckNoOp(int which) {
814 CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
815 return result->InputAt(which);
820 // Helper function for strict and non-strict equality reductions.
821 void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
822 Node* r, IrOpcode::Value expected) {
823 for (int j = 0; j < 2; j++) {
824 Node* p0 = j == 0 ? l : r;
825 Node* p1 = j == 1 ? l : r;
828 Node* eq = strict ? R->graph.NewNode(R->javascript.StrictEqual(), p0, p1)
829 : R->Binop(R->javascript.Equal(), p0, p1);
830 Node* r = R->reduce(eq);
831 R->CheckPureBinop(expected, r);
836 ? R->graph.NewNode(R->javascript.StrictNotEqual(), p0, p1)
837 : R->Binop(R->javascript.NotEqual(), p0, p1);
838 Node* n = R->reduce(ne);
839 CHECK_EQ(IrOpcode::kBooleanNot, n->opcode());
840 Node* r = n->InputAt(0);
841 R->CheckPureBinop(expected, r);
847 TEST(EqualityForNumbers) {
848 JSTypedLoweringTester R;
850 Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
851 Type::Signed32(), Type::Unsigned32(),
855 for (size_t i = 0; i < arraysize(simple_number_types); ++i) {
856 Node* p0 = R.Parameter(simple_number_types[i], 0);
858 for (size_t j = 0; j < arraysize(simple_number_types); ++j) {
859 Node* p1 = R.Parameter(simple_number_types[j], 1);
861 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
862 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
868 TEST(StrictEqualityForRefEqualTypes) {
869 JSTypedLoweringTester R;
871 Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
872 Type::Object(), Type::Receiver()};
874 Node* p0 = R.Parameter(Type::Any());
875 for (size_t i = 0; i < arraysize(types); i++) {
876 Node* p1 = R.Parameter(types[i]);
877 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
879 // TODO(titzer): Equal(RefEqualTypes)
883 TEST(StringEquality) {
884 JSTypedLoweringTester R;
885 Node* p0 = R.Parameter(Type::String());
886 Node* p1 = R.Parameter(Type::String());
888 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
889 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
893 TEST(RemovePureNumberBinopEffects) {
894 JSTypedLoweringTester R;
896 const Operator* ops[] = {
897 R.javascript.Equal(), R.simplified.NumberEqual(),
898 R.javascript.Add(), R.simplified.NumberAdd(),
899 R.javascript.Subtract(), R.simplified.NumberSubtract(),
900 R.javascript.Multiply(), R.simplified.NumberMultiply(),
901 R.javascript.Divide(), R.simplified.NumberDivide(),
902 R.javascript.Modulus(), R.simplified.NumberModulus(),
903 R.javascript.LessThan(), R.simplified.NumberLessThan(),
904 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
907 for (size_t j = 0; j < arraysize(ops); j += 2) {
908 BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
909 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
911 B.R.CheckPureBinop(B.result->opcode(), B.result);
916 B.CheckEffectsRemoved();
921 TEST(OrderNumberBinopEffects1) {
922 JSTypedLoweringTester R;
924 const Operator* ops[] = {
925 R.javascript.Subtract(), R.simplified.NumberSubtract(),
926 R.javascript.Multiply(), R.simplified.NumberMultiply(),
927 R.javascript.Divide(), R.simplified.NumberDivide(),
928 R.javascript.Modulus(), R.simplified.NumberModulus(),
931 for (size_t j = 0; j < arraysize(ops); j += 2) {
932 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol());
933 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
935 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
936 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
938 CHECK_EQ(B.p0, i0->InputAt(0));
939 CHECK_EQ(B.p1, i1->InputAt(0));
941 // Effects should be ordered start -> i0 -> i1 -> effect_use
942 B.CheckEffectOrdering(i0, i1);
947 TEST(OrderNumberBinopEffects2) {
948 JSTypedLoweringTester R;
950 const Operator* ops[] = {
951 R.javascript.Add(), R.simplified.NumberAdd(),
952 R.javascript.Subtract(), R.simplified.NumberSubtract(),
953 R.javascript.Multiply(), R.simplified.NumberMultiply(),
954 R.javascript.Divide(), R.simplified.NumberDivide(),
955 R.javascript.Modulus(), R.simplified.NumberModulus(),
958 for (size_t j = 0; j < arraysize(ops); j += 2) {
959 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
961 Node* i0 = B.CheckNoOp(0);
962 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
965 CHECK_EQ(B.p1, i1->InputAt(0));
967 // Effects should be ordered start -> i1 -> effect_use
968 B.CheckEffectOrdering(i1);
971 for (size_t j = 0; j < arraysize(ops); j += 2) {
972 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
974 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
975 Node* i1 = B.CheckNoOp(1);
977 CHECK_EQ(B.p0, i0->InputAt(0));
980 // Effects should be ordered start -> i0 -> effect_use
981 B.CheckEffectOrdering(i0);
986 TEST(OrderCompareEffects) {
987 JSTypedLoweringTester R;
989 const Operator* ops[] = {
990 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
991 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
994 for (size_t j = 0; j < arraysize(ops); j += 2) {
995 BinopEffectsTester B(ops[j], Type::Symbol(), Type::String());
996 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
998 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
999 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1001 // Inputs should be commuted.
1002 CHECK_EQ(B.p1, i0->InputAt(0));
1003 CHECK_EQ(B.p0, i1->InputAt(0));
1005 // But effects should be ordered start -> i1 -> effect_use
1006 B.CheckEffectOrdering(i1);
1009 for (size_t j = 0; j < arraysize(ops); j += 2) {
1010 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
1012 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1013 Node* i1 = B.result->InputAt(1);
1015 CHECK_EQ(B.p1, i0->InputAt(0)); // Should be commuted.
1018 // Effects should be ordered start -> i1 -> effect_use
1019 B.CheckEffectOrdering(i0);
1022 for (size_t j = 0; j < arraysize(ops); j += 2) {
1023 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
1025 Node* i0 = B.result->InputAt(0);
1026 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1028 CHECK_EQ(B.p1, i0); // Should be commuted.
1029 CHECK_EQ(B.p0, i1->InputAt(0));
1031 // Effects should be ordered start -> i0 -> effect_use
1032 B.CheckEffectOrdering(i1);
1037 TEST(Int32BinopEffects) {
1038 JSBitwiseTypedLoweringTester R;
1040 for (int j = 0; j < R.kNumberOps; j += 2) {
1041 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1042 BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
1043 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1045 B.R.CheckPureBinop(B.result->opcode(), B.result);
1050 B.CheckEffectsRemoved();
1053 for (int j = 0; j < R.kNumberOps; j += 2) {
1054 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1055 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number());
1056 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1058 B.R.CheckPureBinop(B.result->opcode(), B.result);
1060 B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1061 B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1063 B.CheckEffectsRemoved();
1066 for (int j = 0; j < R.kNumberOps; j += 2) {
1067 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1068 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Primitive());
1070 B.R.CheckPureBinop(B.result->opcode(), B.result);
1072 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1073 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1075 CHECK_EQ(B.p0, i0->InputAt(0));
1076 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1078 CHECK_EQ(B.p1, ii1->InputAt(0));
1080 B.CheckEffectOrdering(ii1);
1083 for (int j = 0; j < R.kNumberOps; j += 2) {
1084 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1085 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Number());
1087 B.R.CheckPureBinop(B.result->opcode(), B.result);
1089 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1090 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1092 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1093 CHECK_EQ(B.p1, i1->InputAt(0));
1095 CHECK_EQ(B.p0, ii0->InputAt(0));
1097 B.CheckEffectOrdering(ii0);
1100 for (int j = 0; j < R.kNumberOps; j += 2) {
1101 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1102 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Primitive());
1104 B.R.CheckPureBinop(B.result->opcode(), B.result);
1106 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1107 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1109 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1110 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1112 CHECK_EQ(B.p0, ii0->InputAt(0));
1113 CHECK_EQ(B.p1, ii1->InputAt(0));
1115 B.CheckEffectOrdering(ii0, ii1);
1120 TEST(Int32AddNarrowing) {
1122 JSBitwiseTypedLoweringTester R;
1124 for (int o = 0; o < R.kNumberOps; o += 2) {
1125 for (size_t i = 0; i < arraysize(kInt32Types); i++) {
1126 Node* n0 = R.Parameter(kInt32Types[i]);
1127 for (size_t j = 0; j < arraysize(kInt32Types); j++) {
1128 Node* n1 = R.Parameter(kInt32Types[j]);
1129 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1131 for (int l = 0; l < 2; l++) {
1132 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1134 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1135 Node* r = R.reduce(or_node);
1137 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1138 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1145 JSBitwiseShiftTypedLoweringTester R;
1147 for (int o = 0; o < R.kNumberOps; o += 2) {
1148 for (size_t i = 0; i < arraysize(kInt32Types); i++) {
1149 Node* n0 = R.Parameter(kInt32Types[i]);
1150 for (size_t j = 0; j < arraysize(kInt32Types); j++) {
1151 Node* n1 = R.Parameter(kInt32Types[j]);
1152 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1154 for (int l = 0; l < 2; l++) {
1155 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1157 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1158 Node* r = R.reduce(or_node);
1160 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1161 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1168 JSBitwiseTypedLoweringTester R;
1170 for (int o = 0; o < R.kNumberOps; o += 2) {
1171 Node* n0 = R.Parameter(I32Type(R.signedness[o]));
1172 Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
1173 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1175 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1176 Node* or_node = R.Binop(R.ops[o], add_node, one);
1177 Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
1178 Node* r = R.reduce(or_node);
1179 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1180 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1181 // Conversion to int32 should be done.
1182 CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
1183 CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
1184 // The other use should also not be touched.
1185 CHECK_EQ(add_node, other_use->InputAt(0));
1186 CHECK_EQ(one, other_use->InputAt(1));
1192 TEST(Int32Comparisons) {
1193 JSTypedLoweringTester R;
1196 const Operator* js_op;
1197 const Operator* uint_op;
1198 const Operator* int_op;
1199 const Operator* num_op;
1204 {R.javascript.LessThan(), R.machine.Uint32LessThan(),
1205 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false},
1206 {R.javascript.LessThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1207 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1209 {R.javascript.GreaterThan(), R.machine.Uint32LessThan(),
1210 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true},
1211 {R.javascript.GreaterThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1212 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1215 for (size_t o = 0; o < arraysize(ops); o++) {
1216 for (size_t i = 0; i < arraysize(kNumberTypes); i++) {
1217 Type* t0 = kNumberTypes[i];
1218 Node* p0 = R.Parameter(t0, 0);
1220 for (size_t j = 0; j < arraysize(kNumberTypes); j++) {
1221 Type* t1 = kNumberTypes[j];
1222 Node* p1 = R.Parameter(t1, 1);
1224 Node* cmp = R.Binop(ops[o].js_op, p0, p1);
1225 Node* r = R.reduce(cmp);
1227 const Operator* expected;
1228 if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) {
1229 expected = ops[o].uint_op;
1230 } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) {
1231 expected = ops[o].int_op;
1233 expected = ops[o].num_op;
1235 R.CheckPureBinop(expected, r);
1236 if (ops[o].commute) {
1237 CHECK_EQ(p1, r->InputAt(0));
1238 CHECK_EQ(p0, r->InputAt(1));
1240 CHECK_EQ(p0, r->InputAt(0));
1241 CHECK_EQ(p1, r->InputAt(1));