From f53576376385bdd2db54f183c1750213ba036ea9 Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Sun, 26 Oct 2014 12:49:56 +0000 Subject: [PATCH] [turbofan] Implement the correct semantics for integer division/modulus. Also fix the sdiv/udiv instructions on ARM as a nice side effect. TEST=cctest,unittests R=jarin@chromium.org Review URL: https://codereview.chromium.org/677483005 Cr-Commit-Position: refs/heads/master@{#24888} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24888 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/simulator-arm.cc | 9 +- src/base/bits.cc | 16 +++ src/base/bits.h | 26 ++++ src/compiler/machine-operator-reducer.cc | 110 ++++++++------ src/compiler/machine-operator-reducer.h | 6 + test/cctest/test-assembler-arm.cc | 74 ++++++++-- test/unittests/base/bits-unittest.cc | 52 +++++++ .../compiler/machine-operator-reducer-unittest.cc | 160 ++++++++++++++++++++- 8 files changed, 391 insertions(+), 62 deletions(-) diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 5105f1e..ceabe78 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -2741,15 +2741,12 @@ void Simulator::DecodeType3(Instruction* instr) { int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t ret_val = 0; - DCHECK(rs_val != 0); // udiv if (instr->Bit(21) == 0x1) { - ret_val = static_cast(static_cast(rm_val) / - static_cast(rs_val)); - } else if ((rm_val == kMinInt) && (rs_val == -1)) { - ret_val = kMinInt; + ret_val = bit_cast(base::bits::UnsignedDiv32( + bit_cast(rm_val), bit_cast(rs_val))); } else { - ret_val = rm_val / rs_val; + ret_val = base::bits::SignedDiv32(rm_val, rs_val); } set_register(rn, ret_val); return; diff --git a/src/base/bits.cc b/src/base/bits.cc index 2818b93..74d747f 100644 --- a/src/base/bits.cc +++ b/src/base/bits.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "src/base/bits.h" + +#include + #include "src/base/logging.h" namespace v8 { @@ -32,6 +35,19 @@ int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc) { bit_cast(SignedMulHigh32(lhs, rhs))); } + +int32_t SignedDiv32(int32_t lhs, int32_t rhs) { + if (rhs == 0) return 0; + if (rhs == -1) return -lhs; + return lhs / rhs; +} + + +int32_t SignedMod32(int32_t lhs, int32_t rhs) { + if (rhs == 0 || rhs == -1) return 0; + return lhs % rhs; +} + } // namespace bits } // namespace base } // namespace v8 diff --git a/src/base/bits.h b/src/base/bits.h index da4d1f2..0f4d4c7 100644 --- a/src/base/bits.h +++ b/src/base/bits.h @@ -199,6 +199,32 @@ int32_t SignedMulHigh32(int32_t lhs, int32_t rhs); // adds the accumulate value |acc|. int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc); + +// SignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient +// truncated to int32. If |rhs| is zero, then zero is returned. If |lhs| +// is minint and |rhs| is -1, it returns minint. +int32_t SignedDiv32(int32_t lhs, int32_t rhs); + + +// SignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder +// truncated to int32. If either |rhs| is zero or |lhs| is minint and |rhs| +// is -1, it returns zero. +int32_t SignedMod32(int32_t lhs, int32_t rhs); + + +// UnsignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient +// truncated to uint32. If |rhs| is zero, then zero is returned. +inline uint32_t UnsignedDiv32(uint32_t lhs, uint32_t rhs) { + return rhs ? lhs / rhs : 0u; +} + + +// UnsignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder +// truncated to uint32. If |rhs| is zero, then zero is returned. +inline uint32_t UnsignedMod32(uint32_t lhs, uint32_t rhs) { + return rhs ? lhs % rhs : 0u; +} + } // namespace bits } // namespace base } // namespace v8 diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc index a8b5edd..f285b8a 100644 --- a/src/compiler/machine-operator-reducer.cc +++ b/src/compiler/machine-operator-reducer.cc @@ -58,6 +58,11 @@ Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) { } +Node* MachineOperatorReducer::Word32Equal(Node* lhs, Node* rhs) { + return graph()->NewNode(machine()->Word32Equal(), lhs, rhs); +} + + Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) { return graph()->NewNode(machine()->Int32Add(), lhs, rhs); } @@ -299,40 +304,12 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { } case IrOpcode::kInt32Div: return ReduceInt32Div(node); - case IrOpcode::kUint32Div: { - Uint32BinopMatcher m(node); - if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x - // TODO(turbofan): if (m.left().Is(0)) - // TODO(turbofan): if (m.right().Is(0)) - // TODO(turbofan): if (m.LeftEqualsRight()) - if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K - return ReplaceInt32(m.left().Value() / m.right().Value()); - } - if (m.right().IsPowerOf2()) { // x / 2^n => x >> n - node->set_op(machine()->Word32Shr()); - node->ReplaceInput(1, Int32Constant(WhichPowerOf2(m.right().Value()))); - return Changed(node); - } - break; - } + case IrOpcode::kUint32Div: + return ReduceUint32Div(node); case IrOpcode::kInt32Mod: return ReduceInt32Mod(node); - case IrOpcode::kUint32Mod: { - Uint32BinopMatcher m(node); - if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0 - // TODO(turbofan): if (m.left().Is(0)) - // TODO(turbofan): if (m.right().Is(0)) - // TODO(turbofan): if (m.LeftEqualsRight()) - if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K - return ReplaceInt32(m.left().Value() % m.right().Value()); - } - if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1 - node->set_op(machine()->Word32And()); - node->ReplaceInput(1, Int32Constant(m.right().Value() - 1)); - return Changed(node); - } - break; - } + case IrOpcode::kUint32Mod: + return ReduceUint32Mod(node); case IrOpcode::kInt32LessThan: { Int32BinopMatcher m(node); if (m.IsFoldable()) { // K < K => K @@ -554,13 +531,16 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) { Int32BinopMatcher m(node); + if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0 if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0 if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x - // TODO(turbofan): if (m.left().Is(0)) - // TODO(turbofan): if (m.LeftEqualsRight()) - if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K - if (m.right().Is(-1)) return ReplaceInt32(-m.left().Value()); - return ReplaceInt32(m.left().Value() / m.right().Value()); + if (m.IsFoldable()) { // K / K => K + return ReplaceInt32( + base::bits::SignedDiv32(m.left().Value(), m.right().Value())); + } + if (m.LeftEqualsRight()) { // x / x => x != 0 + Node* const zero = Int32Constant(0); + return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero)); } if (m.right().Is(-1)) { // x / -1 => 0 - x node->set_op(machine()->Int32Sub()); @@ -595,15 +575,38 @@ Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) { } +Reduction MachineOperatorReducer::ReduceUint32Div(Node* node) { + Uint32BinopMatcher m(node); + if (m.left().Is(0)) return Replace(m.left().node()); // 0 / x => 0 + if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0 + if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x + if (m.IsFoldable()) { // K / K => K + return ReplaceUint32( + base::bits::UnsignedDiv32(m.left().Value(), m.right().Value())); + } + if (m.LeftEqualsRight()) { // x / x => x != 0 + Node* const zero = Int32Constant(0); + return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero)); + } + if (m.right().IsPowerOf2()) { // x / 2^n => x >> n + node->set_op(machine()->Word32Shr()); + node->ReplaceInput(1, Uint32Constant(WhichPowerOf2(m.right().Value()))); + return Changed(node); + } + return NoChange(); +} + + Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) { Int32BinopMatcher m(node); - if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0 - if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0 - // TODO(turbofan): if (m.left().Is(0)) - // TODO(turbofan): if (m.right().Is(0)) - // TODO(turbofan): if (m.LeftEqualsRight()) - if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K - return ReplaceInt32(m.left().Value() % m.right().Value()); + if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0 + if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0 + if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0 + if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0 + if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0 + if (m.IsFoldable()) { // K % K => K + return ReplaceInt32( + base::bits::SignedMod32(m.left().Value(), m.right().Value())); } if (m.right().HasValue()) { Node* const dividend = m.left().node(); @@ -639,6 +642,25 @@ Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) { } +Reduction MachineOperatorReducer::ReduceUint32Mod(Node* node) { + Uint32BinopMatcher m(node); + if (m.left().Is(0)) return Replace(m.left().node()); // 0 % x => 0 + if (m.right().Is(0)) return Replace(m.right().node()); // x % 0 => 0 + if (m.right().Is(1)) return ReplaceUint32(0); // x % 1 => 0 + if (m.LeftEqualsRight()) return ReplaceInt32(0); // x % x => 0 + if (m.IsFoldable()) { // K % K => K + return ReplaceUint32( + base::bits::UnsignedMod32(m.left().Value(), m.right().Value())); + } + if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1 + node->set_op(machine()->Word32And()); + node->ReplaceInput(1, Uint32Constant(m.right().Value() - 1)); + return Changed(node); + } + return NoChange(); +} + + Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) { switch (node->opcode()) { case IrOpcode::kInt32AddWithOverflow: { diff --git a/src/compiler/machine-operator-reducer.h b/src/compiler/machine-operator-reducer.h index 92c2337..475ed2e 100644 --- a/src/compiler/machine-operator-reducer.h +++ b/src/compiler/machine-operator-reducer.h @@ -37,6 +37,7 @@ class MachineOperatorReducer FINAL : public Reducer { Node* Word32And(Node* lhs, uint32_t rhs); Node* Word32Sar(Node* lhs, uint32_t rhs); Node* Word32Shr(Node* lhs, uint32_t rhs); + Node* Word32Equal(Node* lhs, Node* rhs); Node* Int32Add(Node* lhs, Node* rhs); Node* Int32Sub(Node* lhs, Node* rhs); Node* Int32Mul(Node* lhs, Node* rhs); @@ -53,12 +54,17 @@ class MachineOperatorReducer FINAL : public Reducer { Reduction ReplaceInt32(int32_t value) { return Replace(Int32Constant(value)); } + Reduction ReplaceUint32(uint32_t value) { + return Replace(Uint32Constant(value)); + } Reduction ReplaceInt64(int64_t value) { return Replace(Int64Constant(value)); } Reduction ReduceInt32Div(Node* node); + Reduction ReduceUint32Div(Node* node); Reduction ReduceInt32Mod(Node* node); + Reduction ReduceUint32Mod(Node* node); Reduction ReduceProjection(size_t index, Node* node); Graph* graph() const; diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc index 8c1a4d9..9079d5a 100644 --- a/test/cctest/test-assembler-arm.cc +++ b/test/cctest/test-assembler-arm.cc @@ -1442,21 +1442,19 @@ TEST(17) { CHECK_EQ(expected_, t.result); -TEST(18) { +TEST(sdiv) { // Test the sdiv. CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); HandleScope scope(isolate); - - typedef struct { - uint32_t dividend; - uint32_t divisor; - uint32_t result; - } T; - T t; - Assembler assm(isolate, NULL, 0); + struct T { + int32_t dividend; + int32_t divisor; + int32_t result; + } t; + if (CpuFeatures::IsSupported(SUDIV)) { CpuFeatureScope scope(&assm, SUDIV); @@ -1480,6 +1478,8 @@ TEST(18) { #endif F3 f = FUNCTION_CAST(code->entry()); Object* dummy; + TEST_SDIV(0, kMinInt, 0); + TEST_SDIV(0, 1024, 0); TEST_SDIV(1073741824, kMinInt, -2); TEST_SDIV(kMinInt, kMinInt, -1); TEST_SDIV(5, 10, 2); @@ -1498,6 +1498,62 @@ TEST(18) { #undef TEST_SDIV +#define TEST_UDIV(expected_, dividend_, divisor_) \ + t.dividend = dividend_; \ + t.divisor = divisor_; \ + t.result = 0; \ + dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0); \ + CHECK_EQ(expected_, t.result); + + +TEST(udiv) { + // Test the udiv. + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + Assembler assm(isolate, NULL, 0); + + struct T { + uint32_t dividend; + uint32_t divisor; + uint32_t result; + } t; + + if (CpuFeatures::IsSupported(SUDIV)) { + CpuFeatureScope scope(&assm, SUDIV); + + __ mov(r3, Operand(r0)); + + __ ldr(r0, MemOperand(r3, OFFSET_OF(T, dividend))); + __ ldr(r1, MemOperand(r3, OFFSET_OF(T, divisor))); + + __ sdiv(r2, r0, r1); + __ str(r2, MemOperand(r3, OFFSET_OF(T, result))); + + __ bx(lr); + + CodeDesc desc; + assm.GetCode(&desc); + Handle code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); +#ifdef DEBUG + OFStream os(stdout); + code->Print(os); +#endif + F3 f = FUNCTION_CAST(code->entry()); + Object* dummy; + TEST_UDIV(0, 0, 0); + TEST_UDIV(0, 1024, 0); + TEST_UDIV(5, 10, 2); + TEST_UDIV(3, 10, 3); + USE(dummy); + } +} + + +#undef TEST_UDIV + + TEST(smmla) { CcTest::InitializeVM(); Isolate* const isolate = CcTest::i_isolate(); diff --git a/test/unittests/base/bits-unittest.cc b/test/unittests/base/bits-unittest.cc index be41007..9caba84 100644 --- a/test/unittests/base/bits-unittest.cc +++ b/test/unittests/base/bits-unittest.cc @@ -224,6 +224,58 @@ TEST(Bits, SignedMulHighAndAdd32) { } } + +TEST(Bits, SignedDiv32) { + EXPECT_EQ(std::numeric_limits::min(), + SignedDiv32(std::numeric_limits::min(), -1)); + EXPECT_EQ(std::numeric_limits::max(), + SignedDiv32(std::numeric_limits::max(), 1)); + TRACED_FORRANGE(int32_t, i, 0, 50) { + EXPECT_EQ(0, SignedDiv32(i, 0)); + TRACED_FORRANGE(int32_t, j, 1, i) { + EXPECT_EQ(1, SignedDiv32(j, j)); + EXPECT_EQ(i / j, SignedDiv32(i, j)); + EXPECT_EQ(-i / j, SignedDiv32(i, -j)); + } + } +} + + +TEST(Bits, SignedMod32) { + EXPECT_EQ(0, SignedMod32(std::numeric_limits::min(), -1)); + EXPECT_EQ(0, SignedMod32(std::numeric_limits::max(), 1)); + TRACED_FORRANGE(int32_t, i, 0, 50) { + EXPECT_EQ(0, SignedMod32(i, 0)); + TRACED_FORRANGE(int32_t, j, 1, i) { + EXPECT_EQ(0, SignedMod32(j, j)); + EXPECT_EQ(i % j, SignedMod32(i, j)); + EXPECT_EQ(i % j, SignedMod32(i, -j)); + } + } +} + + +TEST(Bits, UnsignedDiv32) { + TRACED_FORRANGE(uint32_t, i, 0, 50) { + EXPECT_EQ(0u, UnsignedDiv32(i, 0)); + TRACED_FORRANGE(uint32_t, j, i + 1, 100) { + EXPECT_EQ(1u, UnsignedDiv32(j, j)); + EXPECT_EQ(i / j, UnsignedDiv32(i, j)); + } + } +} + + +TEST(Bits, UnsignedMod32) { + TRACED_FORRANGE(uint32_t, i, 0, 50) { + EXPECT_EQ(0u, UnsignedMod32(i, 0)); + TRACED_FORRANGE(uint32_t, j, i + 1, 100) { + EXPECT_EQ(0u, UnsignedMod32(j, j)); + EXPECT_EQ(i % j, UnsignedMod32(i, j)); + } + } +} + } // namespace bits } // namespace base } // namespace v8 diff --git a/test/unittests/compiler/machine-operator-reducer-unittest.cc b/test/unittests/compiler/machine-operator-reducer-unittest.cc index a5b1cfe..461c2be 100644 --- a/test/unittests/compiler/machine-operator-reducer-unittest.cc +++ b/test/unittests/compiler/machine-operator-reducer-unittest.cc @@ -731,19 +731,111 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { } +TEST_F(MachineOperatorReducerTest, Int32DivWithParameters) { + Node* const p0 = Parameter(0); + Reduction const r = Reduce(graph()->NewNode(machine()->Int32Div(), p0, p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsWord32Equal(IsWord32Equal(p0, IsInt32Constant(0)), IsInt32Constant(0))); +} + + +// ----------------------------------------------------------------------------- +// Uint32Div + + +TEST_F(MachineOperatorReducerTest, Uint32DivWithConstant) { + Node* const p0 = Parameter(0); + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Div(), Int32Constant(0), p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(0))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(1))); + ASSERT_TRUE(r.Changed()); + EXPECT_EQ(r.replacement(), p0); + } + TRACED_FOREACH(uint32_t, dividend, kUint32Values) { + TRACED_FOREACH(uint32_t, divisor, kUint32Values) { + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), + Uint32Constant(dividend), + Uint32Constant(divisor))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsInt32Constant(bit_cast( + base::bits::UnsignedDiv32(dividend, divisor)))); + } + } + TRACED_FORRANGE(uint32_t, shift, 1, 31) { + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0, + Uint32Constant(1u << shift))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsWord32Shr(p0, IsInt32Constant(bit_cast(shift)))); + } +} + + +TEST_F(MachineOperatorReducerTest, Uint32DivWithParameters) { + Node* const p0 = Parameter(0); + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0, p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsWord32Equal(IsWord32Equal(p0, IsInt32Constant(0)), IsInt32Constant(0))); +} + + // ----------------------------------------------------------------------------- // Int32Mod TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { Node* const p0 = Parameter(0); - static const int32_t kOnes[] = {-1, 1}; - TRACED_FOREACH(int32_t, one, kOnes) { + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Mod(), Int32Constant(0), p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(0))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(one))); + Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1))); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(-1))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + TRACED_FOREACH(int32_t, dividend, kInt32Values) { + TRACED_FOREACH(int32_t, divisor, kInt32Values) { + Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(), + Int32Constant(dividend), + Int32Constant(divisor))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsInt32Constant(base::bits::SignedMod32(dividend, divisor))); + } + } TRACED_FORRANGE(int32_t, shift, 1, 30) { Reduction const r = Reduce( graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1 << shift))); @@ -797,6 +889,68 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { } +TEST_F(MachineOperatorReducerTest, Int32ModWithParameters) { + Node* const p0 = Parameter(0); + Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(), p0, p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); +} + + +// ----------------------------------------------------------------------------- +// Uint32Mod + + +TEST_F(MachineOperatorReducerTest, Uint32ModWithConstant) { + Node* const p0 = Parameter(0); + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(0))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Mod(), Int32Constant(0), p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + { + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(1))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); + } + TRACED_FOREACH(uint32_t, dividend, kUint32Values) { + TRACED_FOREACH(uint32_t, divisor, kUint32Values) { + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), + Uint32Constant(dividend), + Uint32Constant(divisor))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsInt32Constant(bit_cast( + base::bits::UnsignedMod32(dividend, divisor)))); + } + } + TRACED_FORRANGE(uint32_t, shift, 1, 31) { + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, + Uint32Constant(1u << shift))); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), + IsWord32And(p0, IsInt32Constant( + bit_cast((1u << shift) - 1u)))); + } +} + + +TEST_F(MachineOperatorReducerTest, Uint32ModWithParameters) { + Node* const p0 = Parameter(0); + Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, p0)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsInt32Constant(0)); +} + + // ----------------------------------------------------------------------------- // Int32AddWithOverflow -- 2.7.4