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.
6 #include "test/cctest/cctest.h"
8 #include "src/compiler/graph-inl.h"
9 #include "src/compiler/js-typed-lowering.h"
10 #include "src/compiler/node-properties-inl.h"
11 #include "src/compiler/opcodes.h"
12 #include "src/compiler/typer.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()),
29 source_positions(&graph),
31 typer.DecorateGraph(&graph);
32 Node* s = graph.NewNode(common.Start(num_parameters));
39 JSOperatorBuilder javascript;
40 MachineOperatorBuilder machine;
41 SimplifiedOperatorBuilder simplified;
42 CommonOperatorBuilder common;
45 SourcePositionTable source_positions;
48 Node* Parameter(Type* t, int32_t index = 0) {
49 Node* n = graph.NewNode(common.Parameter(index), graph.start());
50 NodeProperties::SetBounds(n, Bounds(Type::None(), t));
54 Node* reduce(Node* node) {
55 JSGraph jsgraph(&graph, &common, &typer);
56 JSTypedLowering reducer(&jsgraph, &source_positions);
57 Reduction reduction = reducer.Reduce(node);
58 if (reduction.Changed()) return reduction.replacement();
62 Node* start() { return graph.start(); }
65 if (context_node == NULL) {
66 context_node = graph.NewNode(common.Parameter(-1), graph.start());
71 Node* control() { return start(); }
73 void CheckPureBinop(IrOpcode::Value expected, Node* node) {
74 CHECK_EQ(expected, node->opcode());
75 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
78 void CheckPureBinop(Operator* expected, Node* node) {
79 CHECK_EQ(expected->opcode(), node->op()->opcode());
80 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
83 Node* ReduceUnop(Operator* op, Type* input_type) {
84 return reduce(Unop(op, Parameter(input_type)));
87 Node* ReduceBinop(Operator* op, Type* left_type, Type* right_type) {
88 return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
91 Node* Binop(Operator* op, Node* left, Node* right) {
92 // JS binops also require context, effect, and control
93 return graph.NewNode(op, left, right, context(), start(), control());
96 Node* Unop(Operator* op, Node* input) {
97 // JS unops also require context, effect, and control
98 return graph.NewNode(op, input, context(), start(), control());
101 Node* UseForEffect(Node* node) {
102 // TODO(titzer): use EffectPhi after fixing EffectCount
103 return graph.NewNode(javascript.ToNumber(), node, context(), node,
107 void CheckEffectInput(Node* effect, Node* use) {
108 CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
111 void CheckInt32Constant(int32_t expected, Node* result) {
112 CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
113 CHECK_EQ(expected, ValueOf<int32_t>(result->op()));
116 void CheckNumberConstant(double expected, Node* result) {
117 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
118 CHECK_EQ(expected, ValueOf<double>(result->op()));
121 void CheckNaN(Node* result) {
122 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
123 double value = ValueOf<double>(result->op());
124 CHECK(std::isnan(value));
127 void CheckTrue(Node* result) {
128 CheckHandle(isolate->factory()->true_value(), result);
131 void CheckFalse(Node* result) {
132 CheckHandle(isolate->factory()->false_value(), result);
135 void CheckHandle(Handle<Object> expected, Node* result) {
136 CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
137 Handle<Object> value = ValueOf<Handle<Object> >(result->op());
138 CHECK_EQ(*expected, *value);
142 static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
146 static Type* kInt32Types[] = {
147 Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
148 Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
149 Type::Signed32(), Type::Unsigned32(), Type::Integral32()};
152 static Type* kNumberTypes[] = {
153 Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
154 Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
155 Type::Signed32(), Type::Unsigned32(), Type::Integral32(),
156 Type::MinusZero(), Type::NaN(), Type::OtherNumber(),
160 static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
161 Type::Number(), Type::String(), Type::Object()};
164 static Type* I32Type(bool is_signed) {
165 return is_signed ? Type::Signed32() : Type::Unsigned32();
169 static IrOpcode::Value NumberToI32(bool is_signed) {
170 return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
175 JSTypedLoweringTester R;
177 for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); ++i) {
178 Node* p0 = R.Parameter(kStringTypes[i], 0);
180 for (size_t j = 0; j < ARRAY_SIZE(kStringTypes); ++j) {
181 Node* p1 = R.Parameter(kStringTypes[j], 1);
183 Node* add = R.Binop(R.javascript.Add(), p0, p1);
184 Node* r = R.reduce(add);
186 R.CheckPureBinop(IrOpcode::kStringAdd, r);
187 CHECK_EQ(p0, r->InputAt(0));
188 CHECK_EQ(p1, r->InputAt(1));
195 JSTypedLoweringTester R;
196 for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); ++i) {
197 Node* p0 = R.Parameter(kNumberTypes[i], 0);
198 Node* p1 = R.Parameter(kNumberTypes[i], 1);
199 Node* add = R.Binop(R.javascript.Add(), p0, p1);
200 Node* r = R.reduce(add);
202 R.CheckPureBinop(IrOpcode::kNumberAdd, r);
203 CHECK_EQ(p0, r->InputAt(0));
204 CHECK_EQ(p1, r->InputAt(1));
210 JSTypedLoweringTester R;
212 R.javascript.Add(), R.simplified.NumberAdd(),
213 R.javascript.Subtract(), R.simplified.NumberSubtract(),
214 R.javascript.Multiply(), R.simplified.NumberMultiply(),
215 R.javascript.Divide(), R.simplified.NumberDivide(),
216 R.javascript.Modulus(), R.simplified.NumberModulus(),
219 for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); ++i) {
220 Node* p0 = R.Parameter(kNumberTypes[i], 0);
222 for (size_t j = 0; j < ARRAY_SIZE(kNumberTypes); ++j) {
223 Node* p1 = R.Parameter(kNumberTypes[j], 1);
225 for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
226 Node* add = R.Binop(ops[k], p0, p1);
227 Node* r = R.reduce(add);
229 R.CheckPureBinop(ops[k + 1], r);
230 CHECK_EQ(p0, r->InputAt(0));
231 CHECK_EQ(p1, r->InputAt(1));
238 static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
239 Type* old_type = NodeProperties::GetBounds(old_input).upper;
240 Type* expected_type = I32Type(is_signed);
241 if (old_type->Is(expected_type)) {
242 CHECK_EQ(old_input, new_input);
243 } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
244 CHECK(NodeProperties::GetBounds(new_input).upper->Is(expected_type));
245 double v = ValueOf<double>(new_input->op());
246 double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
249 CHECK_EQ(NumberToI32(is_signed), new_input->opcode());
254 // A helper class for testing lowering of bitwise shift operators.
255 class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
257 static const int kNumberOps = 6;
258 Operator* ops[kNumberOps];
259 bool signedness[kNumberOps];
261 JSBitwiseShiftTypedLoweringTester() {
263 set(i++, javascript.ShiftLeft(), true);
264 set(i++, machine.Word32Shl(), false);
265 set(i++, javascript.ShiftRight(), true);
266 set(i++, machine.Word32Sar(), false);
267 set(i++, javascript.ShiftRightLogical(), false);
268 set(i++, machine.Word32Shr(), false);
272 void set(int idx, Operator* op, bool s) {
279 TEST(Int32BitwiseShifts) {
280 JSBitwiseShiftTypedLoweringTester R;
283 Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
284 Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
285 Type::NaN(), Type::OtherNumber(), Type::Undefined(),
286 Type::Null(), Type::Boolean(), Type::Number(),
287 Type::String(), Type::Object()};
289 for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
290 Node* p0 = R.Parameter(types[i], 0);
292 for (size_t j = 0; j < ARRAY_SIZE(types); ++j) {
293 Node* p1 = R.Parameter(types[j], 1);
295 for (int k = 0; k < R.kNumberOps; k += 2) {
296 Node* add = R.Binop(R.ops[k], p0, p1);
297 Node* r = R.reduce(add);
299 R.CheckPureBinop(R.ops[k + 1], r);
300 Node* r0 = r->InputAt(0);
301 Node* r1 = r->InputAt(1);
303 CheckToI32(p0, r0, R.signedness[k]);
305 R.CheckPureBinop(IrOpcode::kWord32And, r1);
306 CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
307 R.CheckInt32Constant(0x1F, r1->InputAt(1));
314 // A helper class for testing lowering of bitwise operators.
315 class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
317 static const int kNumberOps = 6;
318 Operator* ops[kNumberOps];
319 bool signedness[kNumberOps];
321 JSBitwiseTypedLoweringTester() {
323 set(i++, javascript.BitwiseOr(), true);
324 set(i++, machine.Word32Or(), true);
325 set(i++, javascript.BitwiseXor(), true);
326 set(i++, machine.Word32Xor(), true);
327 set(i++, javascript.BitwiseAnd(), true);
328 set(i++, machine.Word32And(), true);
332 void set(int idx, Operator* op, bool s) {
339 TEST(Int32BitwiseBinops) {
340 JSBitwiseTypedLoweringTester R;
343 Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
344 Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
345 Type::NaN(), Type::OtherNumber(), Type::Undefined(),
346 Type::Null(), Type::Boolean(), Type::Number(),
347 Type::String(), Type::Object()};
349 for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
350 Node* p0 = R.Parameter(types[i], 0);
352 for (size_t j = 0; j < ARRAY_SIZE(types); ++j) {
353 Node* p1 = R.Parameter(types[j], 1);
355 for (int k = 0; k < R.kNumberOps; k += 2) {
356 Node* add = R.Binop(R.ops[k], p0, p1);
357 Node* r = R.reduce(add);
359 R.CheckPureBinop(R.ops[k + 1], r);
361 CheckToI32(p0, r->InputAt(0), R.signedness[k]);
362 CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
370 JSTypedLoweringTester R;
371 Operator* ton = R.javascript.ToNumber();
373 for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); i++) { // ToNumber(number)
374 Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
375 CHECK_EQ(IrOpcode::kParameter, r->opcode());
378 { // ToNumber(undefined)
379 Node* r = R.ReduceUnop(ton, Type::Undefined());
384 Node* r = R.ReduceUnop(ton, Type::Null());
385 R.CheckNumberConstant(0.0, r);
390 TEST(JSToNumber_replacement) {
391 JSTypedLoweringTester R;
393 Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()};
395 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
396 Node* n = R.Parameter(types[i]);
397 Node* c = R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
398 R.start(), R.start());
399 Node* effect_use = R.UseForEffect(c);
400 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
402 R.CheckEffectInput(c, effect_use);
403 Node* r = R.reduce(c);
405 if (types[i]->Is(Type::Number())) {
408 CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
411 CHECK_EQ(n, add->InputAt(0));
412 CHECK_EQ(r, add->InputAt(1));
413 R.CheckEffectInput(R.start(), effect_use);
418 TEST(JSToNumberOfConstant) {
419 JSTypedLoweringTester R;
421 Operator* ops[] = {R.common.NumberConstant(0), R.common.NumberConstant(-1),
422 R.common.NumberConstant(0.1), R.common.Int32Constant(1177),
423 R.common.Float64Constant(0.99)};
425 for (size_t i = 0; i < ARRAY_SIZE(ops); i++) {
426 Node* n = R.graph.NewNode(ops[i]);
427 Node* convert = R.Unop(R.javascript.ToNumber(), n);
428 Node* r = R.reduce(convert);
429 // Note that either outcome below is correct. It only depends on whether
430 // the types of constants are eagerly computed or only computed by the
432 if (NodeProperties::GetBounds(n).upper->Is(Type::Number())) {
433 // If number constants are eagerly typed, then reduction should
434 // remove the ToNumber.
437 // Otherwise, type-based lowering should only look at the type, and
438 // *not* try to constant fold.
439 CHECK_EQ(convert, r);
445 TEST(JSToNumberOfNumberOrOtherPrimitive) {
446 JSTypedLoweringTester R;
447 Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
450 for (size_t i = 0; i < ARRAY_SIZE(others); i++) {
451 Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
452 Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
453 CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
459 JSTypedLoweringTester R;
460 Operator* op = R.javascript.ToBoolean();
462 { // ToBoolean(undefined)
463 Node* r = R.ReduceUnop(op, Type::Undefined());
468 Node* r = R.ReduceUnop(op, Type::Null());
472 { // ToBoolean(boolean)
473 Node* r = R.ReduceUnop(op, Type::Boolean());
474 CHECK_EQ(IrOpcode::kParameter, r->opcode());
477 { // ToBoolean(number)
478 Node* r = R.ReduceUnop(op, Type::Number());
479 CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
480 Node* i = r->InputAt(0);
481 CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
482 // ToBoolean(number) => BooleanNot(NumberEqual(x, #0))
485 { // ToBoolean(string)
486 Node* r = R.ReduceUnop(op, Type::String());
487 // TODO(titzer): test will break with better js-typed-lowering
488 CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
491 { // ToBoolean(object)
492 Node* r = R.ReduceUnop(op, Type::DetectableObject());
496 { // ToBoolean(undetectable)
497 Node* r = R.ReduceUnop(op, Type::Undetectable());
501 { // ToBoolean(object)
502 Node* r = R.ReduceUnop(op, Type::Object());
503 CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
508 TEST(JSToBoolean_replacement) {
509 JSTypedLoweringTester R;
511 Type* types[] = {Type::Null(), Type::Undefined(), Type::Boolean(),
512 Type::DetectableObject(), Type::Undetectable()};
514 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
515 Node* n = R.Parameter(types[i]);
516 Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context(),
517 R.start(), R.start());
518 Node* effect_use = R.UseForEffect(c);
519 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
521 R.CheckEffectInput(c, effect_use);
522 Node* r = R.reduce(c);
524 if (types[i]->Is(Type::Boolean())) {
527 CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
530 CHECK_EQ(n, add->InputAt(0));
531 CHECK_EQ(r, add->InputAt(1));
532 R.CheckEffectInput(R.start(), effect_use);
538 JSTypedLoweringTester R;
540 for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); i++) {
541 Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
542 CHECK_EQ(IrOpcode::kParameter, r->opcode());
545 Operator* op = R.javascript.ToString();
547 { // ToString(undefined) => "undefined"
548 Node* r = R.ReduceUnop(op, Type::Undefined());
549 R.CheckHandle(R.isolate->factory()->undefined_string(), r);
552 { // ToString(null) => "null"
553 Node* r = R.ReduceUnop(op, Type::Null());
554 R.CheckHandle(R.isolate->factory()->null_string(), r);
557 { // ToString(boolean)
558 Node* r = R.ReduceUnop(op, Type::Boolean());
559 // TODO(titzer): could be a branch
560 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
563 { // ToString(number)
564 Node* r = R.ReduceUnop(op, Type::Number());
565 // TODO(titzer): could remove effects
566 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
569 { // ToString(string)
570 Node* r = R.ReduceUnop(op, Type::String());
571 CHECK_EQ(IrOpcode::kParameter, r->opcode()); // No-op
574 { // ToString(object)
575 Node* r = R.ReduceUnop(op, Type::Object());
576 CHECK_EQ(IrOpcode::kJSToString, r->opcode()); // No reduction.
581 TEST(JSToString_replacement) {
582 JSTypedLoweringTester R;
584 Type* types[] = {Type::Null(), Type::Undefined(), Type::String()};
586 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
587 Node* n = R.Parameter(types[i]);
588 Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
589 R.start(), R.start());
590 Node* effect_use = R.UseForEffect(c);
591 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
593 R.CheckEffectInput(c, effect_use);
594 Node* r = R.reduce(c);
596 if (types[i]->Is(Type::String())) {
599 CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
602 CHECK_EQ(n, add->InputAt(0));
603 CHECK_EQ(r, add->InputAt(1));
604 R.CheckEffectInput(R.start(), effect_use);
609 TEST(StringComparison) {
610 JSTypedLoweringTester R;
613 R.javascript.LessThan(), R.simplified.StringLessThan(),
614 R.javascript.LessThanOrEqual(), R.simplified.StringLessThanOrEqual(),
615 R.javascript.GreaterThan(), R.simplified.StringLessThan(),
616 R.javascript.GreaterThanOrEqual(), R.simplified.StringLessThanOrEqual()};
618 for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); i++) {
619 Node* p0 = R.Parameter(kStringTypes[i], 0);
620 for (size_t j = 0; j < ARRAY_SIZE(kStringTypes); j++) {
621 Node* p1 = R.Parameter(kStringTypes[j], 1);
623 for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
624 Node* cmp = R.Binop(ops[k], p0, p1);
625 Node* r = R.reduce(cmp);
627 R.CheckPureBinop(ops[k + 1], r);
629 // GreaterThan and GreaterThanOrEqual commute the inputs
630 // and use the LessThan and LessThanOrEqual operators.
631 CHECK_EQ(p1, r->InputAt(0));
632 CHECK_EQ(p0, r->InputAt(1));
634 CHECK_EQ(p0, r->InputAt(0));
635 CHECK_EQ(p1, r->InputAt(1));
643 static void CheckIsConvertedToNumber(Node* val, Node* converted) {
644 if (NodeProperties::GetBounds(val).upper->Is(Type::Number())) {
645 CHECK_EQ(val, converted);
647 if (converted->opcode() == IrOpcode::kNumberConstant) return;
648 CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
649 CHECK_EQ(val, converted->InputAt(0));
654 TEST(NumberComparison) {
655 JSTypedLoweringTester R;
658 R.javascript.LessThan(), R.simplified.NumberLessThan(),
659 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
660 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
661 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
663 for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
664 Type* t0 = kJSTypes[i];
665 if (t0->Is(Type::String())) continue; // skip Type::String
666 Node* p0 = R.Parameter(t0, 0);
668 for (size_t j = 0; j < ARRAY_SIZE(kJSTypes); j++) {
669 Type* t1 = kJSTypes[j];
670 if (t1->Is(Type::String())) continue; // skip Type::String
671 Node* p1 = R.Parameter(t1, 1);
673 for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
674 Node* cmp = R.Binop(ops[k], p0, p1);
675 Node* r = R.reduce(cmp);
677 R.CheckPureBinop(ops[k + 1], r);
679 // GreaterThan and GreaterThanOrEqual commute the inputs
680 // and use the LessThan and LessThanOrEqual operators.
681 CheckIsConvertedToNumber(p1, r->InputAt(0));
682 CheckIsConvertedToNumber(p0, r->InputAt(1));
684 CheckIsConvertedToNumber(p0, r->InputAt(0));
685 CheckIsConvertedToNumber(p1, r->InputAt(1));
693 TEST(MixedComparison1) {
694 JSTypedLoweringTester R;
696 Type* types[] = {Type::Number(), Type::String(),
697 Type::Union(Type::Number(), Type::String(), R.main_zone())};
699 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
700 Node* p0 = R.Parameter(types[i], 0);
702 for (size_t j = 0; j < ARRAY_SIZE(types); j++) {
703 Node* p1 = R.Parameter(types[j], 1);
705 Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
706 Node* r = R.reduce(cmp);
708 if (!types[i]->Maybe(Type::String()) ||
709 !types[j]->Maybe(Type::String())) {
710 if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
711 R.CheckPureBinop(R.simplified.StringLessThan(), r);
713 R.CheckPureBinop(R.simplified.NumberLessThan(), r);
716 CHECK_EQ(cmp, r); // No reduction of mixed types.
724 TEST(ObjectComparison) {
725 JSTypedLoweringTester R;
727 Node* p0 = R.Parameter(Type::Object(), 0);
728 Node* p1 = R.Parameter(Type::Object(), 1);
730 Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
731 Node* effect_use = R.UseForEffect(cmp);
733 R.CheckEffectInput(R.start(), cmp);
734 R.CheckEffectInput(cmp, effect_use);
736 Node* r = R.reduce(cmp);
738 R.CheckPureBinop(R.simplified.NumberLessThan(), r);
740 Node* i0 = r->InputAt(0);
741 Node* i1 = r->InputAt(1);
745 CHECK_EQ(IrOpcode::kJSToNumber, i0->opcode());
746 CHECK_EQ(IrOpcode::kJSToNumber, i1->opcode());
748 // Check effect chain is correct.
749 R.CheckEffectInput(R.start(), i0);
750 R.CheckEffectInput(i0, i1);
751 R.CheckEffectInput(i1, effect_use);
756 JSTypedLoweringTester R;
757 Operator* opnot = R.javascript.UnaryNot();
759 for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
760 Node* r = R.ReduceUnop(opnot, kJSTypes[i]);
761 // TODO(titzer): test will break if/when js-typed-lowering constant folds.
762 CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
767 TEST(RemoveToNumberEffects) {
768 JSTypedLoweringTester R;
770 Node* effect_use = NULL;
771 for (int i = 0; i < 10; i++) {
772 Node* p0 = R.Parameter(Type::Number());
773 Node* ton = R.Unop(R.javascript.ToNumber(), p0);
778 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
782 effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
786 effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
788 effect_use = R.graph.NewNode(R.javascript.Add(), ton, ton, R.context(),
792 effect_use = R.graph.NewNode(R.javascript.Add(), p0, p0, R.context(),
796 effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start());
799 effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start());
802 R.CheckEffectInput(R.start(), ton);
803 if (effect_use != NULL) R.CheckEffectInput(ton, effect_use);
805 Node* r = R.reduce(ton);
807 CHECK_NE(R.start(), r);
809 if (effect_use != NULL) {
810 R.CheckEffectInput(R.start(), effect_use);
811 // Check that value uses of ToNumber() do not go to start().
812 for (int i = 0; i < effect_use->op()->InputCount(); i++) {
813 CHECK_NE(R.start(), effect_use->InputAt(i));
818 CHECK_EQ(NULL, effect_use); // should have done all cases above.
822 // Helper class for testing the reduction of a single binop.
823 class BinopEffectsTester {
825 explicit BinopEffectsTester(Operator* op, Type* t0, Type* t1)
827 p0(R.Parameter(t0, 0)),
828 p1(R.Parameter(t1, 1)),
829 binop(R.Binop(op, p0, p1)),
830 effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
831 // Effects should be ordered start -> binop -> effect_use
832 R.CheckEffectInput(R.start(), binop);
833 R.CheckEffectInput(binop, effect_use);
834 result = R.reduce(binop);
837 JSTypedLoweringTester R;
844 void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
846 void CheckEffectOrdering(Node* n0) {
847 R.CheckEffectInput(R.start(), n0);
848 R.CheckEffectInput(n0, effect_use);
851 void CheckEffectOrdering(Node* n0, Node* n1) {
852 R.CheckEffectInput(R.start(), n0);
853 R.CheckEffectInput(n0, n1);
854 R.CheckEffectInput(n1, effect_use);
857 Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
858 return CheckConverted(opcode, result->InputAt(which), effects);
861 Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
862 CHECK_EQ(opcode, node->opcode());
864 CHECK_LT(0, OperatorProperties::GetEffectInputCount(node->op()));
866 CHECK_EQ(0, OperatorProperties::GetEffectInputCount(node->op()));
871 Node* CheckNoOp(int which) {
872 CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
873 return result->InputAt(which);
878 // Helper function for strict and non-strict equality reductions.
879 void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
880 Node* r, IrOpcode::Value expected) {
881 for (int j = 0; j < 2; j++) {
882 Node* p0 = j == 0 ? l : r;
883 Node* p1 = j == 1 ? l : r;
886 Node* eq = strict ? R->graph.NewNode(R->javascript.StrictEqual(), p0, p1)
887 : R->Binop(R->javascript.Equal(), p0, p1);
888 Node* r = R->reduce(eq);
889 R->CheckPureBinop(expected, r);
894 ? R->graph.NewNode(R->javascript.StrictNotEqual(), p0, p1)
895 : R->Binop(R->javascript.NotEqual(), p0, p1);
896 Node* n = R->reduce(ne);
897 CHECK_EQ(IrOpcode::kBooleanNot, n->opcode());
898 Node* r = n->InputAt(0);
899 R->CheckPureBinop(expected, r);
905 TEST(EqualityForNumbers) {
906 JSTypedLoweringTester R;
908 Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
909 Type::Signed32(), Type::Unsigned32(),
913 for (size_t i = 0; i < ARRAY_SIZE(simple_number_types); ++i) {
914 Node* p0 = R.Parameter(simple_number_types[i], 0);
916 for (size_t j = 0; j < ARRAY_SIZE(simple_number_types); ++j) {
917 Node* p1 = R.Parameter(simple_number_types[j], 1);
919 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
920 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
926 TEST(StrictEqualityForRefEqualTypes) {
927 JSTypedLoweringTester R;
929 Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
930 Type::Object(), Type::Receiver()};
932 Node* p0 = R.Parameter(Type::Any());
933 for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
934 Node* p1 = R.Parameter(types[i]);
935 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
937 // TODO(titzer): Equal(RefEqualTypes)
941 TEST(StringEquality) {
942 JSTypedLoweringTester R;
943 Node* p0 = R.Parameter(Type::String());
944 Node* p1 = R.Parameter(Type::String());
946 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
947 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
951 TEST(RemovePureNumberBinopEffects) {
952 JSTypedLoweringTester R;
955 R.javascript.Equal(), R.simplified.NumberEqual(),
956 R.javascript.Add(), R.simplified.NumberAdd(),
957 R.javascript.Subtract(), R.simplified.NumberSubtract(),
958 R.javascript.Multiply(), R.simplified.NumberMultiply(),
959 R.javascript.Divide(), R.simplified.NumberDivide(),
960 R.javascript.Modulus(), R.simplified.NumberModulus(),
961 R.javascript.LessThan(), R.simplified.NumberLessThan(),
962 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
965 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
966 BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
967 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
969 B.R.CheckPureBinop(B.result->opcode(), B.result);
974 B.CheckEffectsRemoved();
979 TEST(OrderNumberBinopEffects1) {
980 JSTypedLoweringTester R;
983 R.javascript.Subtract(), R.simplified.NumberSubtract(),
984 R.javascript.Multiply(), R.simplified.NumberMultiply(),
985 R.javascript.Divide(), R.simplified.NumberDivide(),
986 R.javascript.Modulus(), R.simplified.NumberModulus(),
989 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
990 BinopEffectsTester B(ops[j], Type::Object(), Type::String());
991 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
993 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
994 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
996 CHECK_EQ(B.p0, i0->InputAt(0));
997 CHECK_EQ(B.p1, i1->InputAt(0));
999 // Effects should be ordered start -> i0 -> i1 -> effect_use
1000 B.CheckEffectOrdering(i0, i1);
1005 TEST(OrderNumberBinopEffects2) {
1006 JSTypedLoweringTester R;
1009 R.javascript.Add(), R.simplified.NumberAdd(),
1010 R.javascript.Subtract(), R.simplified.NumberSubtract(),
1011 R.javascript.Multiply(), R.simplified.NumberMultiply(),
1012 R.javascript.Divide(), R.simplified.NumberDivide(),
1013 R.javascript.Modulus(), R.simplified.NumberModulus(),
1016 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
1017 BinopEffectsTester B(ops[j], Type::Number(), Type::Object());
1019 Node* i0 = B.CheckNoOp(0);
1020 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1023 CHECK_EQ(B.p1, i1->InputAt(0));
1025 // Effects should be ordered start -> i1 -> effect_use
1026 B.CheckEffectOrdering(i1);
1029 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
1030 BinopEffectsTester B(ops[j], Type::Object(), Type::Number());
1032 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1033 Node* i1 = B.CheckNoOp(1);
1035 CHECK_EQ(B.p0, i0->InputAt(0));
1038 // Effects should be ordered start -> i0 -> effect_use
1039 B.CheckEffectOrdering(i0);
1044 TEST(OrderCompareEffects) {
1045 JSTypedLoweringTester R;
1048 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
1049 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1052 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
1053 BinopEffectsTester B(ops[j], Type::Object(), Type::String());
1054 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
1056 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1057 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1059 // Inputs should be commuted.
1060 CHECK_EQ(B.p1, i0->InputAt(0));
1061 CHECK_EQ(B.p0, i1->InputAt(0));
1063 // But effects should be ordered start -> i1 -> i0 -> effect_use
1064 B.CheckEffectOrdering(i1, i0);
1067 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
1068 BinopEffectsTester B(ops[j], Type::Number(), Type::Object());
1070 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1071 Node* i1 = B.result->InputAt(1);
1073 CHECK_EQ(B.p1, i0->InputAt(0)); // Should be commuted.
1076 // Effects should be ordered start -> i1 -> effect_use
1077 B.CheckEffectOrdering(i0);
1080 for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
1081 BinopEffectsTester B(ops[j], Type::Object(), Type::Number());
1083 Node* i0 = B.result->InputAt(0);
1084 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1086 CHECK_EQ(B.p1, i0); // Should be commuted.
1087 CHECK_EQ(B.p0, i1->InputAt(0));
1089 // Effects should be ordered start -> i0 -> effect_use
1090 B.CheckEffectOrdering(i1);
1095 TEST(Int32BinopEffects) {
1096 JSBitwiseTypedLoweringTester R;
1098 for (int j = 0; j < R.kNumberOps; j += 2) {
1099 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1100 BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
1101 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1103 B.R.CheckPureBinop(B.result->opcode(), B.result);
1108 B.CheckEffectsRemoved();
1111 for (int j = 0; j < R.kNumberOps; j += 2) {
1112 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1113 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number());
1114 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1116 B.R.CheckPureBinop(B.result->opcode(), B.result);
1118 B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1119 B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1121 B.CheckEffectsRemoved();
1124 for (int j = 0; j < R.kNumberOps; j += 2) {
1125 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1126 BinopEffectsTester B(R.ops[j], Type::Number(), Type::Object());
1128 B.R.CheckPureBinop(B.result->opcode(), B.result);
1130 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1131 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1133 CHECK_EQ(B.p0, i0->InputAt(0));
1134 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1136 CHECK_EQ(B.p1, ii1->InputAt(0));
1138 B.CheckEffectOrdering(ii1);
1141 for (int j = 0; j < R.kNumberOps; j += 2) {
1142 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1143 BinopEffectsTester B(R.ops[j], Type::Object(), Type::Number());
1145 B.R.CheckPureBinop(B.result->opcode(), B.result);
1147 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1148 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1150 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1151 CHECK_EQ(B.p1, i1->InputAt(0));
1153 CHECK_EQ(B.p0, ii0->InputAt(0));
1155 B.CheckEffectOrdering(ii0);
1158 for (int j = 0; j < R.kNumberOps; j += 2) {
1159 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1160 BinopEffectsTester B(R.ops[j], Type::Object(), Type::Object());
1162 B.R.CheckPureBinop(B.result->opcode(), B.result);
1164 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1165 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1167 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1168 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1170 CHECK_EQ(B.p0, ii0->InputAt(0));
1171 CHECK_EQ(B.p1, ii1->InputAt(0));
1173 B.CheckEffectOrdering(ii0, ii1);
1178 TEST(UnaryNotEffects) {
1179 JSTypedLoweringTester R;
1180 Operator* opnot = R.javascript.UnaryNot();
1182 for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
1183 Node* p0 = R.Parameter(kJSTypes[i], 0);
1184 Node* orig = R.Unop(opnot, p0);
1185 Node* effect_use = R.UseForEffect(orig);
1186 Node* value_use = R.graph.NewNode(R.common.Return(), orig);
1187 Node* r = R.reduce(orig);
1188 // TODO(titzer): test will break if/when js-typed-lowering constant folds.
1189 CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
1191 CHECK_EQ(r, value_use->InputAt(0));
1193 if (r->InputAt(0) == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
1194 // The original node was turned into a ToBoolean, which has an effect.
1195 R.CheckEffectInput(R.start(), orig);
1196 R.CheckEffectInput(orig, effect_use);
1198 // effect should have been removed from this node.
1199 R.CheckEffectInput(R.start(), effect_use);
1205 TEST(Int32AddNarrowing) {
1207 JSBitwiseTypedLoweringTester R;
1209 for (int o = 0; o < R.kNumberOps; o += 2) {
1210 for (size_t i = 0; i < ARRAY_SIZE(kInt32Types); i++) {
1211 Node* n0 = R.Parameter(kInt32Types[i]);
1212 for (size_t j = 0; j < ARRAY_SIZE(kInt32Types); j++) {
1213 Node* n1 = R.Parameter(kInt32Types[j]);
1214 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1216 for (int l = 0; l < 2; l++) {
1217 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1219 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1220 Node* r = R.reduce(or_node);
1222 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1223 CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
1224 bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
1226 Type* add_type = NodeProperties::GetBounds(add_node).upper;
1227 CHECK(add_type->Is(I32Type(is_signed)));
1234 JSBitwiseShiftTypedLoweringTester R;
1236 for (int o = 0; o < R.kNumberOps; o += 2) {
1237 for (size_t i = 0; i < ARRAY_SIZE(kInt32Types); i++) {
1238 Node* n0 = R.Parameter(kInt32Types[i]);
1239 for (size_t j = 0; j < ARRAY_SIZE(kInt32Types); j++) {
1240 Node* n1 = R.Parameter(kInt32Types[j]);
1241 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1243 for (int l = 0; l < 2; l++) {
1244 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1246 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1247 Node* r = R.reduce(or_node);
1249 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1250 CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
1251 bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
1253 Type* add_type = NodeProperties::GetBounds(add_node).upper;
1254 CHECK(add_type->Is(I32Type(is_signed)));
1263 TEST(Int32AddNarrowingNotOwned) {
1264 JSBitwiseTypedLoweringTester R;
1266 for (int o = 0; o < R.kNumberOps; o += 2) {
1267 Node* n0 = R.Parameter(I32Type(R.signedness[o]));
1268 Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
1269 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1271 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1272 Node* or_node = R.Binop(R.ops[o], add_node, one);
1273 Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
1274 Node* r = R.reduce(or_node);
1275 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1276 // Should not be reduced to Int32Add because of the other number add.
1277 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1278 // Conversion to int32 should be done.
1279 CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
1280 CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
1281 // The other use should also not be touched.
1282 CHECK_EQ(add_node, other_use->InputAt(0));
1283 CHECK_EQ(one, other_use->InputAt(1));
1288 TEST(Int32Comparisons) {
1289 JSTypedLoweringTester R;
1300 {R.javascript.LessThan(), R.machine.Uint32LessThan(),
1301 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false},
1302 {R.javascript.LessThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1303 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1305 {R.javascript.GreaterThan(), R.machine.Uint32LessThan(),
1306 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true},
1307 {R.javascript.GreaterThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1308 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1311 for (size_t o = 0; o < ARRAY_SIZE(ops); o++) {
1312 for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); i++) {
1313 Type* t0 = kNumberTypes[i];
1314 Node* p0 = R.Parameter(t0, 0);
1316 for (size_t j = 0; j < ARRAY_SIZE(kNumberTypes); j++) {
1317 Type* t1 = kNumberTypes[j];
1318 Node* p1 = R.Parameter(t1, 1);
1320 Node* cmp = R.Binop(ops[o].js_op, p0, p1);
1321 Node* r = R.reduce(cmp);
1324 if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) {
1325 expected = ops[o].uint_op;
1326 } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) {
1327 expected = ops[o].int_op;
1329 expected = ops[o].num_op;
1331 R.CheckPureBinop(expected, r);
1332 if (ops[o].commute) {
1333 CHECK_EQ(p1, r->InputAt(0));
1334 CHECK_EQ(p0, r->InputAt(1));
1336 CHECK_EQ(p0, r->InputAt(0));
1337 CHECK_EQ(p1, r->InputAt(1));