From c0fa4ec01dd1c3837b31e142ceb20845421e34ab Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 26 Apr 2019 16:50:31 +0000 Subject: [PATCH] [ConstantRange] Add abs() support Add support for abs() to ConstantRange. This will allow to handle SPF_ABS select flavor in LVI and will also come in handy as a primitive for the srem implementation. The implementation is slightly tricky, because a) abs of signed min is signed min and b) sign-wrapped ranges may have an abs() that is smaller than a full range, so we need to explicitly handle them. Differential Revision: https://reviews.llvm.org/D61084 llvm-svn: 359321 --- llvm/include/llvm/IR/ConstantRange.h | 4 ++++ llvm/lib/IR/ConstantRange.cpp | 31 +++++++++++++++++++++++++++++++ llvm/unittests/IR/ConstantRangeTest.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h index 5ea3b63..5b9e8a6 100644 --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -397,6 +397,10 @@ public: /// Return a new range that is the logical not of the current set. ConstantRange inverse() const; + /// Calculate absolute value range. If the original range contains signed + /// min, then the resulting range will also contain signed min. + ConstantRange abs() const; + /// Represents whether an operation on the given constant range is known to /// always or never overflow. enum class OverflowResult { AlwaysOverflows, MayOverflow, NeverOverflows }; diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index af71fd3..76e0b0c 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -1156,6 +1156,37 @@ ConstantRange ConstantRange::inverse() const { return ConstantRange(Upper, Lower); } +ConstantRange ConstantRange::abs() const { + if (isEmptySet()) + return getEmpty(); + + if (isSignWrappedSet()) { + APInt Lo; + // Check whether the range crosses zero. + if (Upper.isStrictlyPositive() || !Lower.isStrictlyPositive()) + Lo = APInt::getNullValue(getBitWidth()); + else + Lo = APIntOps::umin(Lower, -Upper + 1); + + // SignedMin is included in the result range. + return ConstantRange(Lo, APInt::getSignedMinValue(getBitWidth()) + 1); + } + + APInt SMin = getSignedMin(), SMax = getSignedMax(); + + // All non-negative. + if (SMin.isNonNegative()) + return *this; + + // All negative. + if (SMax.isNegative()) + return ConstantRange(-SMax, -SMin + 1); + + // Range crosses zero. + return ConstantRange(APInt::getNullValue(getBitWidth()), + APIntOps::umax(-SMin, SMax) + 1); +} + ConstantRange::OverflowResult ConstantRange::unsignedAddMayOverflow( const ConstantRange &Other) const { if (isEmptySet() || Other.isEmptySet()) diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index b9d8fe0..fdd1e6a 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -1797,4 +1797,30 @@ TEST_F(ConstantRangeTest, SSubSat) { }); } +TEST_F(ConstantRangeTest, Abs) { + unsigned Bits = 4; + EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) { + // We're working with unsigned integers here, because it makes the signed + // min case non-wrapping. + APInt Min = APInt::getMaxValue(Bits); + APInt Max = APInt::getMinValue(Bits); + ForeachNumInConstantRange(CR, [&](const APInt &N) { + APInt AbsN = N.abs(); + if (AbsN.ult(Min)) + Min = AbsN; + if (AbsN.ugt(Max)) + Max = AbsN; + }); + + ConstantRange AbsCR = CR.abs(); + if (Min.ugt(Max)) { + EXPECT_TRUE(AbsCR.isEmptySet()); + return; + } + + ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1); + EXPECT_EQ(Exact, AbsCR); + }); +} + } // anonymous namespace -- 2.7.4