[turbofan] Implement the correct semantics for integer division/modulus.
authorbmeurer@chromium.org <bmeurer@chromium.org>
Sun, 26 Oct 2014 12:49:56 +0000 (12:49 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org>
Sun, 26 Oct 2014 12:50:46 +0000 (12:50 +0000)
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
src/base/bits.cc
src/base/bits.h
src/compiler/machine-operator-reducer.cc
src/compiler/machine-operator-reducer.h
test/cctest/test-assembler-arm.cc
test/unittests/base/bits-unittest.cc
test/unittests/compiler/machine-operator-reducer-unittest.cc

index 5105f1e..ceabe78 100644 (file)
@@ -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<int32_t>(static_cast<uint32_t>(rm_val) /
-                                             static_cast<uint32_t>(rs_val));
-            } else if ((rm_val == kMinInt) && (rs_val == -1)) {
-              ret_val = kMinInt;
+              ret_val = bit_cast<int32_t>(base::bits::UnsignedDiv32(
+                  bit_cast<uint32_t>(rm_val), bit_cast<uint32_t>(rs_val)));
             } else {
-              ret_val = rm_val / rs_val;
+              ret_val = base::bits::SignedDiv32(rm_val, rs_val);
             }
             set_register(rn, ret_val);
             return;
index 2818b93..74d747f 100644 (file)
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "src/base/bits.h"
+
+#include <limits>
+
 #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<uint32_t>(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
index da4d1f2..0f4d4c7 100644 (file)
@@ -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
index a8b5edd..f285b8a 100644 (file)
@@ -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: {
index 92c2337..475ed2e 100644 (file)
@@ -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;
index 8c1a4d9..9079d5a 100644 (file)
@@ -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<F3>(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> code = isolate->factory()->NewCode(
+        desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef DEBUG
+    OFStream os(stdout);
+    code->Print(os);
+#endif
+    F3 f = FUNCTION_CAST<F3>(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();
index be41007..9caba84 100644 (file)
@@ -224,6 +224,58 @@ TEST(Bits, SignedMulHighAndAdd32) {
   }
 }
 
+
+TEST(Bits, SignedDiv32) {
+  EXPECT_EQ(std::numeric_limits<int32_t>::min(),
+            SignedDiv32(std::numeric_limits<int32_t>::min(), -1));
+  EXPECT_EQ(std::numeric_limits<int32_t>::max(),
+            SignedDiv32(std::numeric_limits<int32_t>::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<int32_t>::min(), -1));
+  EXPECT_EQ(0, SignedMod32(std::numeric_limits<int32_t>::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
index a5b1cfe..461c2be 100644 (file)
@@ -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<int32_t>(
+                      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<int32_t>(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<int32_t>(
+                      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<int32_t>((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