[llvm] 0f1fb62 - [KnownBitsTest] Align with ConstantRange test infrastructure (NFC)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon May 15 07:49:05 PDT 2023


Author: Nikita Popov
Date: 2023-05-15T16:48:57+02:00
New Revision: 0f1fb626b3314386d3885f328cae36b830a04901

URL: https://github.com/llvm/llvm-project/commit/0f1fb626b3314386d3885f328cae36b830a04901
DIFF: https://github.com/llvm/llvm-project/commit/0f1fb626b3314386d3885f328cae36b830a04901.diff

LOG: [KnownBitsTest] Align with ConstantRange test infrastructure (NFC)

Align the way we perform exhaustive tests for KnownBits with what
we do for ConstantRange. Test each case separately by specifying
a function on KnownBits and one on APInts. Additionally, specify
a callback that determines which cases are supposed to be optimal,
rather than only correct. Unlike the ConstantRange case there is
a well-defined, unique notion of optimality for KnownBits.

If a failure occurs, print out the inputs, computed result and
exact result. Adjust the printing function to produce the output
in a format that is meaningful for KnownBits, i.e. print the
actual known bits, using ? to signify unknowns and ! to signify
conflicts.

Added: 
    

Modified: 
    llvm/include/llvm/Support/KnownBits.h
    llvm/lib/Support/KnownBits.cpp
    llvm/unittests/Support/KnownBitsTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 6344a3cf90764..f2c8e3814c0aa 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -457,6 +457,11 @@ inline KnownBits operator^(const KnownBits &LHS, KnownBits &&RHS) {
   return std::move(RHS);
 }
 
+inline raw_ostream &operator<<(raw_ostream &OS, const KnownBits &Known) {
+  Known.print(OS);
+  return OS;
+}
+
 } // end namespace llvm
 
 #endif

