llvm.org GIT mirror llvm / 66c5f57
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@358855 91177308-0d34-0410-b5e6-96231b3b80d8 Nikita Popov 5 months ago
3 changed file(s) with 142 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
376376 /// arithmetic right shift of a value in this range and a value in \p Other.
377377 ConstantRange ashr(const ConstantRange &Other) const;
378378
379 /// Perform an unsigned saturating addition of two constant ranges.
380 ConstantRange uadd_sat(const ConstantRange &Other) const;
381
382 /// Perform a signed saturating addition of two constant ranges.
383 ConstantRange sadd_sat(const ConstantRange &Other) const;
384
385 /// Perform an unsigned saturating subtraction of two constant ranges.
386 ConstantRange usub_sat(const ConstantRange &Other) const;
387
388 /// Perform a signed saturating subtraction of two constant ranges.
389 ConstantRange ssub_sat(const ConstantRange &Other) const;
390
379391 /// Return a new range that is the logical not of the current set.
380392 ConstantRange inverse() const;
381393
10981098 return getNonEmpty(std::move(min), std::move(max));
10991099 }
11001100
1101 ConstantRange ConstantRange::uadd_sat(const ConstantRange &Other) const {
1102 if (isEmptySet() || Other.isEmptySet())
1103 return getEmpty();
1104
1105 APInt NewL = getUnsignedMin().uadd_sat(Other.getUnsignedMin());
1106 APInt NewU = getUnsignedMax().uadd_sat(Other.getUnsignedMax()) + 1;
1107 return getNonEmpty(std::move(NewL), std::move(NewU));
1108 }
1109
1110 ConstantRange ConstantRange::sadd_sat(const ConstantRange &Other) const {
1111 if (isEmptySet() || Other.isEmptySet())
1112 return getEmpty();
1113
1114 APInt NewL = getSignedMin().sadd_sat(Other.getSignedMin());
1115 APInt NewU = getSignedMax().sadd_sat(Other.getSignedMax()) + 1;
1116 return getNonEmpty(std::move(NewL), std::move(NewU));
1117 }
1118
1119 ConstantRange ConstantRange::usub_sat(const ConstantRange &Other) const {
1120 if (isEmptySet() || Other.isEmptySet())
1121 return getEmpty();
1122
1123 APInt NewL = getUnsignedMin().usub_sat(Other.getUnsignedMax());
1124 APInt NewU = getUnsignedMax().usub_sat(Other.getUnsignedMin()) + 1;
1125 return getNonEmpty(std::move(NewL), std::move(NewU));
1126 }
1127
1128 ConstantRange ConstantRange::ssub_sat(const ConstantRange &Other) const {
1129 if (isEmptySet() || Other.isEmptySet())
1130 return getEmpty();
1131
1132 APInt NewL = getSignedMin().ssub_sat(Other.getSignedMax());
1133 APInt NewU = getSignedMax().ssub_sat(Other.getSignedMin()) + 1;
1134 return getNonEmpty(std::move(NewL), std::move(NewU));
1135 }
1136
11011137 ConstantRange ConstantRange::inverse() const {
11021138 if (isFullSet())
11031139 return getEmpty();
16461646 });
16471647 }
16481648
1649 template
1650 static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
1651 unsigned Bits = 4;
1652 EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
1653 const ConstantRange &CR2) {
1654 ConstantRange CR = RangeFn(CR1, CR2);
1655 if (CR1.isEmptySet() || CR2.isEmptySet()) {
1656 EXPECT_TRUE(CR.isEmptySet());
1657 return;
1658 }
1659
1660 APInt Min = APInt::getMaxValue(Bits);
1661 APInt Max = APInt::getMinValue(Bits);
1662 ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
1663 ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
1664 APInt N = IntFn(N1, N2);
1665 if (N.ult(Min))
1666 Min = N;
1667 if (N.ugt(Max))
1668 Max = N;
1669 });
1670 });
1671
1672 EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
1673 });
1674 }
1675
1676 template
1677 static void TestSignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn) {
1678 unsigned Bits = 4;
1679 EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
1680 const ConstantRange &CR2) {
1681 ConstantRange CR = RangeFn(CR1, CR2);
1682 if (CR1.isEmptySet() || CR2.isEmptySet()) {
1683 EXPECT_TRUE(CR.isEmptySet());
1684 return;
1685 }
1686
1687 APInt Min = APInt::getSignedMaxValue(Bits);
1688 APInt Max = APInt::getSignedMinValue(Bits);
1689 ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
1690 ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
1691 APInt N = IntFn(N1, N2);
1692 if (N.slt(Min))
1693 Min = N;
1694 if (N.sgt(Max))
1695 Max = N;
1696 });
1697 });
1698
1699 EXPECT_EQ(ConstantRange::getNonEmpty(Min, Max + 1), CR);
1700 });
1701 }
1702
1703 TEST_F(ConstantRangeTest, UAddSat) {
1704 TestUnsignedBinOpExhaustive(
1705 [](const ConstantRange &CR1, const ConstantRange &CR2) {
1706 return CR1.uadd_sat(CR2);
1707 },
1708 [](const APInt &N1, const APInt &N2) {
1709 return N1.uadd_sat(N2);
1710 });
1711 }
1712
1713 TEST_F(ConstantRangeTest, USubSat) {
1714 TestUnsignedBinOpExhaustive(
1715 [](const ConstantRange &CR1, const ConstantRange &CR2) {
1716 return CR1.usub_sat(CR2);
1717 },
1718 [](const APInt &N1, const APInt &N2) {
1719 return N1.usub_sat(N2);
1720 });
1721 }
1722
1723 TEST_F(ConstantRangeTest, SAddSat) {
1724 TestSignedBinOpExhaustive(
1725 [](const ConstantRange &CR1, const ConstantRange &CR2) {
1726 return CR1.sadd_sat(CR2);
1727 },
1728 [](const APInt &N1, const APInt &N2) {
1729 return N1.sadd_sat(N2);
1730 });
1731 }
1732
1733 TEST_F(ConstantRangeTest, SSubSat) {
1734 TestSignedBinOpExhaustive(
1735 [](const ConstantRange &CR1, const ConstantRange &CR2) {
1736 return CR1.ssub_sat(CR2);
1737 },
1738 [](const APInt &N1, const APInt &N2) {
1739 return N1.ssub_sat(N2);
1740 });
1741 }
1742
16491743 } // anonymous namespace