Index: llvm/include/llvm/IR/ConstantRange.h =================================================================== --- llvm/include/llvm/IR/ConstantRange.h +++ llvm/include/llvm/IR/ConstantRange.h @@ -119,9 +119,11 @@ /// Return the largest range containing all X such that "X BinOpC Y" is /// guaranteed not to wrap (overflow) for all Y in Other. /// - /// NB! The returned set does *not* contain **all** possible values of X for - /// which "X BinOpC Y" does not wrap -- some viable values of X may be - /// missing, so you cannot use this to constrain X's range. E.g. in the + /// If only one of NoUnsignedWrap or NoSignedWrap is specified, the returned + /// range is exact: It contains *all* possible values of X for which + /// "X BinOpC Y" does not wrap. However, if both NUW and NSW are specified, it + /// may return only a subset of non-wrapping values. In this case the + /// returned region cannot be used to constrain X's range. E.g. in the /// fourth example, "(-2) + 1" is both nsw and nuw (so the "X" could be -2), /// but (-2) is not in the set returned. /// Index: llvm/lib/IR/ConstantRange.cpp =================================================================== --- llvm/lib/IR/ConstantRange.cpp +++ llvm/lib/IR/ConstantRange.cpp @@ -869,8 +869,7 @@ 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. + // makeGuaranteedNoWrapRegion will produce an exact NSW range. auto NSWRange = ConstantRange::makeGuaranteedNoWrapRegion(BinaryOperator::Add, ConstantRange(Other), OverflowingBinaryOperator::NoSignedWrap); Index: llvm/unittests/IR/ConstantRangeTest.cpp =================================================================== --- llvm/unittests/IR/ConstantRangeTest.cpp +++ llvm/unittests/IR/ConstantRangeTest.cpp @@ -1151,6 +1151,76 @@ ConstantRange(APInt::getMinValue(32) + 1, APInt::getSignedMinValue(32))); } +template +void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp, + unsigned NoWrapKind, Fn OverflowFn) { + // When using 4 bits this test needs ~3s on a debug build. + unsigned Bits = 3; + EnumerateTwoConstantRanges(Bits, + [&](const ConstantRange &CR1, const ConstantRange &CR2) { + if (CR2.isEmptySet()) + return; + + ConstantRange NoWrap = + ConstantRange::makeGuaranteedNoWrapRegion(BinOp, CR2, NoWrapKind); + ForeachNumInConstantRange(CR1, [&](const APInt &N1) { + bool NoOverflow = true; + ForeachNumInConstantRange(CR2, [&](const APInt &N2) { + if (OverflowFn(N1, N2)) + NoOverflow = false; + }); + EXPECT_EQ(NoOverflow, NoWrap.contains(N1)); + }); + }); +} + +// Show that makeGuaranteedNoWrapRegion is precise if only one of +// NoUnsignedWrap or NoSignedWrap is used. +TEST(ConstantRange, NoWrapRegionExhaustive) { + TestNoWrapRegionExhaustive( + Instruction::Add, OverflowingBinaryOperator::NoUnsignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.uadd_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive( + Instruction::Add, OverflowingBinaryOperator::NoSignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.sadd_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive( + Instruction::Sub, OverflowingBinaryOperator::NoUnsignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.usub_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive( + Instruction::Sub, OverflowingBinaryOperator::NoSignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.ssub_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive( + Instruction::Mul, OverflowingBinaryOperator::NoUnsignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.umul_ov(N2, Overflow); + return Overflow; + }); + TestNoWrapRegionExhaustive( + Instruction::Mul, OverflowingBinaryOperator::NoSignedWrap, + [](const APInt &N1, const APInt &N2) { + bool Overflow; + (void) N1.smul_ov(N2, Overflow); + return Overflow; + }); +} + TEST(ConstantRange, GetEquivalentICmp) { APInt RHS; CmpInst::Predicate Pred;