diff  --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index e20838278e612..ad6e1c8b7003b 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -652,7 +652,18 @@ KnownBits KnownBits::blsmsk() const {
 }
 
 void KnownBits::print(raw_ostream &OS) const {
-  OS << "{Zero=" << Zero << ", One=" << One << "}";
+  unsigned BitWidth = getBitWidth();
+  for (unsigned I = 0; I < BitWidth; ++I) {
+    unsigned N = BitWidth - I - 1;
+    if (Zero[N] && One[N])
+      OS << "!";
+    else if (Zero[N])
+      OS << "0";
+    else if (One[N])
+      OS << "1";
+    else
+      OS << "?";
+  }
 }
 void KnownBits::dump() const {
   print(dbgs());

diff  --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp
index da1f6b50bdc1b..dbdf28c55a127 100644
--- a/llvm/unittests/Support/KnownBitsTest.cpp
+++ b/llvm/unittests/Support/KnownBitsTest.cpp
@@ -10,12 +10,116 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/KnownBits.h"
 #include "KnownBitsTest.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
 
+using UnaryBitsFn = llvm::function_ref<KnownBits(const KnownBits &)>;
+using UnaryIntFn = llvm::function_ref<std::optional<APInt>(const APInt &)>;
+using UnaryCheckFn = llvm::function_ref<bool(const KnownBits &)>;
+
+using BinaryBitsFn =
+    llvm::function_ref<KnownBits(const KnownBits &, const KnownBits &)>;
+using BinaryIntFn =
+    llvm::function_ref<std::optional<APInt>(const APInt &, const APInt &)>;
+using BinaryCheckFn =
+    llvm::function_ref<bool(const KnownBits &, const KnownBits &)>;
+
+static bool checkCorrectnessOnlyUnary(const KnownBits &) { return false; }
+static bool checkCorrectnessOnlyBinary(const KnownBits &, const KnownBits &) {
+  return false;
+}
+
+static testing::AssertionResult isCorrect(const KnownBits &Exact,
+                                          const KnownBits &Computed,
+                                          ArrayRef<KnownBits> Inputs) {
+  if (Computed.Zero.isSubsetOf(Exact.Zero) &&
+      Computed.One.isSubsetOf(Exact.One))
+    return testing::AssertionSuccess();
+
+  testing::AssertionResult Result = testing::AssertionFailure();
+  Result << "Inputs = ";
+  for (const KnownBits &Input : Inputs)
+    Result << Input << ", ";
+  Result << "Computed = " << Computed << ", Exact = " << Exact;
+  return Result;
+};
+
+static testing::AssertionResult isOptimal(const KnownBits &Exact,
+                                          const KnownBits &Computed,
+                                          ArrayRef<KnownBits> Inputs) {
+  if (Computed == Exact)
+    return testing::AssertionSuccess();
+
+  testing::AssertionResult Result = testing::AssertionFailure();
+  Result << "Inputs = ";
+  for (const KnownBits &Input : Inputs)
+    Result << Input << ", ";
+  Result << "Computed = " << Computed << ", Exact = " << Exact;
+  return Result;
+};
+
+static void testUnaryOpExhaustive(
+    UnaryBitsFn BitsFn, UnaryIntFn IntFn,
+    UnaryCheckFn CheckOptimalityFn = [](const KnownBits &) { return true; }) {
+  unsigned Bits = 4;
+  ForeachKnownBits(Bits, [&](const KnownBits &Known) {
+    KnownBits Computed = BitsFn(Known);
+    KnownBits Exact(Bits);
+    Exact.Zero.setAllBits();
+    Exact.One.setAllBits();
+
+    ForeachNumInKnownBits(Known, [&](const APInt &N) {
+      if (std::optional<APInt> Res = IntFn(N)) {
+        Exact.One &= *Res;
+        Exact.Zero &= ~*Res;
+      }
+    });
+
+    EXPECT_TRUE(isCorrect(Exact, Computed, Known));
+    // We generally don't want to return conflicting known bits, even if it is
+    // legal for always poison results.
+    if (CheckOptimalityFn(Known) && !Exact.hasConflict()) {
+      EXPECT_TRUE(isOptimal(Exact, Computed, Known));
+    }
+  });
+}
+
+static void testBinaryOpExhaustive(
+    BinaryBitsFn BitsFn, BinaryIntFn IntFn,
+    BinaryCheckFn CheckOptimalityFn = [](const KnownBits &, const KnownBits &) {
+      return true;
+    }) {
+  unsigned Bits = 4;
+  ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
+    ForeachKnownBits(Bits, [&](const KnownBits &Known2) {
+      KnownBits Computed = BitsFn(Known1, Known2);
+      KnownBits Exact(Bits);
+      Exact.Zero.setAllBits();
+      Exact.One.setAllBits();
+
+      ForeachNumInKnownBits(Known1, [&](const APInt &N1) {
+        ForeachNumInKnownBits(Known2, [&](const APInt &N2) {
+          if (std::optional<APInt> Res = IntFn(N1, N2)) {
+            Exact.One &= *Res;
+            Exact.Zero &= ~*Res;
+          }
+        });
+      });
+
+      EXPECT_TRUE(isCorrect(Exact, Computed, {Known1, Known2}));
+      // We generally don't want to return conflicting known bits, even if it is
+      // legal for always poison results.
+      if (CheckOptimalityFn(Known1, Known2) && !Exact.hasConflict()) {
+        EXPECT_TRUE(isOptimal(Exact, Computed, {Known1, Known2}));
+      }
+    });
+  });
+}
+
 namespace {
 
 TEST(KnownBitsTest, AddCarryExhaustive) {
@@ -98,233 +202,164 @@ TEST(KnownBitsTest, AddSubExhaustive) {
 }
 
 TEST(KnownBitsTest, BinaryExhaustive) {
-  unsigned Bits = 4;
-  ForeachKnownBits(Bits, [&](const KnownBits &Known1) {
-    ForeachKnownBits(Bits, [&](const KnownBits &Known2) {
-      KnownBits KnownAnd(Bits);
-      KnownAnd.Zero.setAllBits();
-      KnownAnd.One.setAllBits();
-      KnownBits KnownOr(KnownAnd);
-      KnownBits KnownXor(KnownAnd);
-      KnownBits KnownUMax(KnownAnd);
-      KnownBits KnownUMin(KnownAnd);
-      KnownBits KnownSMax(KnownAnd);
-      KnownBits KnownSMin(KnownAnd);
-      KnownBits KnownMul(KnownAnd);
-      KnownBits KnownMulHS(KnownAnd);
-      KnownBits KnownMulHU(KnownAnd);
-      KnownBits KnownUDiv(KnownAnd);
-      KnownBits KnownURem(KnownAnd);
-      KnownBits KnownSRem(KnownAnd);
-      KnownBits KnownShl(KnownAnd);
-      KnownBits KnownLShr(KnownAnd);
-      KnownBits KnownAShr(KnownAnd);
-
-      ForeachNumInKnownBits(Known1, [&](const APInt &N1) {
-        ForeachNumInKnownBits(Known2, [&](const APInt &N2) {
-          APInt Res;
-
-          Res = N1 & N2;
-          KnownAnd.One &= Res;
-          KnownAnd.Zero &= ~Res;
-
-          Res = N1 | N2;
-          KnownOr.One &= Res;
-          KnownOr.Zero &= ~Res;
-
-          Res = N1 ^ N2;
-          KnownXor.One &= Res;
-          KnownXor.Zero &= ~Res;
-
-          Res = APIntOps::umax(N1, N2);
-          KnownUMax.One &= Res;
-          KnownUMax.Zero &= ~Res;
-
-          Res = APIntOps::umin(N1, N2);
-          KnownUMin.One &= Res;
-          KnownUMin.Zero &= ~Res;
-
-          Res = APIntOps::smax(N1, N2);
-          KnownSMax.One &= Res;
-          KnownSMax.Zero &= ~Res;
-
-          Res = APIntOps::smin(N1, N2);
-          KnownSMin.One &= Res;
-          KnownSMin.Zero &= ~Res;
-
-          Res = N1 * N2;
-          KnownMul.One &= Res;
-          KnownMul.Zero &= ~Res;
-
-          Res = (N1.sext(2 * Bits) * N2.sext(2 * Bits)).extractBits(Bits, Bits);
-          KnownMulHS.One &= Res;
-          KnownMulHS.Zero &= ~Res;
-
-          Res = (N1.zext(2 * Bits) * N2.zext(2 * Bits)).extractBits(Bits, Bits);
-          KnownMulHU.One &= Res;
-          KnownMulHU.Zero &= ~Res;
-
-          if (!N2.isZero()) {
-            Res = N1.udiv(N2);
-            KnownUDiv.One &= Res;
-            KnownUDiv.Zero &= ~Res;
-
-            Res = N1.urem(N2);
-            KnownURem.One &= Res;
-            KnownURem.Zero &= ~Res;
-
-            Res = N1.srem(N2);
-            KnownSRem.One &= Res;
-            KnownSRem.Zero &= ~Res;
-          }
-
-          if (N2.ult(1ULL << N1.getBitWidth())) {
-            Res = N1.shl(N2);
-            KnownShl.One &= Res;
-            KnownShl.Zero &= ~Res;
-
-            Res = N1.lshr(N2);
-            KnownLShr.One &= Res;
-            KnownLShr.Zero &= ~Res;
-
-            Res = N1.ashr(N2);
-            KnownAShr.One &= Res;
-            KnownAShr.Zero &= ~Res;
-          } else {
-            KnownShl.resetAll();
-            KnownLShr.resetAll();
-            KnownAShr.resetAll();
-          }
-        });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return Known1 & Known2;
+      },
+      [](const APInt &N1, const APInt &N2) { return N1 & N2; });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return Known1 | Known2;
+      },
+      [](const APInt &N1, const APInt &N2) { return N1 | N2; });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return Known1 ^ Known2;
+      },
+      [](const APInt &N1, const APInt &N2) { return N1 ^ N2; });
+
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::umax(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) { return APIntOps::umax(N1, N2); });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::umin(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) { return APIntOps::umin(N1, N2); });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::smax(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) { return APIntOps::smax(N1, N2); });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::smin(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) { return APIntOps::smin(N1, N2); });
+
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::udiv(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.isZero())
+          return std::nullopt;
+        return N1.udiv(N2);
+      },
+      checkCorrectnessOnlyBinary);
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::urem(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.isZero())
+          return std::nullopt;
+        return N1.urem(N2);
+      },
+      checkCorrectnessOnlyBinary);
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::srem(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.isZero())
+          return std::nullopt;
+        return N1.srem(N2);
+      },
+      checkCorrectnessOnlyBinary);
+
+  // TODO: Make optimal for non-constant cases.
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::shl(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.shl(N2);
+      },
+      [](const KnownBits &, const KnownBits &Known) {
+        return Known.isConstant();
+      });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::lshr(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.lshr(N2);
+      },
+      [](const KnownBits &, const KnownBits &Known) {
+        return Known.isConstant();
+      });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::ashr(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+        if (N2.uge(N2.getBitWidth()))
+          return std::nullopt;
+        return N1.ashr(N2);
+      },
+      [](const KnownBits &, const KnownBits &Known) {
+        return Known.isConstant();
       });
 
