diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h index 2fb176fe9eb9..4ac0a0f650ee 100644 --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -261,6 +261,10 @@ public: /// from an addition of a value in this range and a value in \p Other. ConstantRange add(const ConstantRange &Other) const; + /// Return a new range representing the possible values resulting from a + /// known NSW addition of a value in this range and \p Other constant. + ConstantRange addWithNoSignedWrap(const APInt &Other) const; + /// Return a new range representing the possible values resulting /// from a subtraction of a value in this range and a value in \p Other. ConstantRange sub(const ConstantRange &Other) const; diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index b422eea36920..225eb5e20293 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -674,6 +674,19 @@ ConstantRange::add(const ConstantRange &Other) const { return X; } +ConstantRange ConstantRange::addWithNoSignedWrap(const APInt &Other) const { + // Calculate the subset of this range such that "X + Other" is + // guaranteed not to wrap (overflow) for all X in this subset. + // makeGuaranteedNoWrapRegion will produce an exact NSW range since we are + // passing a single element range. + auto NSWRange = ConstantRange::makeGuaranteedNoWrapRegion(BinaryOperator::Add, + ConstantRange(Other), + OverflowingBinaryOperator::NoSignedWrap); + auto NSWConstrainedRange = intersectWith(NSWRange); + + return NSWConstrainedRange.add(ConstantRange(Other)); +} + ConstantRange ConstantRange::sub(const ConstantRange &Other) const { if (isEmptySet() || Other.isEmptySet()) diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index b890a4a0bc43..58fd04448e2e 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -357,6 +357,32 @@ TEST_F(ConstantRangeTest, Add) { ConstantRange(APInt(16, 0xe))); } +TEST_F(ConstantRangeTest, AddWithNoSignedWrap) { + EXPECT_EQ(Empty.addWithNoSignedWrap(APInt(16, 1)), Empty); + EXPECT_EQ(Full.addWithNoSignedWrap(APInt(16, 1)), + ConstantRange(APInt(16, INT16_MIN+1), APInt(16, INT16_MIN))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, -40), APInt(8, 60))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 120)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, -40), APInt(8, INT8_MIN))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10)).addWithNoSignedWrap(APInt(8, 5)), + ConstantRange(APInt(8, 125), APInt(8, -5))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, 10)), + ConstantRange(APInt(8, INT8_MIN+10), APInt(8, -110))); + + EXPECT_EQ(Empty.addWithNoSignedWrap(APInt(16, -1)), Empty); + EXPECT_EQ(Full.addWithNoSignedWrap(APInt(16, -1)), + ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX))); + EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, -60), APInt(8, 40))); + EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, 50)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, INT8_MIN), APInt(8, 40))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, -5)), + ConstantRange(APInt(8, 115), APInt(8, -125))); + EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -120)).addWithNoSignedWrap(APInt(8, -10)), + ConstantRange(APInt(8, 110), APInt(8, INT8_MIN-10))); +} + TEST_F(ConstantRangeTest, Sub) { EXPECT_EQ(Full.sub(APInt(16, 4)), Full); EXPECT_EQ(Full.sub(Full), Full);