Skip to content

Commit 7fbe5d4

Browse files
committed
[ConstantRange] Add subWithNoWrap() method
Summary: Much like D67339, adds ConstantRange handling for when we know no-wrap behavior of the `sub`. Unlike addWithNoWrap(), we only get lucky re returning empty set for signed wrap. For unsigned, we must perform overflow check manually. A patch that makes use of this in LVI (CVP) to be posted later. Reviewers: nikic, shchenz, efriedma Reviewed By: nikic Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D69918
1 parent 365d729 commit 7fbe5d4

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ class LLVM_NODISCARD ConstantRange {
350350
/// from a subtraction of a value in this range and a value in \p Other.
351351
ConstantRange sub(const ConstantRange &Other) const;
352352

353+
/// Return a new range representing the possible values resulting
354+
/// from an subtraction with wrap type \p NoWrapKind of a value in this
355+
/// range and a value in \p Other.
356+
/// If the result range is disjoint, the preferred range is determined by the
357+
/// \p PreferredRangeType.
358+
ConstantRange subWithNoWrap(const ConstantRange &Other, unsigned NoWrapKind,
359+
PreferredRangeType RangeType = Smallest) const;
360+
353361
/// Return a new range representing the possible values resulting
354362
/// from a multiplication of a value in this range and a value in \p Other,
355363
/// treating both this and \p Other as unsigned ranges.

llvm/lib/IR/ConstantRange.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,36 @@ ConstantRange::sub(const ConstantRange &Other) const {
898898
return X;
899899
}
900900

901+
ConstantRange ConstantRange::subWithNoWrap(const ConstantRange &Other,
902+
unsigned NoWrapKind,
903+
PreferredRangeType RangeType) const {
904+
// Calculate the range for "X - Y" which is guaranteed not to wrap(overflow).
905+
// (X is from this, and Y is from Other)
906+
if (isEmptySet() || Other.isEmptySet())
907+
return getEmpty();
908+
if (isFullSet() && Other.isFullSet())
909+
return getFull();
910+
911+
using OBO = OverflowingBinaryOperator;
912+
ConstantRange Result = sub(Other);
913+
914+
// If an overflow happens for every value pair in these two constant ranges,
915+
// we must return Empty set. In signed case, we get that for free, because we
916+
// get lucky that intersection of add() with ssub_sat() results in an
917+
// empty set. But for unsigned we must perform the overflow check manually.
918+
919+
if (NoWrapKind & OBO::NoSignedWrap)
920+
Result = Result.intersectWith(ssub_sat(Other), RangeType);
921+
922+
if (NoWrapKind & OBO::NoUnsignedWrap) {
923+
if (getUnsignedMax().ult(Other.getUnsignedMin()))
924+
return getEmpty(); // Always overflows.
925+
Result = Result.intersectWith(usub_sat(Other), RangeType);
926+
}
927+
928+
return Result;
929+
}
930+
901931
ConstantRange
902932
ConstantRange::multiply(const ConstantRange &Other) const {
903933
// TODO: If either operand is a single element and the multiply is known to

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,34 @@ TEST_F(ConstantRangeTest, Sub) {
934934
ConstantRange(APInt(16, 0x6)));
935935
}
936936

937+
TEST_F(ConstantRangeTest, SubWithNoWrap) {
938+
typedef OverflowingBinaryOperator OBO;
939+
TestAddWithNoSignedWrapExhaustive(
940+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
941+
return CR1.subWithNoWrap(CR2, OBO::NoSignedWrap);
942+
},
943+
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
944+
return N1.ssub_ov(N2, IsOverflow);
945+
});
946+
TestAddWithNoUnsignedWrapExhaustive(
947+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
948+
return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap);
949+
},
950+
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
951+
return N1.usub_ov(N2, IsOverflow);
952+
});
953+
TestAddWithNoSignedUnsignedWrapExhaustive(
954+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
955+
return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap);
956+
},
957+
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
958+
return N1.ssub_ov(N2, IsOverflow);
959+
},
960+
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
961+
return N1.usub_ov(N2, IsOverflow);
962+
});
963+
}
964+
937965
TEST_F(ConstantRangeTest, Multiply) {
938966
EXPECT_EQ(Full.multiply(Full), Full);
939967
EXPECT_EQ(Full.multiply(Empty), Empty);

0 commit comments

Comments
 (0)