-      KnownBits ComputedAnd = Known1 & Known2;
-      EXPECT_EQ(KnownAnd, ComputedAnd);
-
-      KnownBits ComputedOr = Known1 | Known2;
-      EXPECT_EQ(KnownOr, ComputedOr);
-
-      KnownBits ComputedXor = Known1 ^ Known2;
-      EXPECT_EQ(KnownXor, ComputedXor);
-
-      KnownBits ComputedUMax = KnownBits::umax(Known1, Known2);
-      EXPECT_EQ(KnownUMax, ComputedUMax);
-
-      KnownBits ComputedUMin = KnownBits::umin(Known1, Known2);
-      EXPECT_EQ(KnownUMin, ComputedUMin);
-
-      KnownBits ComputedSMax = KnownBits::smax(Known1, Known2);
-      EXPECT_EQ(KnownSMax, ComputedSMax);
-
-      KnownBits ComputedSMin = KnownBits::smin(Known1, Known2);
-      EXPECT_EQ(KnownSMin, ComputedSMin);
-
-      // The following are conservatively correct, but not guaranteed to be
-      // precise.
-      KnownBits ComputedMul = KnownBits::mul(Known1, Known2);
-      EXPECT_TRUE(ComputedMul.Zero.isSubsetOf(KnownMul.Zero));
-      EXPECT_TRUE(ComputedMul.One.isSubsetOf(KnownMul.One));
-
-      KnownBits ComputedMulHS = KnownBits::mulhs(Known1, Known2);
-      EXPECT_TRUE(ComputedMulHS.Zero.isSubsetOf(KnownMulHS.Zero));
-      EXPECT_TRUE(ComputedMulHS.One.isSubsetOf(KnownMulHS.One));
-
-      KnownBits ComputedMulHU = KnownBits::mulhu(Known1, Known2);
-      EXPECT_TRUE(ComputedMulHU.Zero.isSubsetOf(KnownMulHU.Zero));
-      EXPECT_TRUE(ComputedMulHU.One.isSubsetOf(KnownMulHU.One));
-
-      KnownBits ComputedUDiv = KnownBits::udiv(Known1, Known2);
-      EXPECT_TRUE(ComputedUDiv.Zero.isSubsetOf(KnownUDiv.Zero));
-      EXPECT_TRUE(ComputedUDiv.One.isSubsetOf(KnownUDiv.One));
-
-      KnownBits ComputedURem = KnownBits::urem(Known1, Known2);
-      EXPECT_TRUE(ComputedURem.Zero.isSubsetOf(KnownURem.Zero));
-      EXPECT_TRUE(ComputedURem.One.isSubsetOf(KnownURem.One));
-
-      KnownBits ComputedSRem = KnownBits::srem(Known1, Known2);
-      EXPECT_TRUE(ComputedSRem.Zero.isSubsetOf(KnownSRem.Zero));
-      EXPECT_TRUE(ComputedSRem.One.isSubsetOf(KnownSRem.One));
-
-      KnownBits ComputedShl = KnownBits::shl(Known1, Known2);
-      EXPECT_TRUE(ComputedShl.Zero.isSubsetOf(KnownShl.Zero));
-      EXPECT_TRUE(ComputedShl.One.isSubsetOf(KnownShl.One));
-
-      KnownBits ComputedLShr = KnownBits::lshr(Known1, Known2);
-      EXPECT_TRUE(ComputedLShr.Zero.isSubsetOf(KnownLShr.Zero));
-      EXPECT_TRUE(ComputedLShr.One.isSubsetOf(KnownLShr.One));
-
-      KnownBits ComputedAShr = KnownBits::ashr(Known1, Known2);
-      EXPECT_TRUE(ComputedAShr.Zero.isSubsetOf(KnownAShr.Zero));
-      EXPECT_TRUE(ComputedAShr.One.isSubsetOf(KnownAShr.One));
-    });
-  });
-
-  // Also test 'unary' binary cases where the same argument is repeated.
-  ForeachKnownBits(Bits, [&](const KnownBits &Known) {
-    KnownBits KnownMul(Bits);
-    KnownMul.Zero.setAllBits();
-    KnownMul.One.setAllBits();
-
-    ForeachNumInKnownBits(Known, [&](const APInt &N) {
-      APInt Res = N * N;
-      KnownMul.One &= Res;
-      KnownMul.Zero &= ~Res;
-    });
-
-    KnownBits ComputedMul = KnownBits::mul(Known, Known, /*SelfMultiply*/ true);
-    EXPECT_TRUE(ComputedMul.Zero.isSubsetOf(KnownMul.Zero));
-    EXPECT_TRUE(ComputedMul.One.isSubsetOf(KnownMul.One));
-  });
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::mul(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) { return N1 * N2; },
+      checkCorrectnessOnlyBinary);
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::mulhs(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) {
+        unsigned Bits = N1.getBitWidth();
+        return (N1.sext(2 * Bits) * N2.sext(2 * Bits)).extractBits(Bits, Bits);
+      },
+      checkCorrectnessOnlyBinary);
+  testBinaryOpExhaustive(
+      [](const KnownBits &Known1, const KnownBits &Known2) {
+        return KnownBits::mulhu(Known1, Known2);
+      },
+      [](const APInt &N1, const APInt &N2) {
+        unsigned Bits = N1.getBitWidth();
+        return (N1.zext(2 * Bits) * N2.zext(2 * Bits)).extractBits(Bits, Bits);
+      },
+      checkCorrectnessOnlyBinary);
 }
 
 TEST(KnownBitsTest, UnaryExhaustive) {
-  unsigned Bits = 4;
-  ForeachKnownBits(Bits, [&](const KnownBits &Known) {
-    KnownBits KnownAbs(Bits);
-    KnownAbs.Zero.setAllBits();
-    KnownAbs.One.setAllBits();
-    KnownBits KnownAbsPoison(KnownAbs);
-    KnownBits KnownBlsi(Bits);
-    KnownBlsi.Zero.setAllBits();
-    KnownBlsi.One.setAllBits();
-    KnownBits KnownBlsmsk(Bits);
-    KnownBlsmsk.Zero.setAllBits();
-    KnownBlsmsk.One.setAllBits();
-
-    ForeachNumInKnownBits(Known, [&](const APInt &N) {
-      APInt Res = N.abs();
-      KnownAbs.One &= Res;
-      KnownAbs.Zero &= ~Res;
-
-      if (!N.isMinSignedValue()) {
-        KnownAbsPoison.One &= Res;
-        KnownAbsPoison.Zero &= ~Res;
-      }
-
-      Res = N & -N;
-      KnownBlsi.One &= Res;
-      KnownBlsi.Zero &= ~Res;
-
-      Res = N ^ (N - 1);
-      KnownBlsmsk.One &= Res;
-      KnownBlsmsk.Zero &= ~Res;
-    });
-
-    // abs() is conservatively correct, but not guaranteed to be precise.
-    KnownBits ComputedAbs = Known.abs();
-    EXPECT_TRUE(ComputedAbs.Zero.isSubsetOf(KnownAbs.Zero));
-    EXPECT_TRUE(ComputedAbs.One.isSubsetOf(KnownAbs.One));
-
-    KnownBits ComputedAbsPoison = Known.abs(true);
-    EXPECT_TRUE(ComputedAbsPoison.Zero.isSubsetOf(KnownAbsPoison.Zero));
-    EXPECT_TRUE(ComputedAbsPoison.One.isSubsetOf(KnownAbsPoison.One));
-
-    KnownBits ComputedBlsi = Known.blsi();
-    EXPECT_EQ(KnownBlsi, ComputedBlsi);
-
-    KnownBits ComputedBlsmsk = Known.blsmsk();
-    EXPECT_EQ(KnownBlsmsk, ComputedBlsmsk);
-  });
+  // TODO: Make optimal for cases that are not known non-negative.
+  testUnaryOpExhaustive(
+      [](const KnownBits &Known) { return Known.abs(); },
+      [](const APInt &N) { return N.abs(); },
+      [](const KnownBits &Known) { return Known.isNonNegative(); });
+
+  testUnaryOpExhaustive(
+      [](const KnownBits &Known) { return Known.abs(true); },
+      [](const APInt &N) -> std::optional<APInt> {
+        if (N.isMinSignedValue())
+          return std::nullopt;
+        return N.abs();
+      },
+      [](const KnownBits &Known) { return Known.isNonNegative(); });
+
+  testUnaryOpExhaustive([](const KnownBits &Known) { return Known.blsi(); },
+                        [](const APInt &N) { return N & -N; });
+  testUnaryOpExhaustive([](const KnownBits &Known) { return Known.blsmsk(); },
+                        [](const APInt &N) { return N ^ (N - 1); });
+
+  testUnaryOpExhaustive(
+      [](const KnownBits &Known) {
+        return KnownBits::mul(Known, Known, /*SelfMultiply*/ true);
+      },
+      [](const APInt &N) { return N * N; }, checkCorrectnessOnlyUnary);
 }
 
 TEST(KnownBitsTest, ICmpExhaustive) {


        


More information about the llvm-commits mailing list