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 @@ -92,6 +92,9 @@ /// unsigned domain. static ConstantRange fromKnownBits(const KnownBits &Known, bool IsSigned); + /// Get a known bits valid for a given range. + static KnownBits toKnownBits(const ConstantRange &Range); + /// Produce the smallest range such that all values that may satisfy the given /// predicate with any value contained within Other is contained in the /// returned range. Formally, this returns a superset of diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h --- a/llvm/include/llvm/Support/KnownBits.h +++ b/llvm/include/llvm/Support/KnownBits.h @@ -35,6 +35,12 @@ /// Create a known bits object of BitWidth bits initialized to unknown. KnownBits(unsigned BitWidth) : Zero(BitWidth, 0), One(BitWidth, 0) {} + /// Return true if this KnownBits is equal to another KnownBits. + bool operator==(const KnownBits &CR) const { + return Zero == CR.Zero && One == CR.One; + } + bool operator!=(const KnownBits &KB) const { return !operator==(KB); } + /// Get the bit width of this value. unsigned getBitWidth() const { assert(Zero.getBitWidth() == One.getBitWidth() && @@ -45,6 +51,12 @@ /// Returns true if there is conflicting information. bool hasConflict() const { return Zero.intersects(One); } + /// Returns true if there is conflicting information between this KnownBits + /// and \p Other KnownBits. + bool hasConflict(const KnownBits &Other) const { + return Zero.intersects(Other.One) || One.intersects(Other.Zero); + } + /// Returns true if we know the value of all bits. bool isConstant() const { assert(!hasConflict() && "KnownBits conflict!"); @@ -67,6 +79,17 @@ One.clearAllBits(); } + /// Returns true if given value \p Val matches this KnownBits mask. + bool contains(const APInt &Val) const { + return !Val.intersects(Zero) && !(~Val).intersects(One); + } + + /// This operation checks that all bits known to this KnownBits are also + /// known to \p Other KnownBits. + bool isSubsetOf(const KnownBits &Other) const { + return Zero.isSubsetOf(Other.Zero) && One.isSubsetOf(Other.One); + } + /// Returns true if value is all zero. bool isZero() const { assert(!hasConflict() && "KnownBits conflict!"); @@ -209,8 +232,19 @@ /// Compute known bits resulting from adding LHS and RHS. static KnownBits computeForAddSub(bool Add, bool NSW, const KnownBits &LHS, KnownBits RHS); + + /// Print out the bounds to a stream. + void print(raw_ostream &OS) const; + + /// Allow printing from a debugger easily. + void dump() const; }; +inline raw_ostream &operator<<(raw_ostream &OS, const KnownBits &KB) { + KB.print(OS); + return OS; +} + } // end namespace llvm #endif 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 @@ -74,6 +74,38 @@ return ConstantRange(Lower, Upper + 1); } +// The implementation of ConstantRange::toKnownBits() is a verbatim +// backtransform of what is being done in the ConstantRange::fromKnownBits(). +KnownBits ConstantRange::toKnownBits(const ConstantRange &Range) { + KnownBits Known(Range.getBitWidth()); + + if (Range.isFullSet() || Range.isEmptySet()) + return Known; // No bits known. + + if (const APInt *SingleElement = Range.getSingleElement()) { + Known.One = *SingleElement; + Known.Zero = ~*SingleElement; + return Known; + } + + return Known; + + if (Range.isAllNonNegative() || Range.isAllNegative()) { + Known.One = Range.getLower(); + Known.Zero = ~(Range.getUpper() - 1); + return Known; + } + + Known.One = Range.getLower(); + Known.One.clearSignBit(); + + Known.Zero = Range.getUpper() - 1; + Known.Zero.setSignBit(); + Known.Zero = ~Known.Zero; + + return Known; +} + ConstantRange ConstantRange::makeAllowedICmpRegion(CmpInst::Predicate Pred, const ConstantRange &CR) { if (CR.isEmptySet()) diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp --- a/llvm/lib/Support/KnownBits.cpp +++ b/llvm/lib/Support/KnownBits.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/KnownBits.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -81,3 +83,11 @@ return KnownOut; } + +void KnownBits::print(raw_ostream &OS) const { + OS << "Known-zero: " << Zero << ", Known-one: " << One; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void KnownBits::dump() const { print(dbgs()); } +#endif 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 @@ -2112,7 +2112,7 @@ APInt MaxSigned = APInt::getSignedMinValue(Bits); for (unsigned N = 0; N < Max; ++N) { APInt Num(Bits, N); - if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0) + if (!Known.contains(Num)) continue; if (Num.ult(MinUnsigned)) MinUnsigned = Num; @@ -2129,6 +2129,42 @@ } } +// FIXME +TEST_F(ConstantRangeTest, ToKnownBitsExhaustive) { + unsigned Bits = 4; + KnownBits Known(Bits); + EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) { + if (CR.isEmptySet()) + return; + + // Acquire precise KnownBits knowledge for a given constant range via + // the brute-force approach. + Known.Zero.setAllBits(); + Known.One.setAllBits(); + ForeachNumInConstantRange(CR, [&](const APInt &N) { + Known.Zero &= ~N; + Known.One &= N; + }); + ASSERT_FALSE(Known.hasConflict()); + + // If we produce constant range from known bits, it may be conservatively + // larger than the reference constant range. The reference constant range + // must be contained within the new constant range. + ConstantRange UnsignedCRFromKB(ConstantRange::fromKnownBits(Known, false)); + ConstantRange SignedCRFromKB(ConstantRange::fromKnownBits(Known, true)); + EXPECT_EQ(SignedCRFromKB, UnsignedCRFromKB); + EXPECT_TRUE(UnsignedCRFromKB.contains(CR)); + + // If we distill constant range into known bits, it may result in less + // known bits, but the ones that are known must not be in conflict with + // the reference known bits. + KnownBits KBFromCR(ConstantRange::toKnownBits(CR)); + ASSERT_FALSE(KBFromCR.hasConflict()); + EXPECT_FALSE(KBFromCR.hasConflict(Known)); + EXPECT_TRUE(KBFromCR.isSubsetOf(Known)); + }); +} + TEST_F(ConstantRangeTest, Negative) { // All elements in an empty set (of which there are none) are both negative // and non-negative. Empty & full sets checked explicitly for clarity, but