/// 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.
///
ConstantRange(APInt::getMinValue(32) + 1, APInt::getSignedMinValue(32)));
}
+template<typename Fn>
+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;