[ConstantRange] Support abs with poison flag
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 30 Jul 2020 20:47:33 +0000 (22:47 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 30 Jul 2020 20:49:28 +0000 (22:49 +0200)
This just adds the ConstantRange support, including exhaustive
testing. It's not wired up to the IR intrinsic flag yet.

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

index a027292..318532b 100644 (file)
@@ -464,8 +464,9 @@ public:
   ConstantRange inverse() const;
 
   /// Calculate absolute value range. If the original range contains signed
-  /// min, then the resulting range will also contain signed min.
-  ConstantRange abs() const;
+  /// min, then the resulting range will contain signed min if and only if
+  /// \p IntMinIsPoison is false.
+  ConstantRange abs(bool IntMinIsPoison = false) const;
 
   /// Represents whether an operation on the given constant range is known to
   /// always or never overflow.
index d379140..6e0b5a0 100644 (file)
@@ -1464,7 +1464,7 @@ ConstantRange ConstantRange::inverse() const {
   return ConstantRange(Upper, Lower);
 }
 
-ConstantRange ConstantRange::abs() const {
+ConstantRange ConstantRange::abs(bool IntMinIsPoison) const {
   if (isEmptySet())
     return getEmpty();
 
@@ -1476,12 +1476,23 @@ ConstantRange ConstantRange::abs() const {
     else
       Lo = APIntOps::umin(Lower, -Upper + 1);
 
-    // SignedMin is included in the result range.
-    return ConstantRange(Lo, APInt::getSignedMinValue(getBitWidth()) + 1);
+    // If SignedMin is not poison, then it is included in the result range.
+    if (IntMinIsPoison)
+      return ConstantRange(Lo, APInt::getSignedMinValue(getBitWidth()));
+    else
+      return ConstantRange(Lo, APInt::getSignedMinValue(getBitWidth()) + 1);
   }
 
   APInt SMin = getSignedMin(), SMax = getSignedMax();
 
+  // Skip SignedMin if it is poison.
+  if (IntMinIsPoison && SMin.isMinSignedValue()) {
+    // The range may become empty if it *only* contains SignedMin.
+    if (SMax.isMinSignedValue())
+      return getEmpty();
+    ++SMin;
+  }
+
   // All non-negative.
   if (SMin.isNonNegative())
     return *this;
index 4d19dd5..8bcc6ef 100644 (file)
@@ -60,6 +60,35 @@ static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {
 }
 
 template<typename Fn1, typename Fn2>
+static void TestUnsignedUnaryOpExhaustive(
+    Fn1 RangeFn, Fn2 IntFn, bool SkipSignedIntMin = false) {
+  unsigned Bits = 4;
+  EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
+    APInt Min = APInt::getMaxValue(Bits);
+    APInt Max = APInt::getMinValue(Bits);
+    ForeachNumInConstantRange(CR, [&](const APInt &N) {
+      if (SkipSignedIntMin && N.isMinSignedValue())
+        return;
+
+      APInt AbsN = IntFn(N);
+      if (AbsN.ult(Min))
+        Min = AbsN;
+      if (AbsN.ugt(Max))
+        Max = AbsN;
+    });
+
+    ConstantRange ResultCR = RangeFn(CR);
+    if (Min.ugt(Max)) {
+      EXPECT_TRUE(ResultCR.isEmptySet());
+      return;
+    }
+
+    ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
+    EXPECT_EQ(Exact, ResultCR);
+  });
+}
+
+template<typename Fn1, typename Fn2>
 static void TestUnsignedBinOpExhaustive(
     Fn1 RangeFn, Fn2 IntFn,
     bool SkipZeroRHS = false, bool CorrectnessOnly = false) {
@@ -2270,29 +2299,16 @@ TEST_F(ConstantRangeTest, SShlSat) {
 }
 
 TEST_F(ConstantRangeTest, Abs) {
-  unsigned Bits = 4;
-  EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
-    // We're working with unsigned integers here, because it makes the signed
-    // min case non-wrapping.
-    APInt Min = APInt::getMaxValue(Bits);
-    APInt Max = APInt::getMinValue(Bits);
-    ForeachNumInConstantRange(CR, [&](const APInt &N) {
-      APInt AbsN = N.abs();
-      if (AbsN.ult(Min))
-        Min = AbsN;
-      if (AbsN.ugt(Max))
-        Max = AbsN;
-    });
-
-    ConstantRange AbsCR = CR.abs();
-    if (Min.ugt(Max)) {
-      EXPECT_TRUE(AbsCR.isEmptySet());
-      return;
-    }
-
-    ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
-    EXPECT_EQ(Exact, AbsCR);
-  });
+  // We're working with unsigned integers here, because it makes the signed
+  // min case non-wrapping.
+  TestUnsignedUnaryOpExhaustive(
+      [](const ConstantRange &CR) { return CR.abs(); },
+      [](const APInt &N) { return N.abs(); });
+
+  TestUnsignedUnaryOpExhaustive(
+      [](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },
+      [](const APInt &N) { return N.abs(); },
+      /*SkipSignedIntMin=*/true);
 }
 
 TEST_F(ConstantRangeTest, castOps) {