[llvm] [KnownBits][APInt] Optimize isConstant (NFC) (PR #191919)

Max Graey via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 19 14:41:00 PDT 2026


https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/191919

>From 7bd5aaeffb247c23a051ffca097d011ea36dde5c Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Tue, 14 Apr 2026 04:18:19 +0300
Subject: [PATCH 01/12] init

---
 llvm/include/llvm/Support/KnownBits.h |  8 +++++++-
 llvm/lib/Support/KnownBits.cpp        | 14 ++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index ecedccc9afd95..d3ce50c7f78d1 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -52,7 +52,10 @@ struct KnownBits {
 
   /// Returns true if we know the value of all bits.
   bool isConstant() const {
-    return Zero.popcount() + One.popcount() == getBitWidth();
+    if (Zero.isSingleWord())
+      return (Zero.getZExtValue() | One.getZExtValue()) ==
+             llvm::maskTrailingOnes<uint64_t>(getBitWidth());
+    return isConstantSlowCase();
   }
 
   /// Returns the value when all bits have a known value. This just returns One
@@ -576,6 +579,9 @@ struct KnownBits {
   // Internal helper for getting the initial KnownBits for an `srem` or `urem`
   // operation with the low-bits set.
   static KnownBits remGetLowBits(const KnownBits &LHS, const KnownBits &RHS);
+
+  /// Multi-word case helper for isConstant().
+  bool isConstantSlowCase() const;
 };
 
 inline KnownBits operator&(KnownBits LHS, const KnownBits &RHS) {
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 07e7781d0839d..fc84f7b4100a7 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -19,6 +19,20 @@
 
 using namespace llvm;
 
+bool KnownBits::isConstantSlowCase() const {
+  const unsigned LastWord = Zero.getNumWords() - 1;
+  const uint64_t *Zeros = Zero.getRawData();
+  const uint64_t *Ones = One.getRawData();
+
+  for (unsigned I = 0; I != LastWord; ++I)
+    if ((Zeros[I] | Ones[I]) != UINT64_MAX)
+      return false;
+
+  unsigned TailBits = getBitWidth() - LastWord * APInt::APINT_BITS_PER_WORD;
+  uint64_t TailMask = llvm::maskTrailingOnes<uint64_t>(TailBits);
+  return ((Zeros[LastWord] | Ones[LastWord]) & TailMask) == TailMask;
+}
+
 KnownBits KnownBits::flipSignBit(const KnownBits &Val) {
   unsigned SignBitPosition = Val.getBitWidth() - 1;
   APInt Zero = Val.Zero;

>From 1b92e666d8effe816fd64c2a21459b33e3409dcf Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Tue, 14 Apr 2026 13:19:12 +0300
Subject: [PATCH 02/12] minor tuning

---
 llvm/include/llvm/Support/KnownBits.h | 2 +-
 llvm/lib/Support/KnownBits.cpp        | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index d3ce50c7f78d1..c27eb651aa64d 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -52,7 +52,7 @@ struct KnownBits {
 
   /// Returns true if we know the value of all bits.
   bool isConstant() const {
-    if (Zero.isSingleWord())
+    if (LLVM_LIKELY(Zero.isSingleWord()))
       return (Zero.getZExtValue() | One.getZExtValue()) ==
              llvm::maskTrailingOnes<uint64_t>(getBitWidth());
     return isConstantSlowCase();
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index fc84f7b4100a7..b22acc1a2f3eb 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -20,17 +20,17 @@
 using namespace llvm;
 
 bool KnownBits::isConstantSlowCase() const {
-  const unsigned LastWord = Zero.getNumWords() - 1;
+  const unsigned Last = Zero.getNumWords() - 1;
   const uint64_t *Zeros = Zero.getRawData();
   const uint64_t *Ones = One.getRawData();
 
-  for (unsigned I = 0; I != LastWord; ++I)
+  for (unsigned I = 0; I != Last; ++I)
     if ((Zeros[I] | Ones[I]) != UINT64_MAX)
       return false;
 
-  unsigned TailBits = getBitWidth() - LastWord * APInt::APINT_BITS_PER_WORD;
+  unsigned TailBits = getBitWidth() - Last * APInt::APINT_BITS_PER_WORD;
   uint64_t TailMask = llvm::maskTrailingOnes<uint64_t>(TailBits);
-  return ((Zeros[LastWord] | Ones[LastWord]) & TailMask) == TailMask;
+  return ((Zeros[Last] | Ones[Last]) & TailMask) == TailMask;
 }
 
 KnownBits KnownBits::flipSignBit(const KnownBits &Val) {

>From c1144c54f40d6f502a3253956c0a75fdf5baa498 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 16 Apr 2026 16:25:12 +0300
Subject: [PATCH 03/12] refactor: add isExhaustive for APInt and utilize it for
 isConstant

---
 llvm/include/llvm/ADT/APInt.h         | 15 +++++++++++++++
 llvm/include/llvm/Support/KnownBits.h |  8 +-------
 llvm/lib/Support/APInt.cpp            | 14 ++++++++++++++
 llvm/lib/Support/KnownBits.cpp        | 14 --------------
 4 files changed, 30 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 683eea4fb19a3..d0bc5b77cc2d3 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1268,6 +1268,18 @@ class [[nodiscard]] APInt {
     return isSubsetOfSlowCase(RHS);
   }
 
+  /// This operation checks if valid bits exclusively set in this APInt or RHS.
+  bool isExhaustive(const APInt &RHS) const {
+    assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
+    if (isSingleWord()) {
+      WordType Mask = BitWidth == APINT_BITS_PER_WORD
+                    ? WORDTYPE_MAX
+                    : llvm::maskTrailingOnes<WordType>(BitWidth);
+      return ((U.VAL | RHS.U.VAL) & Mask) == Mask;
+    }
+    return isExhaustiveSlowCase(RHS);
+  }
+
   /// @}
   /// \name Resizing Operators
   /// @{
@@ -2094,6 +2106,9 @@ class [[nodiscard]] APInt {
   /// out-of-line slow case for isSubsetOf.
   LLVM_ABI bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY;
 
+  /// out-of-line slow case for isExhaustive.
+  LLVM_ABI bool isExhaustiveSlowCase(const APInt &RHS) const LLVM_READONLY;
+
   /// out-of-line slow case for setBits.
   LLVM_ABI void setBitsSlowCase(unsigned loBit, unsigned hiBit);
 
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index c27eb651aa64d..2f6b9afa38cb3 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -52,10 +52,7 @@ struct KnownBits {
 
   /// Returns true if we know the value of all bits.
   bool isConstant() const {
-    if (LLVM_LIKELY(Zero.isSingleWord()))
-      return (Zero.getZExtValue() | One.getZExtValue()) ==
-             llvm::maskTrailingOnes<uint64_t>(getBitWidth());
-    return isConstantSlowCase();
+    return Zero.isExhaustive(One);
   }
 
   /// Returns the value when all bits have a known value. This just returns One
@@ -579,9 +576,6 @@ struct KnownBits {
   // Internal helper for getting the initial KnownBits for an `srem` or `urem`
   // operation with the low-bits set.
   static KnownBits remGetLowBits(const KnownBits &LHS, const KnownBits &RHS);
-
-  /// Multi-word case helper for isConstant().
-  bool isConstantSlowCase() const;
 };
 
 inline KnownBits operator&(KnownBits LHS, const KnownBits &RHS) {
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index 6aa5fc615a302..f054cc0b17e7b 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -753,6 +753,20 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const {
   return true;
 }
 
+bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
+  const unsigned Last = getNumWords() - 1;
+  const WordType *LHSWords = getRawData();
+  const WordType *RHSWords = RHS.getRawData();
+
+  for (unsigned I = 0; I != Last; ++I)
+    if ((LHSWords[I] | RHSWords[I]) != WORDTYPE_MAX)
+      return false;
+
+  unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
+  WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
+  return ((LHSWords[Last] | RHSWords[Last]) & TailMask) == TailMask;
+}
+
 APInt APInt::byteSwap() const {
   assert(BitWidth >= 16 && BitWidth % 8 == 0 && "Cannot byteswap!");
   if (BitWidth == 16)
diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index b22acc1a2f3eb..07e7781d0839d 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -19,20 +19,6 @@
 
 using namespace llvm;
 
-bool KnownBits::isConstantSlowCase() const {
-  const unsigned Last = Zero.getNumWords() - 1;
-  const uint64_t *Zeros = Zero.getRawData();
-  const uint64_t *Ones = One.getRawData();
-
-  for (unsigned I = 0; I != Last; ++I)
-    if ((Zeros[I] | Ones[I]) != UINT64_MAX)
-      return false;
-
-  unsigned TailBits = getBitWidth() - Last * APInt::APINT_BITS_PER_WORD;
-  uint64_t TailMask = llvm::maskTrailingOnes<uint64_t>(TailBits);
-  return ((Zeros[Last] | Ones[Last]) & TailMask) == TailMask;
-}
-
 KnownBits KnownBits::flipSignBit(const KnownBits &Val) {
   unsigned SignBitPosition = Val.getBitWidth() - 1;
   APInt Zero = Val.Zero;

>From 0c56d87b9bb25d99dfc5f2b414bc62e54702fbdd Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 16 Apr 2026 16:28:08 +0300
Subject: [PATCH 04/12] format

---
 llvm/include/llvm/ADT/APInt.h         | 4 ++--
 llvm/include/llvm/Support/KnownBits.h | 4 +---
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index d0bc5b77cc2d3..f441a20e81126 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1273,8 +1273,8 @@ class [[nodiscard]] APInt {
     assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
     if (isSingleWord()) {
       WordType Mask = BitWidth == APINT_BITS_PER_WORD
-                    ? WORDTYPE_MAX
-                    : llvm::maskTrailingOnes<WordType>(BitWidth);
+                          ? WORDTYPE_MAX
+                          : llvm::maskTrailingOnes<WordType>(BitWidth);
       return ((U.VAL | RHS.U.VAL) & Mask) == Mask;
     }
     return isExhaustiveSlowCase(RHS);
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 2f6b9afa38cb3..3507cf854ce54 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -51,9 +51,7 @@ struct KnownBits {
   bool hasConflict() const { return Zero.intersects(One); }
 
   /// Returns true if we know the value of all bits.
-  bool isConstant() const {
-    return Zero.isExhaustive(One);
-  }
+  bool isConstant() const { return Zero.isExhaustive(One); }
 
   /// Returns the value when all bits have a known value. This just returns One
   /// with a protective assertion.

>From 6551cc5b1225a30e5063c4ce551cb6b5a63d09a5 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 16 Apr 2026 16:45:26 +0300
Subject: [PATCH 05/12] monor opt due to high part already cleared for canon

---
 llvm/include/llvm/ADT/APInt.h | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index f441a20e81126..b4bf5244f379b 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1271,12 +1271,8 @@ class [[nodiscard]] APInt {
   /// This operation checks if valid bits exclusively set in this APInt or RHS.
   bool isExhaustive(const APInt &RHS) const {
     assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
-    if (isSingleWord()) {
-      WordType Mask = BitWidth == APINT_BITS_PER_WORD
-                          ? WORDTYPE_MAX
-                          : llvm::maskTrailingOnes<WordType>(BitWidth);
-      return ((U.VAL | RHS.U.VAL) & Mask) == Mask;
-    }
+    if (isSingleWord())
+      return (U.VAL | RHS.U.VAL) == llvm::maskTrailingOnes<WordType>(BitWidth);
     return isExhaustiveSlowCase(RHS);
   }
 

>From 7f765609f418bd16886d7619f14d35768336f6e1 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 16 Apr 2026 17:00:55 +0300
Subject: [PATCH 06/12] simplify isExhaustiveSlowCase

---
 llvm/lib/Support/APInt.cpp | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index f054cc0b17e7b..d0c6d33b49371 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -755,16 +755,13 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const {
 
 bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
   const unsigned Last = getNumWords() - 1;
-  const WordType *LHSWords = getRawData();
-  const WordType *RHSWords = RHS.getRawData();
-
   for (unsigned I = 0; I != Last; ++I)
-    if ((LHSWords[I] | RHSWords[I]) != WORDTYPE_MAX)
+    if ((U.pVal[I] | RHS.U.pVal[I]) != WORDTYPE_MAX)
       return false;
 
   unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
   WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
-  return ((LHSWords[Last] | RHSWords[Last]) & TailMask) == TailMask;
+  return ((U.pVal[Last] | RHS.U.pVal[Last]) & TailMask) == TailMask;
 }
 
 APInt APInt::byteSwap() const {

>From adf2eccaf1884f1327a90b0daaccc7355f600663 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 17 Apr 2026 02:43:07 +0300
Subject: [PATCH 07/12] cleanup unnecessary mask for hight bits in slow path

---
 llvm/lib/Support/APInt.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index d0c6d33b49371..c3ec0d224b91d 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -761,7 +761,7 @@ bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
 
   unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
   WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
-  return ((U.pVal[Last] | RHS.U.pVal[Last]) & TailMask) == TailMask;
+  return (U.pVal[Last] | RHS.U.pVal[Last]) == TailMask;
 }
 
 APInt APInt::byteSwap() const {

>From a9b132b1c38f06d3f98a93380a264bad89332e35 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sun, 19 Apr 2026 21:23:20 +0300
Subject: [PATCH 08/12] address review feedback. Add unit tests & upd doc
 comment

---
 llvm/include/llvm/ADT/APInt.h    |  2 +-
 llvm/unittests/ADT/APIntTest.cpp | 26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index b4bf5244f379b..1df7c8d393d40 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1268,7 +1268,7 @@ class [[nodiscard]] APInt {
     return isSubsetOfSlowCase(RHS);
   }
 
-  /// This operation checks if valid bits exclusively set in this APInt or RHS.
+  /// This operation checks if all bits are set in either this or RHS.
   bool isExhaustive(const APInt &RHS) const {
     assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
     if (isSingleWord())
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index ee4c59de34fc4..dc601117283af 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3057,6 +3057,32 @@ TEST(APIntTest, isSubsetOf) {
   EXPECT_TRUE(i128_3.isSubsetOf(i128_3));
 }
 
+TEST(APIntTest, isExhaustive) {
+  APInt i4_0(4, 0);
+  APInt i4_1(4, 1);
+  APInt i4_12(4, 12);
+  APInt i4_14(4, 14);
+  APInt i4_15(4, 15);
+  EXPECT_TRUE(i4_1.isExhaustive(i4_14));
+  EXPECT_TRUE(i4_14.isExhaustive(i4_1));
+  EXPECT_FALSE(i4_1.isExhaustive(i4_12));
+  EXPECT_FALSE(i4_0.isExhaustive(i4_0));
+  EXPECT_TRUE(i4_15.isExhaustive(i4_0));
+
+  APInt i128Lo64 = APInt::getLowBitsSet(128, 64);
+  APInt i128Hi64 = APInt::getHighBitsSet(128, 64);
+  APInt i128Hi63 = APInt::getHighBitsSet(128, 63);
+  EXPECT_TRUE(i128Lo64.isExhaustive(i128Hi64));
+  EXPECT_TRUE(i128Hi64.isExhaustive(i128Lo64));
+  EXPECT_FALSE(i128Lo64.isExhaustive(i128Hi63));
+
+  APInt i65Lo64 = APInt::getLowBitsSet(65, 64);
+  APInt i65Hi1 = APInt::getHighBitsSet(65, 1);
+  EXPECT_TRUE(i65Lo64.isExhaustive(i65Hi1));
+  EXPECT_TRUE(i65Hi1.isExhaustive(i65Lo64));
+  EXPECT_FALSE(i65Lo64.isExhaustive(APInt(65, 0)));
+}
+
 TEST(APIntTest, sext) {
   EXPECT_EQ(0, APInt(1, 0).sext(64));
   EXPECT_EQ(~uint64_t(0), APInt(1, 1).sext(64));

>From e669e4603ee5bec04be37d0d8b5ccabbf1eea3dd Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sun, 19 Apr 2026 23:02:02 +0300
Subject: [PATCH 09/12] try Zero == ~One approach

---
 llvm/include/llvm/ADT/APInt.h         | 11 -----------
 llvm/include/llvm/Support/KnownBits.h |  2 +-
 llvm/lib/Support/APInt.cpp            | 11 -----------
 llvm/unittests/ADT/APIntTest.cpp      | 26 --------------------------
 4 files changed, 1 insertion(+), 49 deletions(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 1df7c8d393d40..683eea4fb19a3 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1268,14 +1268,6 @@ class [[nodiscard]] APInt {
     return isSubsetOfSlowCase(RHS);
   }
 
-  /// This operation checks if all bits are set in either this or RHS.
-  bool isExhaustive(const APInt &RHS) const {
-    assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
-    if (isSingleWord())
-      return (U.VAL | RHS.U.VAL) == llvm::maskTrailingOnes<WordType>(BitWidth);
-    return isExhaustiveSlowCase(RHS);
-  }
-
   /// @}
   /// \name Resizing Operators
   /// @{
@@ -2102,9 +2094,6 @@ class [[nodiscard]] APInt {
   /// out-of-line slow case for isSubsetOf.
   LLVM_ABI bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY;
 
-  /// out-of-line slow case for isExhaustive.
-  LLVM_ABI bool isExhaustiveSlowCase(const APInt &RHS) const LLVM_READONLY;
-
   /// out-of-line slow case for setBits.
   LLVM_ABI void setBitsSlowCase(unsigned loBit, unsigned hiBit);
 
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 3507cf854ce54..dbc1ac36e4ae3 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -51,7 +51,7 @@ struct KnownBits {
   bool hasConflict() const { return Zero.intersects(One); }
 
   /// Returns true if we know the value of all bits.
-  bool isConstant() const { return Zero.isExhaustive(One); }
+  bool isConstant() const { return Zero == ~One; }
 
   /// Returns the value when all bits have a known value. This just returns One
   /// with a protective assertion.
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index c3ec0d224b91d..6aa5fc615a302 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -753,17 +753,6 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const {
   return true;
 }
 
-bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
-  const unsigned Last = getNumWords() - 1;
-  for (unsigned I = 0; I != Last; ++I)
-    if ((U.pVal[I] | RHS.U.pVal[I]) != WORDTYPE_MAX)
-      return false;
-
-  unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
-  WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
-  return (U.pVal[Last] | RHS.U.pVal[Last]) == TailMask;
-}
-
 APInt APInt::byteSwap() const {
   assert(BitWidth >= 16 && BitWidth % 8 == 0 && "Cannot byteswap!");
   if (BitWidth == 16)
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index dc601117283af..ee4c59de34fc4 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3057,32 +3057,6 @@ TEST(APIntTest, isSubsetOf) {
   EXPECT_TRUE(i128_3.isSubsetOf(i128_3));
 }
 
-TEST(APIntTest, isExhaustive) {
-  APInt i4_0(4, 0);
-  APInt i4_1(4, 1);
-  APInt i4_12(4, 12);
-  APInt i4_14(4, 14);
-  APInt i4_15(4, 15);
-  EXPECT_TRUE(i4_1.isExhaustive(i4_14));
-  EXPECT_TRUE(i4_14.isExhaustive(i4_1));
-  EXPECT_FALSE(i4_1.isExhaustive(i4_12));
-  EXPECT_FALSE(i4_0.isExhaustive(i4_0));
-  EXPECT_TRUE(i4_15.isExhaustive(i4_0));
-
-  APInt i128Lo64 = APInt::getLowBitsSet(128, 64);
-  APInt i128Hi64 = APInt::getHighBitsSet(128, 64);
-  APInt i128Hi63 = APInt::getHighBitsSet(128, 63);
-  EXPECT_TRUE(i128Lo64.isExhaustive(i128Hi64));
-  EXPECT_TRUE(i128Hi64.isExhaustive(i128Lo64));
-  EXPECT_FALSE(i128Lo64.isExhaustive(i128Hi63));
-
-  APInt i65Lo64 = APInt::getLowBitsSet(65, 64);
-  APInt i65Hi1 = APInt::getHighBitsSet(65, 1);
-  EXPECT_TRUE(i65Lo64.isExhaustive(i65Hi1));
-  EXPECT_TRUE(i65Hi1.isExhaustive(i65Lo64));
-  EXPECT_FALSE(i65Lo64.isExhaustive(APInt(65, 0)));
-}
-
 TEST(APIntTest, sext) {
   EXPECT_EQ(0, APInt(1, 0).sext(64));
   EXPECT_EQ(~uint64_t(0), APInt(1, 1).sext(64));

>From d6f02ce0177ee5021d3625314e941219e754e0da Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 20 Apr 2026 00:21:33 +0300
Subject: [PATCH 10/12] Revert "try Zero == ~One approach"

This reverts commit e669e4603ee5bec04be37d0d8b5ccabbf1eea3dd.
---
 llvm/include/llvm/ADT/APInt.h         | 11 +++++++++++
 llvm/include/llvm/Support/KnownBits.h |  2 +-
 llvm/lib/Support/APInt.cpp            | 11 +++++++++++
 llvm/unittests/ADT/APIntTest.cpp      | 26 ++++++++++++++++++++++++++
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 683eea4fb19a3..1df7c8d393d40 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1268,6 +1268,14 @@ class [[nodiscard]] APInt {
     return isSubsetOfSlowCase(RHS);
   }
 
+  /// This operation checks if all bits are set in either this or RHS.
+  bool isExhaustive(const APInt &RHS) const {
+    assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
+    if (isSingleWord())
+      return (U.VAL | RHS.U.VAL) == llvm::maskTrailingOnes<WordType>(BitWidth);
+    return isExhaustiveSlowCase(RHS);
+  }
+
   /// @}
   /// \name Resizing Operators
   /// @{
@@ -2094,6 +2102,9 @@ class [[nodiscard]] APInt {
   /// out-of-line slow case for isSubsetOf.
   LLVM_ABI bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY;
 
+  /// out-of-line slow case for isExhaustive.
+  LLVM_ABI bool isExhaustiveSlowCase(const APInt &RHS) const LLVM_READONLY;
+
   /// out-of-line slow case for setBits.
   LLVM_ABI void setBitsSlowCase(unsigned loBit, unsigned hiBit);
 
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index dbc1ac36e4ae3..3507cf854ce54 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -51,7 +51,7 @@ struct KnownBits {
   bool hasConflict() const { return Zero.intersects(One); }
 
   /// Returns true if we know the value of all bits.
-  bool isConstant() const { return Zero == ~One; }
+  bool isConstant() const { return Zero.isExhaustive(One); }
 
   /// Returns the value when all bits have a known value. This just returns One
   /// with a protective assertion.
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index 6aa5fc615a302..c3ec0d224b91d 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -753,6 +753,17 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const {
   return true;
 }
 
+bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
+  const unsigned Last = getNumWords() - 1;
+  for (unsigned I = 0; I != Last; ++I)
+    if ((U.pVal[I] | RHS.U.pVal[I]) != WORDTYPE_MAX)
+      return false;
+
+  unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
+  WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
+  return (U.pVal[Last] | RHS.U.pVal[Last]) == TailMask;
+}
+
 APInt APInt::byteSwap() const {
   assert(BitWidth >= 16 && BitWidth % 8 == 0 && "Cannot byteswap!");
   if (BitWidth == 16)
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index ee4c59de34fc4..dc601117283af 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3057,6 +3057,32 @@ TEST(APIntTest, isSubsetOf) {
   EXPECT_TRUE(i128_3.isSubsetOf(i128_3));
 }
 
+TEST(APIntTest, isExhaustive) {
+  APInt i4_0(4, 0);
+  APInt i4_1(4, 1);
+  APInt i4_12(4, 12);
+  APInt i4_14(4, 14);
+  APInt i4_15(4, 15);
+  EXPECT_TRUE(i4_1.isExhaustive(i4_14));
+  EXPECT_TRUE(i4_14.isExhaustive(i4_1));
+  EXPECT_FALSE(i4_1.isExhaustive(i4_12));
+  EXPECT_FALSE(i4_0.isExhaustive(i4_0));
+  EXPECT_TRUE(i4_15.isExhaustive(i4_0));
+
+  APInt i128Lo64 = APInt::getLowBitsSet(128, 64);
+  APInt i128Hi64 = APInt::getHighBitsSet(128, 64);
+  APInt i128Hi63 = APInt::getHighBitsSet(128, 63);
+  EXPECT_TRUE(i128Lo64.isExhaustive(i128Hi64));
+  EXPECT_TRUE(i128Hi64.isExhaustive(i128Lo64));
+  EXPECT_FALSE(i128Lo64.isExhaustive(i128Hi63));
+
+  APInt i65Lo64 = APInt::getLowBitsSet(65, 64);
+  APInt i65Hi1 = APInt::getHighBitsSet(65, 1);
+  EXPECT_TRUE(i65Lo64.isExhaustive(i65Hi1));
+  EXPECT_TRUE(i65Hi1.isExhaustive(i65Lo64));
+  EXPECT_FALSE(i65Lo64.isExhaustive(APInt(65, 0)));
+}
+
 TEST(APIntTest, sext) {
   EXPECT_EQ(0, APInt(1, 0).sext(64));
   EXPECT_EQ(~uint64_t(0), APInt(1, 1).sext(64));

>From 4eb563c20387783d7601f864b3d9028061f0046b Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 20 Apr 2026 00:37:28 +0300
Subject: [PATCH 11/12] rename isExhaustive to isInverseOf. Use Xor instead Or

---
 llvm/include/llvm/ADT/APInt.h         | 10 +++++-----
 llvm/include/llvm/Support/KnownBits.h |  2 +-
 llvm/lib/Support/APInt.cpp            |  6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 1df7c8d393d40..2b85a26fcb371 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1269,11 +1269,11 @@ class [[nodiscard]] APInt {
   }
 
   /// This operation checks if all bits are set in either this or RHS.
-  bool isExhaustive(const APInt &RHS) const {
+  bool isInverseOf(const APInt &RHS) const {
     assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
     if (isSingleWord())
-      return (U.VAL | RHS.U.VAL) == llvm::maskTrailingOnes<WordType>(BitWidth);
-    return isExhaustiveSlowCase(RHS);
+      return (U.VAL ^ RHS.U.VAL) == llvm::maskTrailingOnes<WordType>(BitWidth);
+    return isInverseOfSlowCase(RHS);
   }
 
   /// @}
@@ -2102,8 +2102,8 @@ class [[nodiscard]] APInt {
   /// out-of-line slow case for isSubsetOf.
   LLVM_ABI bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY;
 
-  /// out-of-line slow case for isExhaustive.
-  LLVM_ABI bool isExhaustiveSlowCase(const APInt &RHS) const LLVM_READONLY;
+  /// out-of-line slow case for isInverseOf.
+  LLVM_ABI bool isInverseOfSlowCase(const APInt &RHS) const LLVM_READONLY;
 
   /// out-of-line slow case for setBits.
   LLVM_ABI void setBitsSlowCase(unsigned loBit, unsigned hiBit);
diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h
index 3507cf854ce54..265703c0cc55d 100644
--- a/llvm/include/llvm/Support/KnownBits.h
+++ b/llvm/include/llvm/Support/KnownBits.h
@@ -51,7 +51,7 @@ struct KnownBits {
   bool hasConflict() const { return Zero.intersects(One); }
 
   /// Returns true if we know the value of all bits.
-  bool isConstant() const { return Zero.isExhaustive(One); }
+  bool isConstant() const { return Zero.isInverseOf(One); }
 
   /// Returns the value when all bits have a known value. This just returns One
   /// with a protective assertion.
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index c3ec0d224b91d..417db25af8c37 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -753,15 +753,15 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const {
   return true;
 }
 
-bool APInt::isExhaustiveSlowCase(const APInt &RHS) const {
+bool APInt::isInverseOfSlowCase(const APInt &RHS) const {
   const unsigned Last = getNumWords() - 1;
   for (unsigned I = 0; I != Last; ++I)
-    if ((U.pVal[I] | RHS.U.pVal[I]) != WORDTYPE_MAX)
+    if ((U.pVal[I] ^ RHS.U.pVal[I]) != WORDTYPE_MAX)
       return false;
 
   unsigned TailBits = BitWidth - Last * APINT_BITS_PER_WORD;
   WordType TailMask = llvm::maskTrailingOnes<WordType>(TailBits);
-  return (U.pVal[Last] | RHS.U.pVal[Last]) == TailMask;
+  return (U.pVal[Last] ^ RHS.U.pVal[Last]) == TailMask;
 }
 
 APInt APInt::byteSwap() const {

>From 234a2e8f1c60ba108fc7b8fad9040fa09e18a8d9 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 20 Apr 2026 00:40:39 +0300
Subject: [PATCH 12/12] update tests

---
 llvm/unittests/ADT/APIntTest.cpp | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index dc601117283af..7a45fd0f4558c 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3057,30 +3057,33 @@ TEST(APIntTest, isSubsetOf) {
   EXPECT_TRUE(i128_3.isSubsetOf(i128_3));
 }
 
-TEST(APIntTest, isExhaustive) {
+TEST(APIntTest, isInverseOf) {
   APInt i4_0(4, 0);
   APInt i4_1(4, 1);
+  APInt i4_3(4, 3);
   APInt i4_12(4, 12);
+  APInt i4_13(4, 13);
   APInt i4_14(4, 14);
   APInt i4_15(4, 15);
-  EXPECT_TRUE(i4_1.isExhaustive(i4_14));
-  EXPECT_TRUE(i4_14.isExhaustive(i4_1));
-  EXPECT_FALSE(i4_1.isExhaustive(i4_12));
-  EXPECT_FALSE(i4_0.isExhaustive(i4_0));
-  EXPECT_TRUE(i4_15.isExhaustive(i4_0));
+  EXPECT_TRUE(i4_1.isInverseOf(i4_14));
+  EXPECT_TRUE(i4_14.isInverseOf(i4_1));
+  EXPECT_FALSE(i4_1.isInverseOf(i4_12));
+  EXPECT_FALSE(i4_3.isInverseOf(i4_13));
+  EXPECT_FALSE(i4_0.isInverseOf(i4_0));
+  EXPECT_TRUE(i4_15.isInverseOf(i4_0));
 
   APInt i128Lo64 = APInt::getLowBitsSet(128, 64);
   APInt i128Hi64 = APInt::getHighBitsSet(128, 64);
   APInt i128Hi63 = APInt::getHighBitsSet(128, 63);
-  EXPECT_TRUE(i128Lo64.isExhaustive(i128Hi64));
-  EXPECT_TRUE(i128Hi64.isExhaustive(i128Lo64));
-  EXPECT_FALSE(i128Lo64.isExhaustive(i128Hi63));
+  EXPECT_TRUE(i128Lo64.isInverseOf(i128Hi64));
+  EXPECT_TRUE(i128Hi64.isInverseOf(i128Lo64));
+  EXPECT_FALSE(i128Lo64.isInverseOf(i128Hi63));
 
   APInt i65Lo64 = APInt::getLowBitsSet(65, 64);
   APInt i65Hi1 = APInt::getHighBitsSet(65, 1);
-  EXPECT_TRUE(i65Lo64.isExhaustive(i65Hi1));
-  EXPECT_TRUE(i65Hi1.isExhaustive(i65Lo64));
-  EXPECT_FALSE(i65Lo64.isExhaustive(APInt(65, 0)));
+  EXPECT_TRUE(i65Lo64.isInverseOf(i65Hi1));
+  EXPECT_TRUE(i65Hi1.isInverseOf(i65Lo64));
+  EXPECT_FALSE(i65Lo64.isInverseOf(APInt(65, 0)));
 }
 
 TEST(APIntTest, sext) {



More information about the llvm-commits mailing list