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/graph-inl.h"
6 #include "src/compiler/js-graph.h"
7 #include "src/compiler/js-typed-lowering.h"
8 #include "src/compiler/machine-operator.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/opcodes.h"
11 #include "src/compiler/operator-properties.h"
12 #include "src/compiler/typer.h"
13 #include "test/cctest/cctest.h"
15 using namespace v8::internal;
16 using namespace v8::internal::compiler;
18 class JSTypedLoweringTester : public HandleAndZoneScope {
20 explicit JSTypedLoweringTester(int num_parameters = 0)
21 : isolate(main_isolate()),
24 javascript(main_zone()),
26 simplified(main_zone()),
29 typer(main_isolate(), &graph, MaybeHandle<Context>()),
31 graph.SetStart(graph.NewNode(common.Start(num_parameters)));
32 graph.SetEnd(graph.NewNode(common.End()));
37 const Operator* binop;
39 JSOperatorBuilder javascript;
40 MachineOperatorBuilder machine;
41 SimplifiedOperatorBuilder simplified;
42 CommonOperatorBuilder common;
47 Node* Parameter(Type* t, int32_t index = 0) {
48 Node* n = graph.NewNode(common.Parameter(index), graph.start());
49 NodeProperties::SetBounds(n, Bounds(Type::None(), t));
53 Node* UndefinedConstant() {
54 Unique<HeapObject> unique = Unique<HeapObject>::CreateImmovable(
55 isolate->factory()->undefined_value());
56 return graph.NewNode(common.HeapConstant(unique));
59 Node* HeapConstant(Handle<HeapObject> constant) {
60 Unique<HeapObject> unique =
61 Unique<HeapObject>::CreateUninitialized(constant);
62 return graph.NewNode(common.HeapConstant(unique));
65 Node* EmptyFrameState(Node* context) {
66 Node* parameters = graph.NewNode(common.StateValues(0));
67 Node* locals = graph.NewNode(common.StateValues(0));
68 Node* stack = graph.NewNode(common.StateValues(0));
71 graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0),
72 OutputFrameStateCombine::Ignore()),
73 parameters, locals, stack, context, UndefinedConstant());
78 Node* reduce(Node* node) {
79 JSGraph jsgraph(main_isolate(), &graph, &common, &javascript, &machine);
80 JSTypedLowering reducer(&jsgraph, main_zone());
81 Reduction reduction = reducer.Reduce(node);
82 if (reduction.Changed()) return reduction.replacement();
86 Node* start() { return graph.start(); }
89 if (context_node == NULL) {
90 context_node = graph.NewNode(common.Parameter(-1), graph.start());
95 Node* control() { return start(); }
97 void CheckPureBinop(IrOpcode::Value expected, Node* node) {
98 CHECK_EQ(expected, node->opcode());
99 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
102 void CheckPureBinop(const Operator* expected, Node* node) {
103 CHECK_EQ(expected->opcode(), node->op()->opcode());
104 CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
107 Node* ReduceUnop(const Operator* op, Type* input_type) {
108 return reduce(Unop(op, Parameter(input_type)));
111 Node* ReduceBinop(const Operator* op, Type* left_type, Type* right_type) {
112 return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
115 Node* Binop(const Operator* op, Node* left, Node* right) {
116 // JS binops also require context, effect, and control
117 if (OperatorProperties::HasFrameStateInput(op)) {
118 return graph.NewNode(op, left, right, context(),
119 EmptyFrameState(context()), start(), control());
121 return graph.NewNode(op, left, right, context(), start(), control());
125 Node* Unop(const Operator* op, Node* input) {
126 // JS unops also require context, effect, and control
127 if (OperatorProperties::HasFrameStateInput(op)) {
128 return graph.NewNode(op, input, context(), EmptyFrameState(context()),
131 return graph.NewNode(op, input, context(), start(), control());
135 Node* UseForEffect(Node* node) {
136 // TODO(titzer): use EffectPhi after fixing EffectCount
137 if (OperatorProperties::HasFrameStateInput(javascript.ToNumber())) {
138 return graph.NewNode(javascript.ToNumber(), node, context(),
139 EmptyFrameState(context()), node, control());
141 return graph.NewNode(javascript.ToNumber(), node, context(), node,
146 void CheckEffectInput(Node* effect, Node* use) {
147 CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
150 void CheckInt32Constant(int32_t expected, Node* result) {
151 CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
152 CHECK_EQ(expected, OpParameter<int32_t>(result));
155 void CheckNumberConstant(double expected, Node* result) {
156 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
157 CHECK_EQ(expected, OpParameter<double>(result));
160 void CheckNaN(Node* result) {
161 CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
162 double value = OpParameter<double>(result);
163 CHECK(std::isnan(value));
166 void CheckTrue(Node* result) {
167 CheckHandle(isolate->factory()->true_value(), result);
170 void CheckFalse(Node* result) {
171 CheckHandle(isolate->factory()->false_value(), result);
174 void CheckHandle(Handle<Object> expected, Node* result) {
175 CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
176 Handle<Object> value = OpParameter<Unique<Object> >(result).handle();
177 CHECK_EQ(*expected, *value);
181 static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
185 static Type* kInt32Types[] = {Type::UnsignedSmall(), Type::Negative32(),
186 Type::Unsigned31(), Type::SignedSmall(),
187 Type::Signed32(), Type::Unsigned32(),
191 static Type* kNumberTypes[] = {
192 Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(),
193 Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(),
194 Type::Integral32(), Type::MinusZero(), Type::NaN(),
195 Type::OrderedNumber(), Type::PlainNumber(), Type::Number()};
198 static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
199 Type::Number(), Type::String(), Type::Object()};
202 static Type* I32Type(bool is_signed) {
203 return is_signed ? Type::Signed32() : Type::Unsigned32();
207 static IrOpcode::Value NumberToI32(bool is_signed) {
208 return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
212 // TODO(turbofan): Lowering of StringAdd is disabled for now.
215 JSTypedLoweringTester R;
217 for (size_t i = 0; i < arraysize(kStringTypes); ++i) {
218 Node* p0 = R.Parameter(kStringTypes[i], 0);
220 for (size_t j = 0; j < arraysize(kStringTypes); ++j) {
221 Node* p1 = R.Parameter(kStringTypes[j], 1);
223 Node* add = R.Binop(R.javascript.Add(), p0, p1);
224 Node* r = R.reduce(add);
226 R.CheckPureBinop(IrOpcode::kStringAdd, r);
227 CHECK_EQ(p0, r->InputAt(0));
228 CHECK_EQ(p1, r->InputAt(1));
236 JSTypedLoweringTester R;
237 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
238 Node* p0 = R.Parameter(kNumberTypes[i], 0);
239 Node* p1 = R.Parameter(kNumberTypes[i], 1);
240 Node* add = R.Binop(R.javascript.Add(), p0, p1);
241 Node* r = R.reduce(add);
243 R.CheckPureBinop(IrOpcode::kNumberAdd, r);
244 CHECK_EQ(p0, r->InputAt(0));
245 CHECK_EQ(p1, r->InputAt(1));
251 JSTypedLoweringTester R;
252 const Operator* ops[] = {
253 R.javascript.Add(), R.simplified.NumberAdd(),
254 R.javascript.Subtract(), R.simplified.NumberSubtract(),
255 R.javascript.Multiply(), R.simplified.NumberMultiply(),
256 R.javascript.Divide(), R.simplified.NumberDivide(),
257 R.javascript.Modulus(), R.simplified.NumberModulus(),
260 for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
261 Node* p0 = R.Parameter(kNumberTypes[i], 0);
263 for (size_t j = 0; j < arraysize(kNumberTypes); ++j) {
264 Node* p1 = R.Parameter(kNumberTypes[j], 1);
266 for (size_t k = 0; k < arraysize(ops); k += 2) {
267 Node* add = R.Binop(ops[k], p0, p1);
268 Node* r = R.reduce(add);
270 R.CheckPureBinop(ops[k + 1], r);
271 CHECK_EQ(p0, r->InputAt(0));
272 CHECK_EQ(p1, r->InputAt(1));
279 static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
280 Type* old_type = NodeProperties::GetBounds(old_input).upper;
281 Type* new_type = NodeProperties::GetBounds(new_input).upper;
282 Type* expected_type = I32Type(is_signed);
283 CHECK(new_type->Is(expected_type));
284 if (old_type->Is(expected_type)) {
285 CHECK_EQ(old_input, new_input);
286 } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
287 double v = OpParameter<double>(new_input);
288 double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
294 // A helper class for testing lowering of bitwise shift operators.
295 class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
297 static const int kNumberOps = 6;
298 const Operator* ops[kNumberOps];
299 bool signedness[kNumberOps];
301 JSBitwiseShiftTypedLoweringTester() {
303 set(i++, javascript.ShiftLeft(), true);
304 set(i++, machine.Word32Shl(), false);
305 set(i++, javascript.ShiftRight(), true);
306 set(i++, machine.Word32Sar(), false);
307 set(i++, javascript.ShiftRightLogical(), false);
308 set(i++, machine.Word32Shr(), false);
312 void set(int idx, const Operator* op, bool s) {
319 TEST(Int32BitwiseShifts) {
320 JSBitwiseShiftTypedLoweringTester R;
323 Type::SignedSmall(), Type::UnsignedSmall(), Type::Negative32(),
324 Type::Unsigned31(), Type::Unsigned32(), Type::Signed32(),
325 Type::MinusZero(), Type::NaN(), Type::Undefined(),
326 Type::Null(), Type::Boolean(), Type::Number(),
327 Type::PlainNumber(), Type::String()};
329 for (size_t i = 0; i < arraysize(types); ++i) {
330 Node* p0 = R.Parameter(types[i], 0);
332 for (size_t j = 0; j < arraysize(types); ++j) {
333 Node* p1 = R.Parameter(types[j], 1);
335 for (int k = 0; k < R.kNumberOps; k += 2) {
336 Node* add = R.Binop(R.ops[k], p0, p1);
337 Node* r = R.reduce(add);
339 R.CheckPureBinop(R.ops[k + 1], r);
340 Node* r0 = r->InputAt(0);
341 Node* r1 = r->InputAt(1);
343 CheckToI32(p0, r0, R.signedness[k]);
345 if (r1->opcode() == IrOpcode::kWord32And) {
346 R.CheckPureBinop(IrOpcode::kWord32And, r1);
347 CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
348 R.CheckInt32Constant(0x1F, r1->InputAt(1));
350 CheckToI32(p1, r1, R.signedness[k]);
358 // A helper class for testing lowering of bitwise operators.
359 class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
361 static const int kNumberOps = 6;
362 const Operator* ops[kNumberOps];
363 bool signedness[kNumberOps];
365 JSBitwiseTypedLoweringTester() {
367 set(i++, javascript.BitwiseOr(), true);
368 set(i++, machine.Word32Or(), true);
369 set(i++, javascript.BitwiseXor(), true);
370 set(i++, machine.Word32Xor(), true);
371 set(i++, javascript.BitwiseAnd(), true);
372 set(i++, machine.Word32And(), true);
376 void set(int idx, const Operator* op, bool s) {
383 TEST(Int32BitwiseBinops) {
384 JSBitwiseTypedLoweringTester R;
387 Type::SignedSmall(), Type::UnsignedSmall(), Type::Unsigned32(),
388 Type::Signed32(), Type::MinusZero(), Type::NaN(),
389 Type::OrderedNumber(), Type::PlainNumber(), Type::Undefined(),
390 Type::Null(), Type::Boolean(), Type::Number(),
393 for (size_t i = 0; i < arraysize(types); ++i) {
394 Node* p0 = R.Parameter(types[i], 0);
396 for (size_t j = 0; j < arraysize(types); ++j) {
397 Node* p1 = R.Parameter(types[j], 1);
399 for (int k = 0; k < R.kNumberOps; k += 2) {
400 Node* add = R.Binop(R.ops[k], p0, p1);
401 Node* r = R.reduce(add);
403 R.CheckPureBinop(R.ops[k + 1], r);
405 CheckToI32(p0, r->InputAt(0), R.signedness[k]);
406 CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
414 JSTypedLoweringTester R;
415 const Operator* ton = R.javascript.ToNumber();
417 for (size_t i = 0; i < arraysize(kNumberTypes); i++) { // ToNumber(number)
418 Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
419 CHECK_EQ(IrOpcode::kParameter, r->opcode());
422 { // ToNumber(undefined)
423 Node* r = R.ReduceUnop(ton, Type::Undefined());
428 Node* r = R.ReduceUnop(ton, Type::Null());
429 R.CheckNumberConstant(0.0, r);
434 TEST(JSToNumber_replacement) {
435 JSTypedLoweringTester R;
437 Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()};
439 for (size_t i = 0; i < arraysize(types); i++) {
440 Node* n = R.Parameter(types[i]);
441 Node* c = R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
442 R.start(), R.start());
443 Node* effect_use = R.UseForEffect(c);
444 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
446 R.CheckEffectInput(c, effect_use);
447 Node* r = R.reduce(c);
449 if (types[i]->Is(Type::Number())) {
452 CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
455 CHECK_EQ(n, add->InputAt(0));
456 CHECK_EQ(r, add->InputAt(1));
457 R.CheckEffectInput(R.start(), effect_use);
462 TEST(JSToNumberOfConstant) {
463 JSTypedLoweringTester R;
465 const Operator* ops[] = {
466 R.common.NumberConstant(0), R.common.NumberConstant(-1),
467 R.common.NumberConstant(0.1), R.common.Int32Constant(1177),
468 R.common.Float64Constant(0.99)};
470 for (size_t i = 0; i < arraysize(ops); i++) {
471 Node* n = R.graph.NewNode(ops[i]);
472 Node* convert = R.Unop(R.javascript.ToNumber(), n);
473 Node* r = R.reduce(convert);
474 // Note that either outcome below is correct. It only depends on whether
475 // the types of constants are eagerly computed or only computed by the
477 if (NodeProperties::GetBounds(n).upper->Is(Type::Number())) {
478 // If number constants are eagerly typed, then reduction should
479 // remove the ToNumber.
482 // Otherwise, type-based lowering should only look at the type, and
483 // *not* try to constant fold.
484 CHECK_EQ(convert, r);
490 TEST(JSToNumberOfNumberOrOtherPrimitive) {
491 JSTypedLoweringTester R;
492 Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
495 for (size_t i = 0; i < arraysize(others); i++) {
496 Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
497 Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
498 CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
504 JSTypedLoweringTester R;
505 const Operator* op = R.javascript.ToBoolean();
507 { // ToBoolean(undefined)
508 Node* r = R.ReduceUnop(op, Type::Undefined());
513 Node* r = R.ReduceUnop(op, Type::Null());
517 { // ToBoolean(boolean)
518 Node* r = R.ReduceUnop(op, Type::Boolean());
519 CHECK_EQ(IrOpcode::kParameter, r->opcode());
522 { // ToBoolean(object)
523 Node* r = R.ReduceUnop(op, Type::DetectableObject());
527 { // ToBoolean(undetectable)
528 Node* r = R.ReduceUnop(op, Type::Undetectable());
532 { // ToBoolean(object)
533 Node* r = R.ReduceUnop(op, Type::Object());
534 CHECK_EQ(IrOpcode::kAnyToBoolean, r->opcode());
540 JSTypedLoweringTester R;
542 for (size_t i = 0; i < arraysize(kStringTypes); i++) {
543 Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
544 CHECK_EQ(IrOpcode::kParameter, r->opcode());
547 const Operator* op = R.javascript.ToString();
549 { // ToString(undefined) => "undefined"
550 Node* r = R.ReduceUnop(op, Type::Undefined());
551 R.CheckHandle(R.isolate->factory()->undefined_string(), r);
554 { // ToString(null) => "null"
555 Node* r = R.ReduceUnop(op, Type::Null());
556 R.CheckHandle(R.isolate->factory()->null_string(), r);
559 { // ToString(boolean)
560 Node* r = R.ReduceUnop(op, Type::Boolean());
561 // TODO(titzer): could be a branch
562 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
565 { // ToString(number)
566 Node* r = R.ReduceUnop(op, Type::Number());
567 // TODO(titzer): could remove effects
568 CHECK_EQ(IrOpcode::kJSToString, r->opcode());
571 { // ToString(string)
572 Node* r = R.ReduceUnop(op, Type::String());
573 CHECK_EQ(IrOpcode::kParameter, r->opcode()); // No-op
576 { // ToString(object)
577 Node* r = R.ReduceUnop(op, Type::Object());
578 CHECK_EQ(IrOpcode::kJSToString, r->opcode()); // No reduction.
583 TEST(JSToString_replacement) {
584 JSTypedLoweringTester R;
586 Type* types[] = {Type::Null(), Type::Undefined(), Type::String()};
588 for (size_t i = 0; i < arraysize(types); i++) {
589 Node* n = R.Parameter(types[i]);
590 Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
591 R.start(), R.start());
592 Node* effect_use = R.UseForEffect(c);
593 Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
595 R.CheckEffectInput(c, effect_use);
596 Node* r = R.reduce(c);
598 if (types[i]->Is(Type::String())) {
601 CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
604 CHECK_EQ(n, add->InputAt(0));
605 CHECK_EQ(r, add->InputAt(1));
606 R.CheckEffectInput(R.start(), effect_use);
611 TEST(StringComparison) {
612 JSTypedLoweringTester R;
614 const Operator* ops[] = {
615 R.javascript.LessThan(), R.simplified.StringLessThan(),
616 R.javascript.LessThanOrEqual(), R.simplified.StringLessThanOrEqual(),
617 R.javascript.GreaterThan(), R.simplified.StringLessThan(),
618 R.javascript.GreaterThanOrEqual(), R.simplified.StringLessThanOrEqual()};
620 for (size_t i = 0; i < arraysize(kStringTypes); i++) {
621 Node* p0 = R.Parameter(kStringTypes[i], 0);
622 for (size_t j = 0; j < arraysize(kStringTypes); j++) {
623 Node* p1 = R.Parameter(kStringTypes[j], 1);
625 for (size_t k = 0; k < arraysize(ops); k += 2) {
626 Node* cmp = R.Binop(ops[k], p0, p1);
627 Node* r = R.reduce(cmp);
629 R.CheckPureBinop(ops[k + 1], r);
631 // GreaterThan and GreaterThanOrEqual commute the inputs
632 // and use the LessThan and LessThanOrEqual operators.
633 CHECK_EQ(p1, r->InputAt(0));
634 CHECK_EQ(p0, r->InputAt(1));
636 CHECK_EQ(p0, r->InputAt(0));
637 CHECK_EQ(p1, r->InputAt(1));
645 static void CheckIsConvertedToNumber(Node* val, Node* converted) {
646 if (NodeProperties::GetBounds(val).upper->Is(Type::Number())) {
647 CHECK_EQ(val, converted);
648 } else if (NodeProperties::GetBounds(val).upper->Is(Type::Boolean())) {
649 CHECK_EQ(IrOpcode::kBooleanToNumber, converted->opcode());
650 CHECK_EQ(val, converted->InputAt(0));
652 if (converted->opcode() == IrOpcode::kNumberConstant) return;
653 CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
654 CHECK_EQ(val, converted->InputAt(0));
659 TEST(NumberComparison) {
660 JSTypedLoweringTester R;
662 const Operator* ops[] = {
663 R.javascript.LessThan(), R.simplified.NumberLessThan(),
664 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
665 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
666 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
668 Node* const p0 = R.Parameter(Type::Number(), 0);
669 Node* const p1 = R.Parameter(Type::Number(), 1);
671 for (size_t k = 0; k < arraysize(ops); k += 2) {
672 Node* cmp = R.Binop(ops[k], p0, p1);
673 Node* r = R.reduce(cmp);
675 R.CheckPureBinop(ops[k + 1], r);
677 // GreaterThan and GreaterThanOrEqual commute the inputs
678 // and use the LessThan and LessThanOrEqual operators.
679 CheckIsConvertedToNumber(p1, r->InputAt(0));
680 CheckIsConvertedToNumber(p0, r->InputAt(1));
682 CheckIsConvertedToNumber(p0, r->InputAt(0));
683 CheckIsConvertedToNumber(p1, r->InputAt(1));
689 TEST(MixedComparison1) {
690 JSTypedLoweringTester R;
692 Type* types[] = {Type::Number(), Type::String(),
693 Type::Union(Type::Number(), Type::String(), R.main_zone())};
695 for (size_t i = 0; i < arraysize(types); i++) {
696 Node* p0 = R.Parameter(types[i], 0);
698 for (size_t j = 0; j < arraysize(types); j++) {
699 Node* p1 = R.Parameter(types[j], 1);
701 Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
702 Node* r = R.reduce(cmp);
704 if (!types[i]->Maybe(Type::String()) ||
705 !types[j]->Maybe(Type::String())) {
706 if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
707 R.CheckPureBinop(R.simplified.StringLessThan(), r);
709 R.CheckPureBinop(R.simplified.NumberLessThan(), r);
712 CHECK_EQ(cmp, r); // No reduction of mixed types.
721 JSTypedLoweringTester R;
722 const Operator* opnot = R.javascript.UnaryNot();
724 for (size_t i = 0; i < arraysize(kJSTypes); i++) {
725 Node* orig = R.Unop(opnot, R.Parameter(kJSTypes[i]));
726 Node* r = R.reduce(orig);
728 if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
729 // The original node was turned into a ToBoolean.
730 CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
731 } else if (r->opcode() != IrOpcode::kHeapConstant) {
732 CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
738 TEST(RemoveToNumberEffects) {
739 FLAG_turbo_deoptimization = true;
741 JSTypedLoweringTester R;
743 Node* effect_use = NULL;
744 for (int i = 0; i < 10; i++) {
745 Node* p0 = R.Parameter(Type::Number());
746 Node* ton = R.Unop(R.javascript.ToNumber(), p0);
747 Node* frame_state = R.EmptyFrameState(R.context());
752 // TODO(jarin) Replace with a query of FLAG_turbo_deoptimization.
753 if (OperatorProperties::HasFrameStateInput(R.javascript.ToNumber())) {
754 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
755 frame_state, ton, R.start());
757 effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
762 // TODO(jarin) Replace with a query of FLAG_turbo_deoptimization.
763 if (OperatorProperties::HasFrameStateInput(R.javascript.ToNumber())) {
765 R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
766 frame_state, ton, R.start());
768 effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton,
769 R.context(), ton, R.start());
773 effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
775 effect_use = R.graph.NewNode(R.javascript.Add(), ton, ton, R.context(),
776 frame_state, ton, R.start());
779 effect_use = R.graph.NewNode(R.javascript.Add(), p0, p0, R.context(),
780 frame_state, ton, R.start());
783 effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start());
786 effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start());
789 R.CheckEffectInput(R.start(), ton);
790 if (effect_use != NULL) R.CheckEffectInput(ton, effect_use);
792 Node* r = R.reduce(ton);
794 CHECK_NE(R.start(), r);
796 if (effect_use != NULL) {
797 R.CheckEffectInput(R.start(), effect_use);
798 // Check that value uses of ToNumber() do not go to start().
799 for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) {
800 CHECK_NE(R.start(), effect_use->InputAt(i));
805 CHECK(!effect_use); // should have done all cases above.
809 // Helper class for testing the reduction of a single binop.
810 class BinopEffectsTester {
812 explicit BinopEffectsTester(const Operator* op, Type* t0, Type* t1)
814 p0(R.Parameter(t0, 0)),
815 p1(R.Parameter(t1, 1)),
816 binop(R.Binop(op, p0, p1)),
817 effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
818 // Effects should be ordered start -> binop -> effect_use
819 R.CheckEffectInput(R.start(), binop);
820 R.CheckEffectInput(binop, effect_use);
821 result = R.reduce(binop);
824 JSTypedLoweringTester R;
831 void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
833 void CheckEffectOrdering(Node* n0) {
834 R.CheckEffectInput(R.start(), n0);
835 R.CheckEffectInput(n0, effect_use);
838 void CheckEffectOrdering(Node* n0, Node* n1) {
839 R.CheckEffectInput(R.start(), n0);
840 R.CheckEffectInput(n0, n1);
841 R.CheckEffectInput(n1, effect_use);
844 Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
845 return CheckConverted(opcode, result->InputAt(which), effects);
848 Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
849 CHECK_EQ(opcode, node->opcode());
851 CHECK_LT(0, node->op()->EffectInputCount());
853 CHECK_EQ(0, node->op()->EffectInputCount());
858 Node* CheckNoOp(int which) {
859 CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
860 return result->InputAt(which);
865 // Helper function for strict and non-strict equality reductions.
866 void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
867 Node* r, IrOpcode::Value expected) {
868 for (int j = 0; j < 2; j++) {
869 Node* p0 = j == 0 ? l : r;
870 Node* p1 = j == 1 ? l : r;
873 Node* eq = strict ? R->graph.NewNode(R->javascript.StrictEqual(), p0, p1)
874 : R->Binop(R->javascript.Equal(), p0, p1);
875 Node* r = R->reduce(eq);
876 R->CheckPureBinop(expected, r);
881 ? R->graph.NewNode(R->javascript.StrictNotEqual(), p0, p1)
882 : R->Binop(R->javascript.NotEqual(), p0, p1);
883 Node* n = R->reduce(ne);
884 CHECK_EQ(IrOpcode::kBooleanNot, n->opcode());
885 Node* r = n->InputAt(0);
886 R->CheckPureBinop(expected, r);
892 TEST(EqualityForNumbers) {
893 JSTypedLoweringTester R;
895 Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
896 Type::Signed32(), Type::Unsigned32(),
900 for (size_t i = 0; i < arraysize(simple_number_types); ++i) {
901 Node* p0 = R.Parameter(simple_number_types[i], 0);
903 for (size_t j = 0; j < arraysize(simple_number_types); ++j) {
904 Node* p1 = R.Parameter(simple_number_types[j], 1);
906 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
907 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
913 TEST(StrictEqualityForRefEqualTypes) {
914 JSTypedLoweringTester R;
916 Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
917 Type::Object(), Type::Receiver()};
919 Node* p0 = R.Parameter(Type::Any());
920 for (size_t i = 0; i < arraysize(types); i++) {
921 Node* p1 = R.Parameter(types[i]);
922 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
924 // TODO(titzer): Equal(RefEqualTypes)
928 TEST(StringEquality) {
929 JSTypedLoweringTester R;
930 Node* p0 = R.Parameter(Type::String());
931 Node* p1 = R.Parameter(Type::String());
933 CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
934 CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
938 TEST(RemovePureNumberBinopEffects) {
939 JSTypedLoweringTester R;
941 const Operator* ops[] = {
942 R.javascript.Equal(), R.simplified.NumberEqual(),
943 R.javascript.Add(), R.simplified.NumberAdd(),
944 R.javascript.Subtract(), R.simplified.NumberSubtract(),
945 R.javascript.Multiply(), R.simplified.NumberMultiply(),
946 R.javascript.Divide(), R.simplified.NumberDivide(),
947 R.javascript.Modulus(), R.simplified.NumberModulus(),
948 R.javascript.LessThan(), R.simplified.NumberLessThan(),
949 R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
952 for (size_t j = 0; j < arraysize(ops); j += 2) {
953 BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
954 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
956 B.R.CheckPureBinop(B.result->opcode(), B.result);
961 B.CheckEffectsRemoved();
966 TEST(OrderNumberBinopEffects1) {
967 JSTypedLoweringTester R;
969 const Operator* ops[] = {
970 R.javascript.Subtract(), R.simplified.NumberSubtract(),
971 R.javascript.Multiply(), R.simplified.NumberMultiply(),
972 R.javascript.Divide(), R.simplified.NumberDivide(),
973 R.javascript.Modulus(), R.simplified.NumberModulus(),
976 for (size_t j = 0; j < arraysize(ops); j += 2) {
977 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol());
978 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
980 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
981 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
983 CHECK_EQ(B.p0, i0->InputAt(0));
984 CHECK_EQ(B.p1, i1->InputAt(0));
986 // Effects should be ordered start -> i0 -> i1 -> effect_use
987 B.CheckEffectOrdering(i0, i1);
992 TEST(OrderNumberBinopEffects2) {
993 JSTypedLoweringTester R;
995 const Operator* ops[] = {
996 R.javascript.Add(), R.simplified.NumberAdd(),
997 R.javascript.Subtract(), R.simplified.NumberSubtract(),
998 R.javascript.Multiply(), R.simplified.NumberMultiply(),
999 R.javascript.Divide(), R.simplified.NumberDivide(),
1000 R.javascript.Modulus(), R.simplified.NumberModulus(),
1003 for (size_t j = 0; j < arraysize(ops); j += 2) {
1004 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
1006 Node* i0 = B.CheckNoOp(0);
1007 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1010 CHECK_EQ(B.p1, i1->InputAt(0));
1012 // Effects should be ordered start -> i1 -> effect_use
1013 B.CheckEffectOrdering(i1);
1016 for (size_t j = 0; j < arraysize(ops); j += 2) {
1017 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
1019 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1020 Node* i1 = B.CheckNoOp(1);
1022 CHECK_EQ(B.p0, i0->InputAt(0));
1025 // Effects should be ordered start -> i0 -> effect_use
1026 B.CheckEffectOrdering(i0);
1031 TEST(OrderCompareEffects) {
1032 JSTypedLoweringTester R;
1034 const Operator* ops[] = {
1035 R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
1036 R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1039 for (size_t j = 0; j < arraysize(ops); j += 2) {
1040 BinopEffectsTester B(ops[j], Type::Symbol(), Type::String());
1041 CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
1043 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1044 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1046 // Inputs should be commuted.
1047 CHECK_EQ(B.p1, i0->InputAt(0));
1048 CHECK_EQ(B.p0, i1->InputAt(0));
1050 // But effects should be ordered start -> i1 -> effect_use
1051 B.CheckEffectOrdering(i1);
1054 for (size_t j = 0; j < arraysize(ops); j += 2) {
1055 BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
1057 Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
1058 Node* i1 = B.result->InputAt(1);
1060 CHECK_EQ(B.p1, i0->InputAt(0)); // Should be commuted.
1063 // Effects should be ordered start -> i1 -> effect_use
1064 B.CheckEffectOrdering(i0);
1067 for (size_t j = 0; j < arraysize(ops); j += 2) {
1068 BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
1070 Node* i0 = B.result->InputAt(0);
1071 Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
1073 CHECK_EQ(B.p1, i0); // Should be commuted.
1074 CHECK_EQ(B.p0, i1->InputAt(0));
1076 // Effects should be ordered start -> i0 -> effect_use
1077 B.CheckEffectOrdering(i1);
1082 TEST(Int32BinopEffects) {
1083 JSBitwiseTypedLoweringTester R;
1085 for (int j = 0; j < R.kNumberOps; j += 2) {
1086 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1087 BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
1088 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1090 B.R.CheckPureBinop(B.result->opcode(), B.result);
1095 B.CheckEffectsRemoved();
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], Type::Number(), Type::Number());
1101 CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
1103 B.R.CheckPureBinop(B.result->opcode(), B.result);
1105 B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1106 B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
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::Primitive());
1115 B.R.CheckPureBinop(B.result->opcode(), B.result);
1117 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1118 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1120 CHECK_EQ(B.p0, i0->InputAt(0));
1121 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1123 CHECK_EQ(B.p1, ii1->InputAt(0));
1125 B.CheckEffectOrdering(ii1);
1128 for (int j = 0; j < R.kNumberOps; j += 2) {
1129 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1130 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Number());
1132 B.R.CheckPureBinop(B.result->opcode(), B.result);
1134 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1135 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1137 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1138 CHECK_EQ(B.p1, i1->InputAt(0));
1140 CHECK_EQ(B.p0, ii0->InputAt(0));
1142 B.CheckEffectOrdering(ii0);
1145 for (int j = 0; j < R.kNumberOps; j += 2) {
1146 bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
1147 BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Primitive());
1149 B.R.CheckPureBinop(B.result->opcode(), B.result);
1151 Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
1152 Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
1154 Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
1155 Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
1157 CHECK_EQ(B.p0, ii0->InputAt(0));
1158 CHECK_EQ(B.p1, ii1->InputAt(0));
1160 B.CheckEffectOrdering(ii0, ii1);
1165 TEST(Int32AddNarrowing) {
1167 JSBitwiseTypedLoweringTester R;
1169 for (int o = 0; o < R.kNumberOps; o += 2) {
1170 for (size_t i = 0; i < arraysize(kInt32Types); i++) {
1171 Node* n0 = R.Parameter(kInt32Types[i]);
1172 for (size_t j = 0; j < arraysize(kInt32Types); j++) {
1173 Node* n1 = R.Parameter(kInt32Types[j]);
1174 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1176 for (int l = 0; l < 2; l++) {
1177 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1179 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1180 Node* r = R.reduce(or_node);
1182 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1183 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1190 JSBitwiseShiftTypedLoweringTester R;
1192 for (int o = 0; o < R.kNumberOps; o += 2) {
1193 for (size_t i = 0; i < arraysize(kInt32Types); i++) {
1194 Node* n0 = R.Parameter(kInt32Types[i]);
1195 for (size_t j = 0; j < arraysize(kInt32Types); j++) {
1196 Node* n1 = R.Parameter(kInt32Types[j]);
1197 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1199 for (int l = 0; l < 2; l++) {
1200 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1202 R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
1203 Node* r = R.reduce(or_node);
1205 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1206 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1213 JSBitwiseTypedLoweringTester R;
1215 for (int o = 0; o < R.kNumberOps; o += 2) {
1216 Node* n0 = R.Parameter(I32Type(R.signedness[o]));
1217 Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
1218 Node* one = R.graph.NewNode(R.common.NumberConstant(1));
1220 Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
1221 Node* or_node = R.Binop(R.ops[o], add_node, one);
1222 Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
1223 Node* r = R.reduce(or_node);
1224 CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
1225 CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
1226 // Conversion to int32 should be done.
1227 CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
1228 CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
1229 // The other use should also not be touched.
1230 CHECK_EQ(add_node, other_use->InputAt(0));
1231 CHECK_EQ(one, other_use->InputAt(1));
1237 TEST(Int32Comparisons) {
1238 JSTypedLoweringTester R;
1241 const Operator* js_op;
1242 const Operator* uint_op;
1243 const Operator* int_op;
1244 const Operator* num_op;
1249 {R.javascript.LessThan(), R.machine.Uint32LessThan(),
1250 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false},
1251 {R.javascript.LessThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1252 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1254 {R.javascript.GreaterThan(), R.machine.Uint32LessThan(),
1255 R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true},
1256 {R.javascript.GreaterThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
1257 R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
1260 for (size_t o = 0; o < arraysize(ops); o++) {
1261 for (size_t i = 0; i < arraysize(kNumberTypes); i++) {
1262 Type* t0 = kNumberTypes[i];
1263 Node* p0 = R.Parameter(t0, 0);
1265 for (size_t j = 0; j < arraysize(kNumberTypes); j++) {
1266 Type* t1 = kNumberTypes[j];
1267 Node* p1 = R.Parameter(t1, 1);
1269 Node* cmp = R.Binop(ops[o].js_op, p0, p1);
1270 Node* r = R.reduce(cmp);
1272 const Operator* expected;
1273 if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) {
1274 expected = ops[o].uint_op;
1275 } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) {
1276 expected = ops[o].int_op;
1278 expected = ops[o].num_op;
1280 R.CheckPureBinop(expected, r);
1281 if (ops[o].commute) {
1282 CHECK_EQ(p1, r->InputAt(0));
1283 CHECK_EQ(p0, r->InputAt(1));
1285 CHECK_EQ(p0, r->InputAt(0));
1286 CHECK_EQ(p1, r->InputAt(1));