Skip to content

Commit 198ab60

Browse files
committed
[ConstantRange] Add saturating add/sub methods
Add support for uadd_sat and friends to ConstantRange, so we can handle uadd.sat and friends in LVI. The implementation is forwarding to the corresponding APInt methods with appropriate bounds. One thing worth pointing out here is that the handling of wrapping ranges is not maximally accurate. A simple example is that adding 0 to a wrapped range will return a full range, rather than the original wrapped range. The tests also only check that the non-wrapping envelope is correct and minimal. Differential Revision: https://reviews.llvm.org/D60946 llvm-svn: 358855
1 parent dbc3fba commit 198ab60

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,18 @@ class LLVM_NODISCARD ConstantRange {
377377
/// arithmetic right shift of a value in this range and a value in \p Other.
378378
ConstantRange ashr(const ConstantRange &Other) const;
379379

380+
/// Perform an unsigned saturating addition of two constant ranges.
381+
ConstantRange uadd_sat(const ConstantRange &Other) const;
382+
383+
/// Perform a signed saturating addition of two constant ranges.
384+
ConstantRange sadd_sat(const ConstantRange &Other) const;
385+
386+
/// Perform an unsigned saturating subtraction of two constant ranges.
387+
ConstantRange usub_sat(const ConstantRange &Other) const;
388+
389+
/// Perform a signed saturating subtraction of two constant ranges.
390+
ConstantRange ssub_sat(const ConstantRange &Other) const;
391+
380392
/// Return a new range that is the logical not of the current set.
381393
ConstantRange inverse() const;
382394

llvm/lib/IR/ConstantRange.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,42 @@ ConstantRange::ashr(const ConstantRange &Other) const {
10991099
return getNonEmpty(std::move(min), std::move(max));
11001100
}
11011101

1102+
ConstantRange ConstantRange::uadd_sat(const ConstantRange &Other) const {
1103+
if (isEmptySet() || Other.isEmptySet())
1104+
return getEmpty();
1105+
1106+
APInt NewL = getUnsignedMin().uadd_sat(Other.getUnsignedMin());
1107+
APInt NewU = getUnsignedMax().uadd_sat(Other.getUnsignedMax()) + 1;
1108+
return getNonEmpty(std::move(NewL), std::move(NewU));
1109+
}
1110+
1111+
ConstantRange ConstantRange::sadd_sat(const ConstantRange &Other) const {
1112+
if (isEmptySet() || Other.isEmptySet())
1113+
return getEmpty();
1114+
1115+
APInt NewL = getSignedMin().sadd_sat(Other.getSignedMin());
1116+
APInt NewU = getSignedMax().sadd_sat(Other.getSignedMax()) + 1;
1117+
return getNonEmpty(std::move(NewL), std::move(NewU));
1118+
}
1119+
1120+
ConstantRange ConstantRange::usub_sat(const ConstantRange &Other) const {
1121+
if (isEmptySet() || Other.isEmptySet())
1122+
return getEmpty();
1123+
1124+
APInt NewL = getUnsignedMin().usub_sat(Other.getUnsignedMax());
1125+
APInt NewU = getUnsignedMax().usub_sat(Other.getUnsignedMin()) + 1;
1126+
return getNonEmpty(std::move(NewL), std::move(NewU));
1127+
}
1128+
1129+
ConstantRange ConstantRange::ssub_sat(const ConstantRange &Other) const {
1130+
if (isEmptySet() || Other.isEmptySet())
1131+
return getEmpty();
1132+
1133+
APInt NewL = getSignedMin().ssub_sat(Other.getSignedMax());
1134+
APInt NewU = getSignedMax().ssub_sat(Other.getSignedMin()) + 1;
1135+
return getNonEmpty(std::move(NewL), std::move(NewU));
1136+
}
1137+
11021138
ConstantRange ConstantRange::inverse() const {
11031139
if (isFullSet())
11041140
return getEmpty();

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,4 +1647,98 @@ TEST_F(ConstantRangeTest, Negative) {
16471647
});
16481648
}
16491649

1650+
template<typename Fn1, typename Fn2>
1651+
static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
1652+
unsigned Bits = 4;
1653+
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
1654+
const ConstantRange &CR2) {
1655+
ConstantRange CR = RangeFn(CR1, CR2);
1656+
if (CR1.isEmptySet() || CR2.isEmptySet()) {
1657+
EXPECT_TRUE(CR.isEmptySet());
1658+
return;
1659+
}
1660+
1661+
APInt Min = APInt::getMaxValue(Bits);
1662+
APInt Max = APInt::getMinValue(Bits);
1663+
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
1664+
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
1665+
APInt N = IntFn(N1, N2);
1666+
if (N.ult(Min))
1667+
Min = N;
1668+
if (N.ugt(Max))
1669+
Max = N;
1670+
});
1671+
});
1672+
1673+
EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
1674+
});
1675+
}
1676+
1677+
template<typename Fn1, typename Fn2>
1678+
static void TestSignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
1679+
unsigned Bits = 4;
1680+
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
1681+
const ConstantRange &CR2) {
1682+
ConstantRange CR = RangeFn(CR1, CR2);
1683+
if (CR1.isEmptySet() || CR2.isEmptySet()) {
1684+
EXPECT_TRUE(CR.isEmptySet());
1685+
return;
1686+
}
1687+
1688+
APInt Min = APInt::getSignedMaxValue(Bits);
1689+
APInt Max = APInt::getSignedMinValue(Bits);
1690+
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
1691+
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
1692+
APInt N = IntFn(N1, N2);
1693+
if (N.slt(Min))
1694+
Min = N;
1695+
if (N.sgt(Max))
1696+
Max = N;
1697+
});
1698+
});
1699+
1700+
EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
1701+
});
1702+
}
1703+
1704+
TEST_F(ConstantRangeTest, UAddSat) {
1705+
TestUnsignedBinOpExhaustive(
1706+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1707+
return CR1.uadd_sat(CR2);
1708+
},
1709+
[](const APInt &N1, const APInt &N2) {
1710+
return N1.uadd_sat(N2);
1711+
});
1712+
}
1713+
1714+
TEST_F(ConstantRangeTest, USubSat) {
1715+
TestUnsignedBinOpExhaustive(
1716+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1717+
return CR1.usub_sat(CR2);
1718+
},
1719+
[](const APInt &N1, const APInt &N2) {
1720+
return N1.usub_sat(N2);
1721+
});
1722+
}
1723+
1724+
TEST_F(ConstantRangeTest, SAddSat) {
1725+
TestSignedBinOpExhaustive(
1726+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1727+
return CR1.sadd_sat(CR2);
1728+
},
1729+
[](const APInt &N1, const APInt &N2) {
1730+
return N1.sadd_sat(N2);
1731+
});
1732+
}
1733+
1734+
TEST_F(ConstantRangeTest, SSubSat) {
1735+
TestSignedBinOpExhaustive(
1736+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1737+
return CR1.ssub_sat(CR2);
1738+
},
1739+
[](const APInt &N1, const APInt &N2) {
1740+
return N1.ssub_sat(N2);
1741+
});
1742+
}
1743+
16501744
} // anonymous namespace

0 commit comments

Comments
 (0)