diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -245,6 +245,11 @@ /// Return the smallest signed value contained in the ConstantRange. APInt getSignedMin() const; + /// Return the value contained in the ConstantRange with smallest absolute + /// value. Returns from 0 to 2 values. If two values are returned, + /// the first one is strictly positive, and second one is strictly negative. + SmallVector getClosestToZero() const; + /// Return true if this range is equal to another range. bool operator==(const ConstantRange &CR) const { return Lower == CR.Lower && Upper == CR.Upper; diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -385,6 +385,30 @@ return getLower(); } +SmallVector ConstantRange::getClosestToZero() const { + if (isEmptySet()) + return {}; + + // If the range contains zero, said zero itself is the answer. + APInt Zero = APInt::getNullValue(getBitWidth()); + if (contains(Zero)) + return {Zero}; + + APInt Lower = getLower(); + APInt Upper = getUpper() - 1; + + // If both limits are equidistant from zero, then we *MUST* return both! + // We can don't bother checking for zero, we know we don't have it by now. + if (Lower.isNonNegative() && Upper.isNegative() && Lower == -Upper) + return {Lower, Upper}; // Return positive one first, and negative second. + + // Return the limit that has smallest absolute value. This is INT_MIN-safe. + auto CompareAbs = [](const APInt &A, const APInt &B) { + return A.abs().ult(B.abs()); + }; + return {std::min({Lower, Upper}, CompareAbs)}; +} + bool ConstantRange::contains(const APInt &V) const { if (Lower == Upper) return isFullSet(); diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -2295,4 +2295,40 @@ }); } +TEST_F(ConstantRangeTest, getClosestToZero) { + unsigned Bits = 4; + EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) { + const SmallVector ClosestToZero = CR.getClosestToZero(); + + EXPECT_EQ(ClosestToZero.empty(), CR.isEmptySet()); + if (ClosestToZero.empty()) + return; + + EXPECT_LE(ClosestToZero.size(), 2U); + if (ClosestToZero.size() == 2U) { + EXPECT_TRUE(ClosestToZero.front().isStrictlyPositive()); + EXPECT_TRUE(ClosestToZero.back().isNegative()); + EXPECT_EQ(ClosestToZero.front(), -(ClosestToZero.back())); + } + + for (const APInt &V : ClosestToZero) + EXPECT_TRUE(CR.contains(V)); + + if (llvm::any_of(ClosestToZero, [Bits](const APInt &V) { + return V == APInt::getSignedMinValue(Bits); + })) { + EXPECT_EQ(ClosestToZero.size(), 1U); + const APInt *S = CR.getSingleElement(); + EXPECT_NE(S, nullptr); + EXPECT_EQ(*S, ClosestToZero.front()); + + return; + } + + APInt ClosestToZeroAbs = ClosestToZero.front().abs(); + for (APInt C = APInt(Bits, 0); C.ult(ClosestToZeroAbs); ++C) + EXPECT_FALSE(CR.contains(C) || CR.contains(-C)); + }); +} + } // anonymous namespace