| Index: test/cctest/compiler/compiler/test-js-typed-lowering.cc
|
| diff --git a/test/cctest/compiler/compiler/test-js-typed-lowering.cc b/test/cctest/compiler/compiler/test-js-typed-lowering.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1bbc76ea1a5a1a8151ccededcc00231ff999ec57
|
| --- /dev/null
|
| +++ b/test/cctest/compiler/compiler/test-js-typed-lowering.cc
|
| @@ -0,0 +1,1345 @@
|
| +// Copyright 2014 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/v8.h"
|
| +#include "test/cctest/cctest.h"
|
| +
|
| +#include "src/compiler/graph-inl.h"
|
| +#include "src/compiler/js-typed-lowering.h"
|
| +#include "src/compiler/node-properties-inl.h"
|
| +#include "src/compiler/opcodes.h"
|
| +#include "src/compiler/typer.h"
|
| +
|
| +using namespace v8::internal;
|
| +using namespace v8::internal::compiler;
|
| +
|
| +class JSTypedLoweringTester : public HandleAndZoneScope {
|
| + public:
|
| + JSTypedLoweringTester()
|
| + : isolate(main_isolate()),
|
| + binop(NULL),
|
| + unop(NULL),
|
| + javascript(main_zone()),
|
| + machine(main_zone()),
|
| + simplified(main_zone()),
|
| + common(main_zone()),
|
| + graph(main_zone()),
|
| + typer(main_zone()),
|
| + source_positions(&graph),
|
| + context_node(NULL) {
|
| + typer.DecorateGraph(&graph);
|
| + }
|
| +
|
| + Isolate* isolate;
|
| + Operator* binop;
|
| + Operator* unop;
|
| + JSOperatorBuilder javascript;
|
| + MachineOperatorBuilder machine;
|
| + SimplifiedOperatorBuilder simplified;
|
| + CommonOperatorBuilder common;
|
| + Graph graph;
|
| + Typer typer;
|
| + SourcePositionTable source_positions;
|
| + Node* context_node;
|
| +
|
| + Node* Parameter(Type* t, int32_t index = 0) {
|
| + Node* n = graph.NewNode(common.Parameter(index));
|
| + NodeProperties::SetBounds(n, Bounds(Type::None(), t));
|
| + return n;
|
| + }
|
| +
|
| + Node* reduce(Node* node) {
|
| + JSGraph jsgraph(&graph, &common, &typer);
|
| + JSTypedLowering reducer(&jsgraph, &source_positions);
|
| + Reduction reduction = reducer.Reduce(node);
|
| + if (reduction.Changed()) return reduction.replacement();
|
| + return node;
|
| + }
|
| +
|
| + Node* start() {
|
| + Node* s = graph.start();
|
| + if (s == NULL) {
|
| + s = graph.NewNode(common.Start());
|
| + graph.SetStart(s);
|
| + }
|
| + return s;
|
| + }
|
| +
|
| + Node* context() {
|
| + if (context_node == NULL) {
|
| + context_node = graph.NewNode(common.Parameter(-1));
|
| + }
|
| + return context_node;
|
| + }
|
| +
|
| + Node* control() { return start(); }
|
| +
|
| + void CheckPureBinop(IrOpcode::Value expected, Node* node) {
|
| + CHECK_EQ(expected, node->opcode());
|
| + CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
|
| + }
|
| +
|
| + void CheckPureBinop(Operator* expected, Node* node) {
|
| + CHECK_EQ(expected->opcode(), node->op()->opcode());
|
| + CHECK_EQ(2, node->InputCount()); // should not have context, effect, etc.
|
| + }
|
| +
|
| + Node* ReduceUnop(Operator* op, Type* input_type) {
|
| + return reduce(Unop(op, Parameter(input_type)));
|
| + }
|
| +
|
| + Node* ReduceBinop(Operator* op, Type* left_type, Type* right_type) {
|
| + return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
|
| + }
|
| +
|
| + Node* Binop(Operator* op, Node* left, Node* right) {
|
| + // JS binops also require context, effect, and control
|
| + return graph.NewNode(op, left, right, context(), start(), control());
|
| + }
|
| +
|
| + Node* Unop(Operator* op, Node* input) {
|
| + // JS unops also require context, effect, and control
|
| + return graph.NewNode(op, input, context(), start(), control());
|
| + }
|
| +
|
| + Node* UseForEffect(Node* node) {
|
| + // TODO(titzer): use EffectPhi after fixing EffectCount
|
| + return graph.NewNode(javascript.ToNumber(), node, context(), node,
|
| + control());
|
| + }
|
| +
|
| + void CheckEffectInput(Node* effect, Node* use) {
|
| + CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
|
| + }
|
| +
|
| + void CheckInt32Constant(int32_t expected, Node* result) {
|
| + CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
|
| + CHECK_EQ(expected, ValueOf<int32_t>(result->op()));
|
| + }
|
| +
|
| + void CheckNumberConstant(double expected, Node* result) {
|
| + CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
|
| + CHECK_EQ(expected, ValueOf<double>(result->op()));
|
| + }
|
| +
|
| + void CheckNaN(Node* result) {
|
| + CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
|
| + double value = ValueOf<double>(result->op());
|
| + CHECK(std::isnan(value));
|
| + }
|
| +
|
| + void CheckTrue(Node* result) {
|
| + CheckHandle(isolate->factory()->true_value(), result);
|
| + }
|
| +
|
| + void CheckFalse(Node* result) {
|
| + CheckHandle(isolate->factory()->false_value(), result);
|
| + }
|
| +
|
| + void CheckHandle(Handle<Object> expected, Node* result) {
|
| + CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
|
| + Handle<Object> value = ValueOf<Handle<Object> >(result->op());
|
| + CHECK_EQ(*expected, *value);
|
| + }
|
| +};
|
| +
|
| +static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
|
| + Type::String()};
|
| +
|
| +
|
| +static Type* kInt32Types[] = {
|
| + Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
|
| + Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
|
| + Type::Signed32(), Type::Unsigned32(), Type::Integral32()};
|
| +
|
| +
|
| +static Type* kNumberTypes[] = {
|
| + Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
|
| + Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
|
| + Type::Signed32(), Type::Unsigned32(), Type::Integral32(),
|
| + Type::MinusZero(), Type::NaN(), Type::OtherNumber(),
|
| + Type::Number()};
|
| +
|
| +
|
| +static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
|
| + Type::Number(), Type::String(), Type::Object()};
|
| +
|
| +
|
| +static Type* I32Type(bool is_signed) {
|
| + return is_signed ? Type::Signed32() : Type::Unsigned32();
|
| +}
|
| +
|
| +
|
| +static IrOpcode::Value NumberToI32(bool is_signed) {
|
| + return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
|
| +}
|
| +
|
| +
|
| +TEST(StringBinops) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); ++i) {
|
| + Node* p0 = R.Parameter(kStringTypes[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(kStringTypes); ++j) {
|
| + Node* p1 = R.Parameter(kStringTypes[j], 1);
|
| +
|
| + Node* add = R.Binop(R.javascript.Add(), p0, p1);
|
| + Node* r = R.reduce(add);
|
| +
|
| + R.CheckPureBinop(IrOpcode::kStringAdd, r);
|
| + CHECK_EQ(p0, r->InputAt(0));
|
| + CHECK_EQ(p1, r->InputAt(1));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(AddNumber1) {
|
| + JSTypedLoweringTester R;
|
| + for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); ++i) {
|
| + Node* p0 = R.Parameter(kNumberTypes[i], 0);
|
| + Node* p1 = R.Parameter(kNumberTypes[i], 1);
|
| + Node* add = R.Binop(R.javascript.Add(), p0, p1);
|
| + Node* r = R.reduce(add);
|
| +
|
| + R.CheckPureBinop(IrOpcode::kNumberAdd, r);
|
| + CHECK_EQ(p0, r->InputAt(0));
|
| + CHECK_EQ(p1, r->InputAt(1));
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(NumberBinops) {
|
| + JSTypedLoweringTester R;
|
| + Operator* ops[] = {
|
| + R.javascript.Add(), R.simplified.NumberAdd(),
|
| + R.javascript.Subtract(), R.simplified.NumberSubtract(),
|
| + R.javascript.Multiply(), R.simplified.NumberMultiply(),
|
| + R.javascript.Divide(), R.simplified.NumberDivide(),
|
| + R.javascript.Modulus(), R.simplified.NumberModulus(),
|
| + };
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); ++i) {
|
| + Node* p0 = R.Parameter(kNumberTypes[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(kNumberTypes); ++j) {
|
| + Node* p1 = R.Parameter(kNumberTypes[j], 1);
|
| +
|
| + for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
|
| + Node* add = R.Binop(ops[k], p0, p1);
|
| + Node* r = R.reduce(add);
|
| +
|
| + R.CheckPureBinop(ops[k + 1], r);
|
| + CHECK_EQ(p0, r->InputAt(0));
|
| + CHECK_EQ(p1, r->InputAt(1));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
|
| + Type* old_type = NodeProperties::GetBounds(old_input).upper;
|
| + Type* expected_type = I32Type(is_signed);
|
| + if (old_type->Is(expected_type)) {
|
| + CHECK_EQ(old_input, new_input);
|
| + } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
|
| + CHECK(NodeProperties::GetBounds(new_input).upper->Is(expected_type));
|
| + double v = ValueOf<double>(new_input->op());
|
| + double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
|
| + CHECK_EQ(e, v);
|
| + } else {
|
| + CHECK_EQ(NumberToI32(is_signed), new_input->opcode());
|
| + }
|
| +}
|
| +
|
| +
|
| +// A helper class for testing lowering of bitwise shift operators.
|
| +class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
|
| + public:
|
| + static const int kNumberOps = 6;
|
| + Operator** ops;
|
| + bool* signedness;
|
| +
|
| + JSBitwiseShiftTypedLoweringTester() {
|
| + Operator* o[] = {javascript.ShiftLeft(), machine.Word32Shl(),
|
| + javascript.ShiftRight(), machine.Word32Sar(),
|
| + javascript.ShiftRightLogical(), machine.Word32Shr()};
|
| +
|
| + ops = static_cast<Operator**>(malloc(sizeof(o)));
|
| + memcpy(ops, o, sizeof(o));
|
| +
|
| + // Expected signedness of left and right conversions above.
|
| + bool s[] = {true, false, true, false, false, false};
|
| +
|
| + signedness = static_cast<bool*>(malloc(sizeof(s)));
|
| + memcpy(signedness, s, sizeof(s));
|
| + }
|
| +};
|
| +
|
| +
|
| +TEST(Int32BitwiseShifts) {
|
| + JSBitwiseShiftTypedLoweringTester R;
|
| +
|
| + Type* types[] = {
|
| + Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
|
| + Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
|
| + Type::NaN(), Type::OtherNumber(), Type::Undefined(),
|
| + Type::Null(), Type::Boolean(), Type::Number(),
|
| + Type::String(), Type::Object()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
|
| + Node* p0 = R.Parameter(types[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(types); ++j) {
|
| + Node* p1 = R.Parameter(types[j], 1);
|
| +
|
| + for (int k = 0; k < R.kNumberOps; k += 2) {
|
| + Node* add = R.Binop(R.ops[k], p0, p1);
|
| + Node* r = R.reduce(add);
|
| +
|
| + R.CheckPureBinop(R.ops[k + 1], r);
|
| + Node* r0 = r->InputAt(0);
|
| + Node* r1 = r->InputAt(1);
|
| +
|
| + CheckToI32(p0, r0, R.signedness[k]);
|
| +
|
| + R.CheckPureBinop(IrOpcode::kWord32And, r1);
|
| + CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
|
| + R.CheckInt32Constant(0x1F, r1->InputAt(1));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +// A helper class for testing lowering of bitwise operators.
|
| +class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
|
| + public:
|
| + static const int kNumberOps = 6;
|
| + Operator** ops;
|
| + bool* signedness;
|
| +
|
| + JSBitwiseTypedLoweringTester() {
|
| + Operator* o[] = {javascript.BitwiseOr(), machine.Word32Or(),
|
| + javascript.BitwiseXor(), machine.Word32Xor(),
|
| + javascript.BitwiseAnd(), machine.Word32And()};
|
| +
|
| + ops = static_cast<Operator**>(malloc(sizeof(o)));
|
| + memcpy(ops, o, sizeof(o));
|
| +
|
| + // Expected signedness of left and right conversions above.
|
| + bool s[] = {true, true, true, true, true, true};
|
| +
|
| + signedness = static_cast<bool*>(malloc(sizeof(s)));
|
| + memcpy(signedness, s, sizeof(s));
|
| + }
|
| +};
|
| +
|
| +
|
| +TEST(Int32BitwiseBinops) {
|
| + JSBitwiseTypedLoweringTester R;
|
| +
|
| + Type* types[] = {
|
| + Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
|
| + Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
|
| + Type::NaN(), Type::OtherNumber(), Type::Undefined(),
|
| + Type::Null(), Type::Boolean(), Type::Number(),
|
| + Type::String(), Type::Object()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
|
| + Node* p0 = R.Parameter(types[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(types); ++j) {
|
| + Node* p1 = R.Parameter(types[j], 1);
|
| +
|
| + for (int k = 0; k < R.kNumberOps; k += 2) {
|
| + Node* add = R.Binop(R.ops[k], p0, p1);
|
| + Node* r = R.reduce(add);
|
| +
|
| + R.CheckPureBinop(R.ops[k + 1], r);
|
| +
|
| + CheckToI32(p0, r->InputAt(0), R.signedness[k]);
|
| + CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToNumber1) {
|
| + JSTypedLoweringTester R;
|
| + Operator* ton = R.javascript.ToNumber();
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); i++) { // ToNumber(number)
|
| + Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
|
| + CHECK_EQ(IrOpcode::kParameter, r->opcode());
|
| + }
|
| +
|
| + { // ToNumber(undefined)
|
| + Node* r = R.ReduceUnop(ton, Type::Undefined());
|
| + R.CheckNaN(r);
|
| + }
|
| +
|
| + { // ToNumber(null)
|
| + Node* r = R.ReduceUnop(ton, Type::Null());
|
| + R.CheckNumberConstant(0.0, r);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToNumber_replacement) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
|
| + Node* n = R.Parameter(types[i]);
|
| + Node* c = R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
|
| + R.start(), R.start());
|
| + Node* effect_use = R.UseForEffect(c);
|
| + Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
|
| +
|
| + R.CheckEffectInput(c, effect_use);
|
| + Node* r = R.reduce(c);
|
| +
|
| + if (types[i]->Is(Type::Number())) {
|
| + CHECK_EQ(n, r);
|
| + } else {
|
| + CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
|
| + }
|
| +
|
| + CHECK_EQ(n, add->InputAt(0));
|
| + CHECK_EQ(r, add->InputAt(1));
|
| + R.CheckEffectInput(R.start(), effect_use);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToNumberOfConstant) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {R.common.NumberConstant(0), R.common.NumberConstant(-1),
|
| + R.common.NumberConstant(0.1), R.common.Int32Constant(1177),
|
| + R.common.Float64Constant(0.99)};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(ops); i++) {
|
| + Node* n = R.graph.NewNode(ops[i]);
|
| + Node* convert = R.Unop(R.javascript.ToNumber(), n);
|
| + Node* r = R.reduce(convert);
|
| + // Note that either outcome below is correct. It only depends on whether
|
| + // the types of constants are eagerly computed or only computed by the
|
| + // typing pass.
|
| + if (NodeProperties::GetBounds(n).upper->Is(Type::Number())) {
|
| + // If number constants are eagerly typed, then reduction should
|
| + // remove the ToNumber.
|
| + CHECK_EQ(n, r);
|
| + } else {
|
| + // Otherwise, type-based lowering should only look at the type, and
|
| + // *not* try to constant fold.
|
| + CHECK_EQ(convert, r);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToNumberOfNumberOrOtherPrimitive) {
|
| + JSTypedLoweringTester R;
|
| + Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
|
| + Type::String()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(others); i++) {
|
| + Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
|
| + Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
|
| + CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToBoolean) {
|
| + JSTypedLoweringTester R;
|
| + Operator* op = R.javascript.ToBoolean();
|
| +
|
| + { // ToBoolean(undefined)
|
| + Node* r = R.ReduceUnop(op, Type::Undefined());
|
| + R.CheckFalse(r);
|
| + }
|
| +
|
| + { // ToBoolean(null)
|
| + Node* r = R.ReduceUnop(op, Type::Null());
|
| + R.CheckFalse(r);
|
| + }
|
| +
|
| + { // ToBoolean(boolean)
|
| + Node* r = R.ReduceUnop(op, Type::Boolean());
|
| + CHECK_EQ(IrOpcode::kParameter, r->opcode());
|
| + }
|
| +
|
| + { // ToBoolean(number)
|
| + Node* r = R.ReduceUnop(op, Type::Number());
|
| + CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
|
| + Node* i = r->InputAt(0);
|
| + CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
|
| + // ToBoolean(number) => BooleanNot(NumberEqual(x, #0))
|
| + }
|
| +
|
| + { // ToBoolean(string)
|
| + Node* r = R.ReduceUnop(op, Type::String());
|
| + // TODO(titzer): test will break with better js-typed-lowering
|
| + CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
|
| + }
|
| +
|
| + { // ToBoolean(object)
|
| + Node* r = R.ReduceUnop(op, Type::DetectableObject());
|
| + R.CheckTrue(r);
|
| + }
|
| +
|
| + { // ToBoolean(undetectable)
|
| + Node* r = R.ReduceUnop(op, Type::Undetectable());
|
| + R.CheckFalse(r);
|
| + }
|
| +
|
| + { // ToBoolean(object)
|
| + Node* r = R.ReduceUnop(op, Type::Object());
|
| + CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToBoolean_replacement) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* types[] = {Type::Null(), Type::Undefined(), Type::Boolean(),
|
| + Type::DetectableObject(), Type::Undetectable()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
|
| + Node* n = R.Parameter(types[i]);
|
| + Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context(),
|
| + R.start(), R.start());
|
| + Node* effect_use = R.UseForEffect(c);
|
| + Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
|
| +
|
| + R.CheckEffectInput(c, effect_use);
|
| + Node* r = R.reduce(c);
|
| +
|
| + if (types[i]->Is(Type::Boolean())) {
|
| + CHECK_EQ(n, r);
|
| + } else {
|
| + CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
|
| + }
|
| +
|
| + CHECK_EQ(n, add->InputAt(0));
|
| + CHECK_EQ(r, add->InputAt(1));
|
| + R.CheckEffectInput(R.start(), effect_use);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToString1) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); i++) {
|
| + Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
|
| + CHECK_EQ(IrOpcode::kParameter, r->opcode());
|
| + }
|
| +
|
| + Operator* op = R.javascript.ToString();
|
| +
|
| + { // ToString(undefined) => "undefined"
|
| + Node* r = R.ReduceUnop(op, Type::Undefined());
|
| + R.CheckHandle(R.isolate->factory()->undefined_string(), r);
|
| + }
|
| +
|
| + { // ToString(null) => "null"
|
| + Node* r = R.ReduceUnop(op, Type::Null());
|
| + R.CheckHandle(R.isolate->factory()->null_string(), r);
|
| + }
|
| +
|
| + { // ToString(boolean)
|
| + Node* r = R.ReduceUnop(op, Type::Boolean());
|
| + // TODO(titzer): could be a branch
|
| + CHECK_EQ(IrOpcode::kJSToString, r->opcode());
|
| + }
|
| +
|
| + { // ToString(number)
|
| + Node* r = R.ReduceUnop(op, Type::Number());
|
| + // TODO(titzer): could remove effects
|
| + CHECK_EQ(IrOpcode::kJSToString, r->opcode());
|
| + }
|
| +
|
| + { // ToString(string)
|
| + Node* r = R.ReduceUnop(op, Type::String());
|
| + CHECK_EQ(IrOpcode::kParameter, r->opcode()); // No-op
|
| + }
|
| +
|
| + { // ToString(object)
|
| + Node* r = R.ReduceUnop(op, Type::Object());
|
| + CHECK_EQ(IrOpcode::kJSToString, r->opcode()); // No reduction.
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(JSToString_replacement) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* types[] = {Type::Null(), Type::Undefined(), Type::String()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
|
| + Node* n = R.Parameter(types[i]);
|
| + Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
|
| + R.start(), R.start());
|
| + Node* effect_use = R.UseForEffect(c);
|
| + Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
|
| +
|
| + R.CheckEffectInput(c, effect_use);
|
| + Node* r = R.reduce(c);
|
| +
|
| + if (types[i]->Is(Type::String())) {
|
| + CHECK_EQ(n, r);
|
| + } else {
|
| + CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
|
| + }
|
| +
|
| + CHECK_EQ(n, add->InputAt(0));
|
| + CHECK_EQ(r, add->InputAt(1));
|
| + R.CheckEffectInput(R.start(), effect_use);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(StringComparison) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.LessThan(), R.simplified.StringLessThan(),
|
| + R.javascript.LessThanOrEqual(), R.simplified.StringLessThanOrEqual(),
|
| + R.javascript.GreaterThan(), R.simplified.StringLessThan(),
|
| + R.javascript.GreaterThanOrEqual(), R.simplified.StringLessThanOrEqual()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kStringTypes); i++) {
|
| + Node* p0 = R.Parameter(kStringTypes[i], 0);
|
| + for (size_t j = 0; j < ARRAY_SIZE(kStringTypes); j++) {
|
| + Node* p1 = R.Parameter(kStringTypes[j], 1);
|
| +
|
| + for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
|
| + Node* cmp = R.Binop(ops[k], p0, p1);
|
| + Node* r = R.reduce(cmp);
|
| +
|
| + R.CheckPureBinop(ops[k + 1], r);
|
| + if (k >= 4) {
|
| + // GreaterThan and GreaterThanOrEqual commute the inputs
|
| + // and use the LessThan and LessThanOrEqual operators.
|
| + CHECK_EQ(p1, r->InputAt(0));
|
| + CHECK_EQ(p0, r->InputAt(1));
|
| + } else {
|
| + CHECK_EQ(p0, r->InputAt(0));
|
| + CHECK_EQ(p1, r->InputAt(1));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +static void CheckIsConvertedToNumber(Node* val, Node* converted) {
|
| + if (NodeProperties::GetBounds(val).upper->Is(Type::Number())) {
|
| + CHECK_EQ(val, converted);
|
| + } else {
|
| + if (converted->opcode() == IrOpcode::kNumberConstant) return;
|
| + CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
|
| + CHECK_EQ(val, converted->InputAt(0));
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(NumberComparison) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.LessThan(), R.simplified.NumberLessThan(),
|
| + R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
|
| + R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
|
| + R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
|
| + Type* t0 = kJSTypes[i];
|
| + if (t0->Is(Type::String())) continue; // skip Type::String
|
| + Node* p0 = R.Parameter(t0, 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(kJSTypes); j++) {
|
| + Type* t1 = kJSTypes[j];
|
| + if (t1->Is(Type::String())) continue; // skip Type::String
|
| + Node* p1 = R.Parameter(t1, 1);
|
| +
|
| + for (size_t k = 0; k < ARRAY_SIZE(ops); k += 2) {
|
| + Node* cmp = R.Binop(ops[k], p0, p1);
|
| + Node* r = R.reduce(cmp);
|
| +
|
| + R.CheckPureBinop(ops[k + 1], r);
|
| + if (k >= 4) {
|
| + // GreaterThan and GreaterThanOrEqual commute the inputs
|
| + // and use the LessThan and LessThanOrEqual operators.
|
| + CheckIsConvertedToNumber(p1, r->InputAt(0));
|
| + CheckIsConvertedToNumber(p0, r->InputAt(1));
|
| + } else {
|
| + CheckIsConvertedToNumber(p0, r->InputAt(0));
|
| + CheckIsConvertedToNumber(p1, r->InputAt(1));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(MixedComparison1) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* types[] = {Type::Number(), Type::String(),
|
| + Type::Union(Type::Number(), Type::String(), R.main_zone())};
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
|
| + Node* p0 = R.Parameter(types[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(types); j++) {
|
| + Node* p1 = R.Parameter(types[j], 1);
|
| + {
|
| + Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
|
| + Node* r = R.reduce(cmp);
|
| +
|
| + if (!types[i]->Maybe(Type::String()) ||
|
| + !types[j]->Maybe(Type::String())) {
|
| + if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
|
| + R.CheckPureBinop(R.simplified.StringLessThan(), r);
|
| + } else {
|
| + R.CheckPureBinop(R.simplified.NumberLessThan(), r);
|
| + }
|
| + } else {
|
| + CHECK_EQ(cmp, r); // No reduction of mixed types.
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(ObjectComparison) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Node* p0 = R.Parameter(Type::Object(), 0);
|
| + Node* p1 = R.Parameter(Type::Object(), 1);
|
| +
|
| + Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
|
| + Node* effect_use = R.UseForEffect(cmp);
|
| +
|
| + R.CheckEffectInput(R.start(), cmp);
|
| + R.CheckEffectInput(cmp, effect_use);
|
| +
|
| + Node* r = R.reduce(cmp);
|
| +
|
| + R.CheckPureBinop(R.simplified.NumberLessThan(), r);
|
| +
|
| + Node* i0 = r->InputAt(0);
|
| + Node* i1 = r->InputAt(1);
|
| +
|
| + CHECK_NE(p0, i0);
|
| + CHECK_NE(p1, i1);
|
| + CHECK_EQ(IrOpcode::kJSToNumber, i0->opcode());
|
| + CHECK_EQ(IrOpcode::kJSToNumber, i1->opcode());
|
| +
|
| + // Check effect chain is correct.
|
| + R.CheckEffectInput(R.start(), i0);
|
| + R.CheckEffectInput(i0, i1);
|
| + R.CheckEffectInput(i1, effect_use);
|
| +}
|
| +
|
| +
|
| +TEST(UnaryNot) {
|
| + JSTypedLoweringTester R;
|
| + Operator* opnot = R.javascript.UnaryNot();
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
|
| + Node* r = R.ReduceUnop(opnot, kJSTypes[i]);
|
| + // TODO(titzer): test will break if/when js-typed-lowering constant folds.
|
| + CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(RemoveToNumberEffects) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Node* effect_use = NULL;
|
| + for (int i = 0; i < 10; i++) {
|
| + Node* p0 = R.Parameter(Type::Number());
|
| + Node* ton = R.Unop(R.javascript.ToNumber(), p0);
|
| + effect_use = NULL;
|
| +
|
| + switch (i) {
|
| + case 0:
|
| + effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
|
| + ton, R.start());
|
| + break;
|
| + case 1:
|
| + effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
|
| + ton, R.start());
|
| + break;
|
| + case 2:
|
| + effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
|
| + case 3:
|
| + effect_use = R.graph.NewNode(R.javascript.Add(), ton, ton, R.context(),
|
| + ton, R.start());
|
| + break;
|
| + case 4:
|
| + effect_use = R.graph.NewNode(R.javascript.Add(), p0, p0, R.context(),
|
| + ton, R.start());
|
| + break;
|
| + case 5:
|
| + effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start());
|
| + break;
|
| + case 6:
|
| + effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start());
|
| + }
|
| +
|
| + R.CheckEffectInput(R.start(), ton);
|
| + if (effect_use != NULL) R.CheckEffectInput(ton, effect_use);
|
| +
|
| + Node* r = R.reduce(ton);
|
| + CHECK_EQ(p0, r);
|
| + CHECK_NE(R.start(), r);
|
| +
|
| + if (effect_use != NULL) {
|
| + R.CheckEffectInput(R.start(), effect_use);
|
| + // Check that value uses of ToNumber() do not go to start().
|
| + for (int i = 0; i < effect_use->op()->InputCount(); i++) {
|
| + CHECK_NE(R.start(), effect_use->InputAt(i));
|
| + }
|
| + }
|
| + }
|
| +
|
| + CHECK_EQ(NULL, effect_use); // should have done all cases above.
|
| +}
|
| +
|
| +
|
| +// Helper class for testing the reduction of a single binop.
|
| +class BinopEffectsTester {
|
| + public:
|
| + explicit BinopEffectsTester(Operator* op, Type* t0, Type* t1)
|
| + : R(),
|
| + p0(R.Parameter(t0, 0)),
|
| + p1(R.Parameter(t1, 1)),
|
| + binop(R.Binop(op, p0, p1)),
|
| + effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
|
| + // Effects should be ordered start -> binop -> effect_use
|
| + R.CheckEffectInput(R.start(), binop);
|
| + R.CheckEffectInput(binop, effect_use);
|
| + result = R.reduce(binop);
|
| + }
|
| +
|
| + JSTypedLoweringTester R;
|
| + Node* p0;
|
| + Node* p1;
|
| + Node* binop;
|
| + Node* effect_use;
|
| + Node* result;
|
| +
|
| + void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
|
| +
|
| + void CheckEffectOrdering(Node* n0) {
|
| + R.CheckEffectInput(R.start(), n0);
|
| + R.CheckEffectInput(n0, effect_use);
|
| + }
|
| +
|
| + void CheckEffectOrdering(Node* n0, Node* n1) {
|
| + R.CheckEffectInput(R.start(), n0);
|
| + R.CheckEffectInput(n0, n1);
|
| + R.CheckEffectInput(n1, effect_use);
|
| + }
|
| +
|
| + Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
|
| + return CheckConverted(opcode, result->InputAt(which), effects);
|
| + }
|
| +
|
| + Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
|
| + CHECK_EQ(opcode, node->opcode());
|
| + if (effects) {
|
| + CHECK_LT(0, NodeProperties::GetEffectInputCount(node));
|
| + } else {
|
| + CHECK_EQ(0, NodeProperties::GetEffectInputCount(node));
|
| + }
|
| + return node;
|
| + }
|
| +
|
| + Node* CheckNoOp(int which) {
|
| + CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
|
| + return result->InputAt(which);
|
| + }
|
| +};
|
| +
|
| +
|
| +// Helper function for strict and non-strict equality reductions.
|
| +void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
|
| + Node* r, IrOpcode::Value expected) {
|
| + for (int j = 0; j < 2; j++) {
|
| + Node* p0 = j == 0 ? l : r;
|
| + Node* p1 = j == 1 ? l : r;
|
| +
|
| + {
|
| + Node* eq = strict ? R->graph.NewNode(R->javascript.StrictEqual(), p0, p1)
|
| + : R->Binop(R->javascript.Equal(), p0, p1);
|
| + Node* r = R->reduce(eq);
|
| + R->CheckPureBinop(expected, r);
|
| + }
|
| +
|
| + {
|
| + Node* ne = strict
|
| + ? R->graph.NewNode(R->javascript.StrictNotEqual(), p0, p1)
|
| + : R->Binop(R->javascript.NotEqual(), p0, p1);
|
| + Node* n = R->reduce(ne);
|
| + CHECK_EQ(IrOpcode::kBooleanNot, n->opcode());
|
| + Node* r = n->InputAt(0);
|
| + R->CheckPureBinop(expected, r);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(EqualityForNumbers) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
|
| + Type::Signed32(), Type::Unsigned32(),
|
| + Type::Number()};
|
| +
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(simple_number_types); ++i) {
|
| + Node* p0 = R.Parameter(simple_number_types[i], 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(simple_number_types); ++j) {
|
| + Node* p1 = R.Parameter(simple_number_types[j], 1);
|
| +
|
| + CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
|
| + CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(StrictEqualityForRefEqualTypes) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
|
| + Type::Object(), Type::Receiver()};
|
| +
|
| + Node* p0 = R.Parameter(Type::Any());
|
| + for (size_t i = 0; i < ARRAY_SIZE(types); i++) {
|
| + Node* p1 = R.Parameter(types[i]);
|
| + CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
|
| + }
|
| + // TODO(titzer): Equal(RefEqualTypes)
|
| +}
|
| +
|
| +
|
| +TEST(StringEquality) {
|
| + JSTypedLoweringTester R;
|
| + Node* p0 = R.Parameter(Type::String());
|
| + Node* p1 = R.Parameter(Type::String());
|
| +
|
| + CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
|
| + CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
|
| +}
|
| +
|
| +
|
| +TEST(RemovePureNumberBinopEffects) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.Equal(), R.simplified.NumberEqual(),
|
| + R.javascript.Add(), R.simplified.NumberAdd(),
|
| + R.javascript.Subtract(), R.simplified.NumberSubtract(),
|
| + R.javascript.Multiply(), R.simplified.NumberMultiply(),
|
| + R.javascript.Divide(), R.simplified.NumberDivide(),
|
| + R.javascript.Modulus(), R.simplified.NumberModulus(),
|
| + R.javascript.LessThan(), R.simplified.NumberLessThan(),
|
| + R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
|
| + };
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
|
| + CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + B.CheckNoOp(0);
|
| + B.CheckNoOp(1);
|
| +
|
| + B.CheckEffectsRemoved();
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(OrderNumberBinopEffects1) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.Subtract(), R.simplified.NumberSubtract(),
|
| + R.javascript.Multiply(), R.simplified.NumberMultiply(),
|
| + R.javascript.Divide(), R.simplified.NumberDivide(),
|
| + R.javascript.Modulus(), R.simplified.NumberModulus(),
|
| + };
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Object(), Type::String());
|
| + CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
|
| +
|
| + Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
|
| + Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
|
| +
|
| + CHECK_EQ(B.p0, i0->InputAt(0));
|
| + CHECK_EQ(B.p1, i1->InputAt(0));
|
| +
|
| + // Effects should be ordered start -> i0 -> i1 -> effect_use
|
| + B.CheckEffectOrdering(i0, i1);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(OrderNumberBinopEffects2) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.Add(), R.simplified.NumberAdd(),
|
| + R.javascript.Subtract(), R.simplified.NumberSubtract(),
|
| + R.javascript.Multiply(), R.simplified.NumberMultiply(),
|
| + R.javascript.Divide(), R.simplified.NumberDivide(),
|
| + R.javascript.Modulus(), R.simplified.NumberModulus(),
|
| + };
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Number(), Type::Object());
|
| +
|
| + Node* i0 = B.CheckNoOp(0);
|
| + Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
|
| +
|
| + CHECK_EQ(B.p0, i0);
|
| + CHECK_EQ(B.p1, i1->InputAt(0));
|
| +
|
| + // Effects should be ordered start -> i1 -> effect_use
|
| + B.CheckEffectOrdering(i1);
|
| + }
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Object(), Type::Number());
|
| +
|
| + Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
|
| + Node* i1 = B.CheckNoOp(1);
|
| +
|
| + CHECK_EQ(B.p0, i0->InputAt(0));
|
| + CHECK_EQ(B.p1, i1);
|
| +
|
| + // Effects should be ordered start -> i0 -> effect_use
|
| + B.CheckEffectOrdering(i0);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(OrderCompareEffects) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + Operator* ops[] = {
|
| + R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
|
| + R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
|
| + };
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Object(), Type::String());
|
| + CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
|
| +
|
| + Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
|
| + Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
|
| +
|
| + // Inputs should be commuted.
|
| + CHECK_EQ(B.p1, i0->InputAt(0));
|
| + CHECK_EQ(B.p0, i1->InputAt(0));
|
| +
|
| + // But effects should be ordered start -> i1 -> i0 -> effect_use
|
| + B.CheckEffectOrdering(i1, i0);
|
| + }
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Number(), Type::Object());
|
| +
|
| + Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
|
| + Node* i1 = B.result->InputAt(1);
|
| +
|
| + CHECK_EQ(B.p1, i0->InputAt(0)); // Should be commuted.
|
| + CHECK_EQ(B.p0, i1);
|
| +
|
| + // Effects should be ordered start -> i1 -> effect_use
|
| + B.CheckEffectOrdering(i0);
|
| + }
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(ops); j += 2) {
|
| + BinopEffectsTester B(ops[j], Type::Object(), Type::Number());
|
| +
|
| + Node* i0 = B.result->InputAt(0);
|
| + Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
|
| +
|
| + CHECK_EQ(B.p1, i0); // Should be commuted.
|
| + CHECK_EQ(B.p0, i1->InputAt(0));
|
| +
|
| + // Effects should be ordered start -> i0 -> effect_use
|
| + B.CheckEffectOrdering(i1);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(Int32BinopEffects) {
|
| + JSBitwiseTypedLoweringTester R;
|
| +
|
| + for (int j = 0; j < R.kNumberOps; j += 2) {
|
| + bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
|
| + BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
|
| + CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + B.CheckNoOp(0);
|
| + B.CheckNoOp(1);
|
| +
|
| + B.CheckEffectsRemoved();
|
| + }
|
| +
|
| + for (int j = 0; j < R.kNumberOps; j += 2) {
|
| + bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
|
| + BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number());
|
| + CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
|
| + B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
|
| +
|
| + B.CheckEffectsRemoved();
|
| + }
|
| +
|
| + for (int j = 0; j < R.kNumberOps; j += 2) {
|
| + bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
|
| + BinopEffectsTester B(R.ops[j], Type::Number(), Type::Object());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
|
| + Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
|
| +
|
| + CHECK_EQ(B.p0, i0->InputAt(0));
|
| + Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
|
| +
|
| + CHECK_EQ(B.p1, ii1->InputAt(0));
|
| +
|
| + B.CheckEffectOrdering(ii1);
|
| + }
|
| +
|
| + for (int j = 0; j < R.kNumberOps; j += 2) {
|
| + bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
|
| + BinopEffectsTester B(R.ops[j], Type::Object(), Type::Number());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
|
| + Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
|
| +
|
| + Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
|
| + CHECK_EQ(B.p1, i1->InputAt(0));
|
| +
|
| + CHECK_EQ(B.p0, ii0->InputAt(0));
|
| +
|
| + B.CheckEffectOrdering(ii0);
|
| + }
|
| +
|
| + for (int j = 0; j < R.kNumberOps; j += 2) {
|
| + bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
|
| + BinopEffectsTester B(R.ops[j], Type::Object(), Type::Object());
|
| +
|
| + B.R.CheckPureBinop(B.result->opcode(), B.result);
|
| +
|
| + Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
|
| + Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
|
| +
|
| + Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
|
| + Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
|
| +
|
| + CHECK_EQ(B.p0, ii0->InputAt(0));
|
| + CHECK_EQ(B.p1, ii1->InputAt(0));
|
| +
|
| + B.CheckEffectOrdering(ii0, ii1);
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(UnaryNotEffects) {
|
| + JSTypedLoweringTester R;
|
| + Operator* opnot = R.javascript.UnaryNot();
|
| +
|
| + for (size_t i = 0; i < ARRAY_SIZE(kJSTypes); i++) {
|
| + Node* p0 = R.Parameter(kJSTypes[i], 0);
|
| + Node* orig = R.Unop(opnot, p0);
|
| + Node* effect_use = R.UseForEffect(orig);
|
| + Node* value_use = R.graph.NewNode(R.common.Return(), orig);
|
| + Node* r = R.reduce(orig);
|
| + // TODO(titzer): test will break if/when js-typed-lowering constant folds.
|
| + CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
|
| +
|
| + CHECK_EQ(r, value_use->InputAt(0));
|
| +
|
| + if (r->InputAt(0) == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
|
| + // The original node was turned into a ToBoolean, which has an effect.
|
| + R.CheckEffectInput(R.start(), orig);
|
| + R.CheckEffectInput(orig, effect_use);
|
| + } else {
|
| + // effect should have been removed from this node.
|
| + R.CheckEffectInput(R.start(), effect_use);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(Int32AddNarrowing) {
|
| + {
|
| + JSBitwiseTypedLoweringTester R;
|
| +
|
| + for (int o = 0; o < R.kNumberOps; o += 2) {
|
| + for (size_t i = 0; i < ARRAY_SIZE(kInt32Types); i++) {
|
| + Node* n0 = R.Parameter(kInt32Types[i]);
|
| + for (size_t j = 0; j < ARRAY_SIZE(kInt32Types); j++) {
|
| + Node* n1 = R.Parameter(kInt32Types[j]);
|
| + Node* one = R.graph.NewNode(R.common.NumberConstant(1));
|
| +
|
| + for (int l = 0; l < 2; l++) {
|
| + Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
|
| + Node* or_node =
|
| + R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
|
| + Node* r = R.reduce(or_node);
|
| +
|
| + CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
|
| + CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
|
| + bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
|
| +
|
| + Type* add_type = NodeProperties::GetBounds(add_node).upper;
|
| + CHECK(add_type->Is(I32Type(is_signed)));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + {
|
| + JSBitwiseShiftTypedLoweringTester R;
|
| +
|
| + for (int o = 0; o < R.kNumberOps; o += 2) {
|
| + for (size_t i = 0; i < ARRAY_SIZE(kInt32Types); i++) {
|
| + Node* n0 = R.Parameter(kInt32Types[i]);
|
| + for (size_t j = 0; j < ARRAY_SIZE(kInt32Types); j++) {
|
| + Node* n1 = R.Parameter(kInt32Types[j]);
|
| + Node* one = R.graph.NewNode(R.common.NumberConstant(1));
|
| +
|
| + for (int l = 0; l < 2; l++) {
|
| + Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
|
| + Node* or_node =
|
| + R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
|
| + Node* r = R.reduce(or_node);
|
| +
|
| + CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
|
| + CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
|
| + bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
|
| +
|
| + Type* add_type = NodeProperties::GetBounds(add_node).upper;
|
| + CHECK(add_type->Is(I32Type(is_signed)));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(Int32AddNarrowingNotOwned) {
|
| + JSBitwiseTypedLoweringTester R;
|
| +
|
| + for (int o = 0; o < R.kNumberOps; o += 2) {
|
| + Node* n0 = R.Parameter(I32Type(R.signedness[o]));
|
| + Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
|
| + Node* one = R.graph.NewNode(R.common.NumberConstant(1));
|
| +
|
| + Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
|
| + Node* or_node = R.Binop(R.ops[o], add_node, one);
|
| + Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
|
| + Node* r = R.reduce(or_node);
|
| + CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
|
| + // Should not be reduced to Int32Add because of the other number add.
|
| + CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
|
| + // Conversion to int32 should be done.
|
| + CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
|
| + CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
|
| + // The other use should also not be touched.
|
| + CHECK_EQ(add_node, other_use->InputAt(0));
|
| + CHECK_EQ(one, other_use->InputAt(1));
|
| + }
|
| +}
|
| +
|
| +
|
| +TEST(Int32Comparisons) {
|
| + JSTypedLoweringTester R;
|
| +
|
| + struct Entry {
|
| + Operator* js_op;
|
| + Operator* uint_op;
|
| + Operator* int_op;
|
| + Operator* num_op;
|
| + bool commute;
|
| + };
|
| +
|
| + Entry ops[] = {
|
| + {R.javascript.LessThan(), R.machine.Uint32LessThan(),
|
| + R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false},
|
| + {R.javascript.LessThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
|
| + R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
|
| + false},
|
| + {R.javascript.GreaterThan(), R.machine.Uint32LessThan(),
|
| + R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true},
|
| + {R.javascript.GreaterThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
|
| + R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
|
| + true}};
|
| +
|
| + for (size_t o = 0; o < ARRAY_SIZE(ops); o++) {
|
| + for (size_t i = 0; i < ARRAY_SIZE(kNumberTypes); i++) {
|
| + Type* t0 = kNumberTypes[i];
|
| + Node* p0 = R.Parameter(t0, 0);
|
| +
|
| + for (size_t j = 0; j < ARRAY_SIZE(kNumberTypes); j++) {
|
| + Type* t1 = kNumberTypes[j];
|
| + Node* p1 = R.Parameter(t1, 1);
|
| +
|
| + Node* cmp = R.Binop(ops[o].js_op, p0, p1);
|
| + Node* r = R.reduce(cmp);
|
| +
|
| + Operator* expected;
|
| + if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) {
|
| + expected = ops[o].uint_op;
|
| + } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) {
|
| + expected = ops[o].int_op;
|
| + } else {
|
| + expected = ops[o].num_op;
|
| + }
|
| + R.CheckPureBinop(expected, r);
|
| + if (ops[o].commute) {
|
| + CHECK_EQ(p1, r->InputAt(0));
|
| + CHECK_EQ(p0, r->InputAt(1));
|
| + } else {
|
| + CHECK_EQ(p0, r->InputAt(0));
|
| + CHECK_EQ(p1, r->InputAt(1));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|