]> granicus.if.org Git - llvm/commitdiff
[ConstantRange] Clarify makeGuaranteedNoWrapRegion() guarantees; NFC
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 12 Apr 2019 19:36:47 +0000 (19:36 +0000)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 12 Apr 2019 19:36:47 +0000 (19:36 +0000)
makeGuaranteedNoWrapRegion() is actually makeExactNoWrapRegion() as
long as only one of NUW or NSW is specified. This is not obvious from
the current documentation, and some code seems to think that it is
only exact for single-element ranges. Clarify docs and add tests to
be more confident this really holds.

There are currently no users of makeGuaranteedNoWrapRegion() that
pass both NUW and NSW. I think it would be best to drop support for
this entirely and then rename the function to makeExactNoWrapRegion().

Knowing that the no-wrap region is exact is useful, because we can
backwards-constrain values. What I have in mind in particular is
that LVI should be able to constrain values on edges where the
with.overflow overflow flag is false.

Differential Revision: https://reviews.llvm.org/D60598

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@358305 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/IR/ConstantRange.h
lib/IR/ConstantRange.cpp
unittests/IR/ConstantRangeTest.cpp

index d98bf96a21cf52f3811dffd4ef039a0b00391834..d35e11abdd95ab6f5779a9e65a9e37f761b8ed62 100644 (file)
@@ -119,9 +119,11 @@ public:
   /// 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 35c2b0e2e2cc7168c3feef1c722c3368aab4144e..fe67059a8926b0310a445fb6d2ec1ab37039499f 100644 (file)
@@ -869,8 +869,7 @@ ConstantRange::add(const ConstantRange &Other) const {
 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 6d1b512d2292078a88c00c077912f7841182affa..322be45bb28f3efd20fe225470cb13b8d40f1b6f 100644 (file)
@@ -1151,6 +1151,76 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {
       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;