Skip to content

Commit f945429

Browse files
committed
[ConstantRange] Add urem support
Add urem support to ConstantRange, so we can handle in in LVI. This is an approximate implementation that tries to capture the most useful conditions: If the LHS is always strictly smaller than the RHS, then the urem is a no-op and the result is the same as the LHS range. Otherwise the lower bound is zero and the upper bound is min(LHSMax, RHSMax - 1). Differential Revision: https://reviews.llvm.org/D60952 llvm-svn: 359019
1 parent 4a52397 commit f945429

File tree

3 files changed

+74
-8
lines changed

3 files changed

+74
-8
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,11 @@ class LLVM_NODISCARD ConstantRange {
356356
/// \p Other.
357357
ConstantRange udiv(const ConstantRange &Other) const;
358358

359+
/// Return a new range representing the possible values resulting
360+
/// from an unsigned remainder operation of a value in this range and a
361+
/// value in \p Other.
362+
ConstantRange urem(const ConstantRange &Other) const;
363+
359364
/// Return a new range representing the possible values resulting
360365
/// from a binary-and of a value in this range by a value in \p Other.
361366
ConstantRange binaryAnd(const ConstantRange &Other) const;

llvm/lib/IR/ConstantRange.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,8 @@ ConstantRange ConstantRange::binaryOp(Instruction::BinaryOps BinOp,
794794
return multiply(Other);
795795
case Instruction::UDiv:
796796
return udiv(Other);
797+
case Instruction::URem:
798+
return urem(Other);
797799
case Instruction::Shl:
798800
return shl(Other);
799801
case Instruction::LShr:
@@ -991,6 +993,19 @@ ConstantRange::udiv(const ConstantRange &RHS) const {
991993
return getNonEmpty(std::move(Lower), std::move(Upper));
992994
}
993995

996+
ConstantRange ConstantRange::urem(const ConstantRange &RHS) const {
997+
if (isEmptySet() || RHS.isEmptySet() || RHS.getUnsignedMax().isNullValue())
998+
return getEmpty();
999+
1000+
// L % R for L < R is L.
1001+
if (getUnsignedMax().ult(RHS.getUnsignedMin()))
1002+
return *this;
1003+
1004+
// L % R is <= L and < R.
1005+
APInt Upper = APIntOps::umin(getUnsignedMax(), RHS.getUnsignedMax() - 1) + 1;
1006+
return getNonEmpty(APInt::getNullValue(getBitWidth()), std::move(Upper));
1007+
}
1008+
9941009
ConstantRange
9951010
ConstantRange::binaryAnd(const ConstantRange &Other) const {
9961011
if (isEmptySet() || Other.isEmptySet())

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,19 @@ static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {
5959
}
6060

6161
template<typename Fn1, typename Fn2>
62-
static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
62+
static void TestUnsignedBinOpExhaustive(
63+
Fn1 RangeFn, Fn2 IntFn,
64+
bool SkipZeroRHS = false, bool CorrectnessOnly = false) {
6365
unsigned Bits = 4;
6466
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
6567
const ConstantRange &CR2) {
66-
ConstantRange CR = RangeFn(CR1, CR2);
67-
if (CR1.isEmptySet() || CR2.isEmptySet()) {
68-
EXPECT_TRUE(CR.isEmptySet());
69-
return;
70-
}
71-
7268
APInt Min = APInt::getMaxValue(Bits);
7369
APInt Max = APInt::getMinValue(Bits);
7470
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
7571
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
72+
if (SkipZeroRHS && N2 == 0)
73+
return;
74+
7675
APInt N = IntFn(N1, N2);
7776
if (N.ult(Min))
7877
Min = N;
@@ -81,7 +80,18 @@ static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
8180
});
8281
});
8382

84-
EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
83+
ConstantRange CR = RangeFn(CR1, CR2);
84+
if (Min.ugt(Max)) {
85+
EXPECT_TRUE(CR.isEmptySet());
86+
return;
87+
}
88+
89+
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
90+
if (CorrectnessOnly) {
91+
EXPECT_TRUE(CR.contains(Exact));
92+
} else {
93+
EXPECT_EQ(Exact, CR);
94+
}
8595
});
8696
}
8797

@@ -813,6 +823,42 @@ TEST_F(ConstantRangeTest, UDiv) {
813823
EXPECT_EQ(Wrap.udiv(Wrap), Full);
814824
}
815825

826+
TEST_F(ConstantRangeTest, URem) {
827+
EXPECT_EQ(Full.urem(Empty), Empty);
828+
EXPECT_EQ(Empty.urem(Full), Empty);
829+
// urem by zero is poison.
830+
EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0))), Empty);
831+
// urem by full range doesn't contain MaxValue.
832+
EXPECT_EQ(Full.urem(Full), ConstantRange(APInt(16, 0), APInt(16, 0xffff)));
833+
// urem is upper bounded by maximum RHS minus one.
834+
EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0), APInt(16, 123))),
835+
ConstantRange(APInt(16, 0), APInt(16, 122)));
836+
// urem is upper bounded by maximum LHS.
837+
EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 123)).urem(Full),
838+
ConstantRange(APInt(16, 0), APInt(16, 123)));
839+
// If the LHS is always lower than the RHS, the result is the LHS.
840+
EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20))
841+
.urem(ConstantRange(APInt(16, 20), APInt(16, 30))),
842+
ConstantRange(APInt(16, 10), APInt(16, 20)));
843+
// It has to be strictly lower, otherwise the top value may wrap to zero.
844+
EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20))
845+
.urem(ConstantRange(APInt(16, 19), APInt(16, 30))),
846+
ConstantRange(APInt(16, 0), APInt(16, 20)));
847+
// [12, 14] % 10 is [2, 4], but we conservatively compute [0, 9].
848+
EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15))
849+
.urem(ConstantRange(APInt(16, 10))),
850+
ConstantRange(APInt(16, 0), APInt(16, 10)));
851+
852+
TestUnsignedBinOpExhaustive(
853+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
854+
return CR1.urem(CR2);
855+
},
856+
[](const APInt &N1, const APInt &N2) {
857+
return N1.urem(N2);
858+
},
859+
/* SkipZeroRHS */ true, /* CorrectnessOnly */ true);
860+
}
861+
816862
TEST_F(ConstantRangeTest, Shl) {
817863
ConstantRange Some2(APInt(16, 0xfff), APInt(16, 0x8000));
818864
ConstantRange WrapNullMax(APInt(16, 0x1), APInt(16, 0x0));

0 commit comments

Comments
 (0)