Index: llvm/include/llvm/IR/ConstantRange.h =================================================================== --- llvm/include/llvm/IR/ConstantRange.h +++ llvm/include/llvm/IR/ConstantRange.h @@ -356,6 +356,11 @@ /// \p Other. ConstantRange udiv(const ConstantRange &Other) const; + /// Return a new range representing the possible values resulting + /// from an unsigned remainder operation of a value in this range and a + /// value in \p Other. + ConstantRange urem(const ConstantRange &Other) const; + /// Return a new range representing the possible values resulting /// from a binary-and of a value in this range by a value in \p Other. ConstantRange binaryAnd(const ConstantRange &Other) const; Index: llvm/lib/IR/ConstantRange.cpp =================================================================== --- llvm/lib/IR/ConstantRange.cpp +++ llvm/lib/IR/ConstantRange.cpp @@ -794,6 +794,8 @@ return multiply(Other); case Instruction::UDiv: return udiv(Other); + case Instruction::URem: + return urem(Other); case Instruction::Shl: return shl(Other); case Instruction::LShr: @@ -991,6 +993,18 @@ return getNonEmpty(std::move(Lower), std::move(Upper)); } +ConstantRange ConstantRange::urem(const ConstantRange &RHS) const { + if (isEmptySet() || RHS.isEmptySet() || RHS.getUnsignedMax().isNullValue()) + return getEmpty(); + + // L % R for L < R is L. + APInt Lower = getUnsignedMax().ult(RHS.getUnsignedMin()) + ? getUnsignedMin() : APInt::getNullValue(getBitWidth()); + // L % R is <= L and < R. + APInt Upper = APIntOps::umin(getUnsignedMax(), RHS.getUnsignedMax() - 1) + 1; + return getNonEmpty(std::move(Lower), std::move(Upper)); +} + ConstantRange ConstantRange::binaryAnd(const ConstantRange &Other) const { if (isEmptySet() || Other.isEmptySet()) Index: llvm/unittests/IR/ConstantRangeTest.cpp =================================================================== --- llvm/unittests/IR/ConstantRangeTest.cpp +++ llvm/unittests/IR/ConstantRangeTest.cpp @@ -759,6 +759,33 @@ EXPECT_EQ(Wrap.udiv(Wrap), Full); } +TEST_F(ConstantRangeTest, URem) { + EXPECT_EQ(Full.urem(Empty), Empty); + EXPECT_EQ(Empty.urem(Full), Empty); + // urem by zero is poison. + EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0))), Empty); + // urem by full range doesn't contain MaxValue. + EXPECT_EQ(Full.urem(Full), ConstantRange(APInt(16, 0), APInt(16, 0xffff))); + // urem is upper bounded by maximum RHS minus one. + EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0), APInt(16, 123))), + ConstantRange(APInt(16, 0), APInt(16, 122))); + // urem is upper bounded by maximum LHS. + EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 123)).urem(Full), + ConstantRange(APInt(16, 0), APInt(16, 123))); + // If the LHS is always lower than the RHS, the result is the LHS. + EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20)) + .urem(ConstantRange(APInt(16, 20), APInt(16, 30))), + ConstantRange(APInt(16, 10), APInt(16, 20))); + // It has to be strictly lower, otherwise the top value may wrap to zero. + EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20)) + .urem(ConstantRange(APInt(16, 19), APInt(16, 30))), + ConstantRange(APInt(16, 0), APInt(16, 20))); + // [12, 14] % 10 is [2, 4], but we conservatively compute [0, 9]. + EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15)) + .urem(ConstantRange(APInt(16, 10))), + ConstantRange(APInt(16, 0), APInt(16, 10))); +} + TEST_F(ConstantRangeTest, Shl) { ConstantRange Some2(APInt(16, 0xfff), APInt(16, 0x8000)); ConstantRange WrapNullMax(APInt(16, 0x1), APInt(16, 0x0));