+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberMultiply_NonTruncating) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(111);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ t.Return(mul);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(static_cast<double>(*i) / k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToUint32) {
+ uint32_t constants[] = {100, 10, 1, 1, 2, 4, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i / k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_BadConstants) {
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(-1);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Sub, use->InputAt(0)->opcode());
+ }
+
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+
+ {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Mod, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* mod = t.NumberModulus(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(std::fmod(static_cast<double>(*i), k));
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), mod);
+ Node* ret = t.Return(trunc);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, ret->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToUint32) {
+ uint32_t constants[] = {1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* mod =
+ t.NumberModulus(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i % k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_Int32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ t.Return(mod);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mod, mod->opcode()); // Pesky -0 behavior.
+ }
+}
+
+
+TEST(NumberModulus_Uint32) {
+ const double kConstants[] = {2, 100, 1000, 1024, 2048};
+ const MachineType kTypes[] = {kMachInt32, kMachUint32};
+
+ for (auto const type : kTypes) {
+ for (auto const c : kConstants) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(c);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, type);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, use->InputAt(0)->opcode());
+ }
+ }
+}
+
+
+TEST(PhiRepresentation) {
+ HandleAndZoneScope scope;
+ Zone* z = scope.main_zone();
+
+ Factory* f = z->isolate()->factory();
+ Handle<Object> range_min = f->NewNumber(-1e13);
+ Handle<Object> range_max = f->NewNumber(1e+15);
+ Type* range = Type::Range(range_min, range_max, z);
+
+ struct TestData {
+ Type* arg1;
+ Type* arg2;
+ MachineType use;
+ MachineTypeUnion expected;
+ };
+
+ TestData test_data[] = {
+ {Type::Signed32(), Type::Unsigned32(), kMachInt32,
+ kRepWord32 | kTypeNumber},
+ {range, range, kMachUint32, kRepWord32 | kTypeNumber},
+ {Type::Signed32(), Type::Signed32(), kMachInt32, kMachInt32},
+ {Type::Unsigned32(), Type::Unsigned32(), kMachInt32, kMachUint32},
+ {Type::Number(), Type::Signed32(), kMachInt32, kMachFloat64},
+ {Type::Signed32(), Type::String(), kMachInt32, kMachAnyTagged}};
+
+ for (auto const d : test_data) {
+ TestingGraph t(d.arg1, d.arg2, Type::Boolean());
+
+ Node* br = t.graph()->NewNode(t.common()->Branch(), t.p2, t.start);
+ Node* tb = t.graph()->NewNode(t.common()->IfTrue(), br);
+ Node* fb = t.graph()->NewNode(t.common()->IfFalse(), br);
+ Node* m = t.graph()->NewNode(t.common()->Merge(2), tb, fb);
+
+ Node* phi =
+ t.graph()->NewNode(t.common()->Phi(kMachAnyTagged, 2), t.p0, t.p1, m);
+
+ Bounds phi_bounds = Bounds::Either(Bounds(d.arg1), Bounds(d.arg2), z);
+ NodeProperties::SetBounds(phi, phi_bounds);
+
+ Node* use = t.Use(phi, d.use);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(d.expected, OpParameter<MachineType>(phi));
+ }