summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-04-21 15:23:05 +0000
committerNikita Popov <nikita.ppv@gmail.com>2019-04-21 15:23:05 +0000
commit198ab6013678e35d6b6cbd9cefad84691ff358b2 (patch)
tree1f9553127afaab6934d002d68945598c1f638b2b
parentdbc3fbafe7cbf10bac30da4d4b6eb6082fed3daa (diff)
downloadbcm5719-llvm-198ab6013678e35d6b6cbd9cefad84691ff358b2.tar.gz
bcm5719-llvm-198ab6013678e35d6b6cbd9cefad84691ff358b2.zip
[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
-rw-r--r--llvm/include/llvm/IR/ConstantRange.h12
-rw-r--r--llvm/lib/IR/ConstantRange.cpp36
-rw-r--r--llvm/unittests/IR/ConstantRangeTest.cpp94
3 files changed, 142 insertions, 0 deletions
diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index 6dbe4053994..f006a9321fc 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -377,6 +377,18 @@ public:
/// arithmetic right shift of a value in this range and a value in \p Other.
ConstantRange ashr(const ConstantRange &Other) const;
+ /// Perform an unsigned saturating addition of two constant ranges.
+ ConstantRange uadd_sat(const ConstantRange &Other) const;
+
+ /// Perform a signed saturating addition of two constant ranges.
+ ConstantRange sadd_sat(const ConstantRange &Other) const;
+
+ /// Perform an unsigned saturating subtraction of two constant ranges.
+ ConstantRange usub_sat(const ConstantRange &Other) const;
+
+ /// Perform a signed saturating subtraction of two constant ranges.
+ ConstantRange ssub_sat(const ConstantRange &Other) const;
+
/// Return a new range that is the logical not of the current set.
ConstantRange inverse() const;
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 7bd14e62dc1..b2bdd0abd9c 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -1099,6 +1099,42 @@ ConstantRange::ashr(const ConstantRange &Other) const {
return getNonEmpty(std::move(min), std::move(max));
}
+ConstantRange ConstantRange::uadd_sat(const ConstantRange &Other) const {
+ if (isEmptySet() || Other.isEmptySet())
+ return getEmpty();
+
+ APInt NewL = getUnsignedMin().uadd_sat(Other.getUnsignedMin());
+ APInt NewU = getUnsignedMax().uadd_sat(Other.getUnsignedMax()) + 1;
+ return getNonEmpty(std::move(NewL), std::move(NewU));
+}
+
+ConstantRange ConstantRange::sadd_sat(const ConstantRange &Other) const {
+ if (isEmptySet() || Other.isEmptySet())
+ return getEmpty();
+
+ APInt NewL = getSignedMin().sadd_sat(Other.getSignedMin());
+ APInt NewU = getSignedMax().sadd_sat(Other.getSignedMax()) + 1;
+ return getNonEmpty(std::move(NewL), std::move(NewU));
+}
+
+ConstantRange ConstantRange::usub_sat(const ConstantRange &Other) const {
+ if (isEmptySet() || Other.isEmptySet())
+ return getEmpty();
+
+ APInt NewL = getUnsignedMin().usub_sat(Other.getUnsignedMax());
+ APInt NewU = getUnsignedMax().usub_sat(Other.getUnsignedMin()) + 1;
+ return getNonEmpty(std::move(NewL), std::move(NewU));
+}
+
+ConstantRange ConstantRange::ssub_sat(const ConstantRange &Other) const {
+ if (isEmptySet() || Other.isEmptySet())
+ return getEmpty();
+
+ APInt NewL = getSignedMin().ssub_sat(Other.getSignedMax());
+ APInt NewU = getSignedMax().ssub_sat(Other.getSignedMin()) + 1;
+ return getNonEmpty(std::move(NewL), std::move(NewU));
+}
+
ConstantRange ConstantRange::inverse() const {
if (isFullSet())
return getEmpty();
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index 060d69126e2..3306fe3ea69 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -1647,4 +1647,98 @@ TEST_F(ConstantRangeTest, Negative) {
});
}
+template<typename Fn1, typename Fn2>
+static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
+ unsigned Bits = 4;
+ EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
+ const ConstantRange &CR2) {
+ ConstantRange CR = RangeFn(CR1, CR2);
+ if (CR1.isEmptySet() || CR2.isEmptySet()) {
+ EXPECT_TRUE(CR.isEmptySet());
+ return;
+ }
+
+ APInt Min = APInt::getMaxValue(Bits);
+ APInt Max = APInt::getMinValue(Bits);
+ ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
+ ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
+ APInt N = IntFn(N1, N2);
+ if (N.ult(Min))
+ Min = N;
+ if (N.ugt(Max))
+ Max = N;
+ });
+ });
+
+ EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
+ });
+}
+
+template<typename Fn1, typename Fn2>
+static void TestSignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
+ unsigned Bits = 4;
+ EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
+ const ConstantRange &CR2) {
+ ConstantRange CR = RangeFn(CR1, CR2);
+ if (CR1.isEmptySet() || CR2.isEmptySet()) {
+ EXPECT_TRUE(CR.isEmptySet());
+ return;
+ }
+
+ APInt Min = APInt::getSignedMaxValue(Bits);
+ APInt Max = APInt::getSignedMinValue(Bits);
+ ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
+ ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
+ APInt N = IntFn(N1, N2);
+ if (N.slt(Min))
+ Min = N;
+ if (N.sgt(Max))
+ Max = N;
+ });
+ });
+
+ EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
+ });
+}
+
+TEST_F(ConstantRangeTest, UAddSat) {
+ TestUnsignedBinOpExhaustive(
+ [](const ConstantRange &CR1, const ConstantRange &CR2) {
+ return CR1.uadd_sat(CR2);
+ },
+ [](const APInt &N1, const APInt &N2) {
+ return N1.uadd_sat(N2);
+ });
+}
+
+TEST_F(ConstantRangeTest, USubSat) {
+ TestUnsignedBinOpExhaustive(
+ [](const ConstantRange &CR1, const ConstantRange &CR2) {
+ return CR1.usub_sat(CR2);
+ },
+ [](const APInt &N1, const APInt &N2) {
+ return N1.usub_sat(N2);
+ });
+}
+
+TEST_F(ConstantRangeTest, SAddSat) {
+ TestSignedBinOpExhaustive(
+ [](const ConstantRange &CR1, const ConstantRange &CR2) {
+ return CR1.sadd_sat(CR2);
+ },
+ [](const APInt &N1, const APInt &N2) {
+ return N1.sadd_sat(N2);
+ });
+}
+
+TEST_F(ConstantRangeTest, SSubSat) {
+ TestSignedBinOpExhaustive(
+ [](const ConstantRange &CR1, const ConstantRange &CR2) {
+ return CR1.ssub_sat(CR2);
+ },
+ [](const APInt &N1, const APInt &N2) {
+ return N1.ssub_sat(N2);
+ });
+}
+
} // anonymous namespace
OpenPOWER on IntegriCloud