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 @@ -269,6 +269,27 @@ return makeExactMulNSWRegion(Other.getSignedMin()) .intersectWith(makeExactMulNSWRegion(Other.getSignedMax())); + + case Instruction::Shl: { + // For given range of shift amounts, if we ignore all illegal shift amounts + // (that always produce poison), what shift amount range is left? + ConstantRange ShAmt = Other.intersectWith( + ConstantRange(APInt(BitWidth, 0), APInt(BitWidth, (BitWidth - 1) + 1))); + if (ShAmt.isEmptySet()) { + // If the entire range of shift amounts is already poison-producing, + // then we can freely add more poison-producing flags ontop of that. + return getFull(BitWidth); + } + // There are some legal shift amounts, we can compute conservatively-correct + // range of no-wrap inputs. Note that by now we have clamped the ShAmtUMax + // to be at most bitwidth-1, which results in most conservative range. + APInt ShAmtUMax = ShAmt.getUnsignedMax(); + if (Unsigned) + return getNonEmpty(APInt::getNullValue(BitWidth), + APInt::getMaxValue(BitWidth).lshr(ShAmtUMax) + 1); + return getNonEmpty(APInt::getSignedMinValue(BitWidth).ashr(ShAmtUMax), + APInt::getSignedMaxValue(BitWidth).ashr(ShAmtUMax) + 1); + } } } 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 @@ -1532,6 +1532,54 @@ EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( Instruction::Sub, One, OBO::NoUnsignedWrap), ConstantRange(APInt::getMinValue(32) + 1, APInt::getMinValue(32))); + + ConstantRange OneLessThanBitWidth(APInt(32, 0), APInt(32, 31) + 1); + ConstantRange UpToBitWidth(APInt(32, 0), APInt(32, 32) + 1); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, OneLessThanBitWidth, OBO::NoUnsignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, OneLessThanBitWidth, OBO::NoSignedWrap)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 1) + 1)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap), + ConstantRange(APInt(32, -1), APInt(32, 0) + 1)); + + ConstantRange IllegalShAmt(APInt(32, 32), APInt(32, 0) + 1); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, IllegalShAmt, OBO::NoUnsignedWrap), + ConstantRange::getFull(32)); + + EXPECT_EQ( + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + OBO::NoUnsignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoUnsignedWrap)); + EXPECT_EQ( + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + OBO::NoSignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1), + OBO::NoSignedWrap)); + + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + OBO::NoUnsignedWrap), + ConstantRange(APInt(32, 0), APInt(32, 65535) + 1)); + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(32, -32), APInt(32, 16) + 1), + OBO::NoSignedWrap), + ConstantRange(APInt(32, -32768), APInt(32, 32767) + 1)); } template @@ -1543,6 +1591,8 @@ [&](const ConstantRange &CR1, const ConstantRange &CR2) { if (CR2.isEmptySet()) return; + if (Instruction::isShift(BinOp) && CR2.getUnsignedMax().uge(Bits)) + return; ConstantRange NoWrap = ConstantRange::makeGuaranteedNoWrapRegion(BinOp, CR2, NoWrapKind); @@ -1610,6 +1660,20 @@ (void) N1.smul_ov(N2, Overflow); return Overflow; }); + TestNoWrapRegionExhaustive(Instruction::Shl, + OverflowingBinaryOperator::NoUnsignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void)N1.ushl_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive(Instruction::Shl, + OverflowingBinaryOperator::NoSignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void)N1.sshl_ov(N2, Overflow); + return Overflow; + }); } TEST(ConstantRange, GetEquivalentICmp) { @@ -1773,6 +1837,92 @@ } } +TEST(ConstantRange, MakeGuaranteedNoWrapRegionShlUnsignedSingleValue) { + typedef OverflowingBinaryOperator OBO; + + static constexpr int Bitwidth = 8; + + for (uint64_t I = std::numeric_limits::min(); I < Bitwidth; I++) { + auto Range = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(Bitwidth, I), APInt(Bitwidth, I + 1)), + OBO::NoUnsignedWrap); + + for (uint64_t V = std::numeric_limits::min(); + V <= std::numeric_limits::max(); V++) { + bool Overflow; + (void)APInt(Bitwidth, V).ushl_ov(APInt(Bitwidth, I), Overflow); + EXPECT_EQ(!Overflow, Range.contains(APInt(Bitwidth, V))); + } + } +} + +TEST(ConstantRange, MakeGuaranteedNoWrapRegionShlSignedSingleValue) { + typedef OverflowingBinaryOperator OBO; + + static constexpr int Bitwidth = 8; + + for (uint64_t I = std::numeric_limits::min(); I < Bitwidth; I++) { + auto Range = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(Bitwidth, I), APInt(Bitwidth, I + 1)), + OBO::NoSignedWrap); + + for (int64_t V = std::numeric_limits::min(); + V <= std::numeric_limits::max(); V++) { + bool Overflow; + (void)APInt(Bitwidth, V, /*isSigned=*/true) + .sshl_ov(APInt(Bitwidth, I), Overflow); + EXPECT_EQ(!Overflow, + Range.contains(APInt(Bitwidth, V, /*isSigned=*/true))); + } + } +} + +TEST(ConstantRange, MakeGuaranteedNoWrapRegionShlUnsignedRange) { + typedef OverflowingBinaryOperator OBO; + + static constexpr int Bitwidth = 8; + + for (uint64_t Lo = std::numeric_limits::min(); Lo < Bitwidth; Lo++) { + for (uint64_t Hi = Lo; Hi < Bitwidth; Hi++) { + EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(Bitwidth, Lo), APInt(Bitwidth, Hi + 1)), + OBO::NoUnsignedWrap), + ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(Bitwidth, Hi), APInt(Bitwidth, Hi + 1)), + OBO::NoUnsignedWrap)); + } + } +} + +TEST(ConstantRange, MakeGuaranteedNoWrapRegionShlSignedRange) { + typedef OverflowingBinaryOperator OBO; + + static constexpr int Bitwidth = 8; + + int Lo = 2, Hi = 5; + auto Range = ConstantRange::makeGuaranteedNoWrapRegion( + Instruction::Shl, + ConstantRange(APInt(Bitwidth, Lo), APInt(Bitwidth, Hi + 1)), + OBO::NoSignedWrap); + + for (int64_t V = std::numeric_limits::min(); + V <= std::numeric_limits::max(); V++) { + bool AnyOverflow = false; + for (int64_t I = Lo; I <= Hi; I++) { + bool Overflow; + (void)APInt(Bitwidth, V, /*isSigned=*/true) + .sshl_ov(APInt(Bitwidth, I), Overflow); + AnyOverflow |= Overflow; + } + EXPECT_EQ(!AnyOverflow, + Range.contains(APInt(Bitwidth, V, /*isSigned=*/true))); + } +} + #define EXPECT_MAY_OVERFLOW(op) \ EXPECT_EQ(ConstantRange::OverflowResult::MayOverflow, (op)) #define EXPECT_ALWAYS_OVERFLOWS_LOW(op) \