[llvm] [InstCombine] Add additional known bits info for self multiply (PR #151202)

Macsen Casaus via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 8 17:45:50 PDT 2025


https://github.com/macsencasaus updated https://github.com/llvm/llvm-project/pull/151202

>From 4d0e99052769aeb8f5eb92745dad2733364b0664 Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Tue, 29 Jul 2025 10:44:11 -0600
Subject: [PATCH 1/6] pre-commit test

---
 .../test/Transforms/InstCombine/known-bits.ll | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/known-bits.ll b/llvm/test/Transforms/InstCombine/known-bits.ll
index 9a9fec694ff0e..3868b5c3701c9 100644
--- a/llvm/test/Transforms/InstCombine/known-bits.ll
+++ b/llvm/test/Transforms/InstCombine/known-bits.ll
@@ -1244,6 +1244,62 @@ define i1 @extract_value_smul_fail(i8 %xx, i8 %yy) {
   ret i1 %r
 }
 
+define i8 @known_self_mul_bit_0_set(i8 noundef %x) {
+; CHECK-LABEL: @known_self_mul_bit_0_set(
+; CHECK-NEXT:    [[BIT_0_SET:%.*]] = or i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_0_SET]], [[BIT_0_SET]]
+; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 4
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %bit_0_set = or i8 %x, 1
+  %self_mul = mul i8 %bit_0_set, %bit_0_set
+  %r = and i8 %self_mul, 4
+  ret i8 %r
+}
+
+define i8 @known_self_mul_bit_0_unset(i8 noundef %x) {
+; CHECK-LABEL: @known_self_mul_bit_0_unset(
+; CHECK-NEXT:    [[BIT_0_UNSET:%.*]] = and i8 [[X:%.*]], -2
+; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_0_UNSET]], [[BIT_0_UNSET]]
+; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 8
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %bit_0_unset = and i8 %x, -2
+  %self_mul = mul i8 %bit_0_unset, %bit_0_unset
+  %r = and i8 %self_mul, 8
+  ret i8 %r
+}
+
+define i8 @known_self_mul_bit_1_set_bit_0_unset(i8 noundef %x) {
+; CHECK-LABEL: @known_self_mul_bit_1_set_bit_0_unset(
+; CHECK-NEXT:    [[LOWER_2_UNSET:%.*]] = and i8 [[X:%.*]], -4
+; CHECK-NEXT:    [[BIT_1_SET_BIT_0_UNSET:%.*]] = or disjoint i8 [[LOWER_2_UNSET]], 2
+; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_1_SET_BIT_0_UNSET]], [[BIT_1_SET_BIT_0_UNSET]]
+; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 24
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %lower_2_unset = and i8 %x, -4
+  %bit_1_set_bit_0_unset = or disjoint i8 %lower_2_unset, 2
+  %self_mul = mul i8 %bit_1_set_bit_0_unset, %bit_1_set_bit_0_unset
+  %r = and i8 %self_mul, 24
+  ret i8 %r
+}
+
+define i4 @known_self_mul_bit_1_set_bit_0_unset_i4(i4 noundef %x) {
+; CHECK-LABEL: @known_self_mul_bit_1_set_bit_0_unset_i4(
+; CHECK-NEXT:    [[LOWER_2_UNSET:%.*]] = and i4 [[X:%.*]], -4
+; CHECK-NEXT:    [[BIT_1_SET_BIT_0_UNSET:%.*]] = or disjoint i4 [[LOWER_2_UNSET]], 2
+; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i4 [[BIT_1_SET_BIT_0_UNSET]], [[BIT_1_SET_BIT_0_UNSET]]
+; CHECK-NEXT:    [[R:%.*]] = and i4 [[SELF_MUL]], -8
+; CHECK-NEXT:    ret i4 [[R]]
+;
+  %lower_2_unset = and i4 %x, -4
+  %bit_1_set_bit_0_unset = or disjoint i4 %lower_2_unset, 2
+  %self_mul = mul i4 %bit_1_set_bit_0_unset, %bit_1_set_bit_0_unset
+  %r = and i4 %self_mul, 24
+  ret i4 %r
+}
+
 define i8 @known_reduce_or(<2 x i8> %xx) {
 ; CHECK-LABEL: @known_reduce_or(
 ; CHECK-NEXT:    ret i8 1

>From 37dfb4b393979a5aab93ee2de9e102a999654175 Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Tue, 29 Jul 2025 10:45:28 -0600
Subject: [PATCH 2/6] Add additional known bits info for self multiply

---
 llvm/lib/Support/KnownBits.cpp                | 29 +++++++++++++++----
 .../test/Transforms/InstCombine/known-bits.ll | 22 +++-----------
 2 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 94a04ab90987a..a1bb6818fc94d 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -888,11 +888,30 @@ KnownBits KnownBits::mul(const KnownBits &LHS, const KnownBits &RHS,
   Res.Zero |= (~BottomKnown).getLoBits(ResultBitsKnown);
   Res.One = BottomKnown.getLoBits(ResultBitsKnown);
 
-  // If we're self-multiplying then bit[1] is guaranteed to be zero.
-  if (NoUndefSelfMultiply && BitWidth > 1) {
-    assert(Res.One[1] == 0 &&
-           "Self-multiplication failed Quadratic Reciprocity!");
-    Res.Zero.setBit(1);
+  // Self multiplying
+  if (NoUndefSelfMultiply) {
+    // bit[1] is guaranteed to be zero.
+    if (BitWidth > 1) {
+      assert(Res.One[1] == 0 &&
+             "Self-multiplication failed Quadratic Reciprocity!");
+      Res.Zero.setBit(1);
+    }
+
+    // If input bit[0] is set, then output bit[2] is guaranteed to be zero.
+    if (BitWidth > 2 && LHS.One[0])
+      Res.Zero.setBit(2);
+
+    // If input bit[0] is clear, then output bit[3] is guaranteed to be zero.
+    if (BitWidth > 3 && LHS.Zero[0])
+      Res.Zero.setBit(3);
+
+    // If input % 4 == 2, then output bit[3] and bit[4] are guarantted to be
+    // zero.
+    if (BitWidth > 3 && LHS.Zero[0] && LHS.One[1]) {
+      Res.Zero.setBit(3);
+      if (BitWidth > 4)
+        Res.Zero.setBit(4);
+    }
   }
 
   return Res;
diff --git a/llvm/test/Transforms/InstCombine/known-bits.ll b/llvm/test/Transforms/InstCombine/known-bits.ll
index 3868b5c3701c9..f8c97d86a9230 100644
--- a/llvm/test/Transforms/InstCombine/known-bits.ll
+++ b/llvm/test/Transforms/InstCombine/known-bits.ll
@@ -1246,10 +1246,7 @@ define i1 @extract_value_smul_fail(i8 %xx, i8 %yy) {
 
 define i8 @known_self_mul_bit_0_set(i8 noundef %x) {
 ; CHECK-LABEL: @known_self_mul_bit_0_set(
-; CHECK-NEXT:    [[BIT_0_SET:%.*]] = or i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_0_SET]], [[BIT_0_SET]]
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 4
-; CHECK-NEXT:    ret i8 [[R]]
+; CHECK-NEXT:    ret i8 0
 ;
   %bit_0_set = or i8 %x, 1
   %self_mul = mul i8 %bit_0_set, %bit_0_set
@@ -1259,10 +1256,7 @@ define i8 @known_self_mul_bit_0_set(i8 noundef %x) {
 
 define i8 @known_self_mul_bit_0_unset(i8 noundef %x) {
 ; CHECK-LABEL: @known_self_mul_bit_0_unset(
-; CHECK-NEXT:    [[BIT_0_UNSET:%.*]] = and i8 [[X:%.*]], -2
-; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_0_UNSET]], [[BIT_0_UNSET]]
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 8
-; CHECK-NEXT:    ret i8 [[R]]
+; CHECK-NEXT:    ret i8 0
 ;
   %bit_0_unset = and i8 %x, -2
   %self_mul = mul i8 %bit_0_unset, %bit_0_unset
@@ -1272,11 +1266,7 @@ define i8 @known_self_mul_bit_0_unset(i8 noundef %x) {
 
 define i8 @known_self_mul_bit_1_set_bit_0_unset(i8 noundef %x) {
 ; CHECK-LABEL: @known_self_mul_bit_1_set_bit_0_unset(
-; CHECK-NEXT:    [[LOWER_2_UNSET:%.*]] = and i8 [[X:%.*]], -4
-; CHECK-NEXT:    [[BIT_1_SET_BIT_0_UNSET:%.*]] = or disjoint i8 [[LOWER_2_UNSET]], 2
-; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i8 [[BIT_1_SET_BIT_0_UNSET]], [[BIT_1_SET_BIT_0_UNSET]]
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[SELF_MUL]], 24
-; CHECK-NEXT:    ret i8 [[R]]
+; CHECK-NEXT:    ret i8 0
 ;
   %lower_2_unset = and i8 %x, -4
   %bit_1_set_bit_0_unset = or disjoint i8 %lower_2_unset, 2
@@ -1287,11 +1277,7 @@ define i8 @known_self_mul_bit_1_set_bit_0_unset(i8 noundef %x) {
 
 define i4 @known_self_mul_bit_1_set_bit_0_unset_i4(i4 noundef %x) {
 ; CHECK-LABEL: @known_self_mul_bit_1_set_bit_0_unset_i4(
-; CHECK-NEXT:    [[LOWER_2_UNSET:%.*]] = and i4 [[X:%.*]], -4
-; CHECK-NEXT:    [[BIT_1_SET_BIT_0_UNSET:%.*]] = or disjoint i4 [[LOWER_2_UNSET]], 2
-; CHECK-NEXT:    [[SELF_MUL:%.*]] = mul i4 [[BIT_1_SET_BIT_0_UNSET]], [[BIT_1_SET_BIT_0_UNSET]]
-; CHECK-NEXT:    [[R:%.*]] = and i4 [[SELF_MUL]], -8
-; CHECK-NEXT:    ret i4 [[R]]
+; CHECK-NEXT:    ret i4 0
 ;
   %lower_2_unset = and i4 %x, -4
   %bit_1_set_bit_0_unset = or disjoint i4 %lower_2_unset, 2

>From 116f88acad054a1144607ace985d22fb40b03966 Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Sat, 2 Aug 2025 16:42:15 -0600
Subject: [PATCH 3/6] Generalize X * X info to all bit widths

---
 llvm/lib/Support/KnownBits.cpp | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index a1bb6818fc94d..377f5f13eb642 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -897,20 +897,18 @@ KnownBits KnownBits::mul(const KnownBits &LHS, const KnownBits &RHS,
       Res.Zero.setBit(1);
     }
 
-    // If input bit[0] is set, then output bit[2] is guaranteed to be zero.
-    if (BitWidth > 2 && LHS.One[0])
-      Res.Zero.setBit(2);
+    // If X has TZ trailing zeroes, then bit (2 * TZ + 1) must be zero.
+    unsigned TZ = (~LHS.Zero).countr_zero();
+    unsigned TwoTZP1 = 2 * TZ + 1;
+    if (TwoTZP1 < BitWidth)
+      Res.Zero.setBit(TwoTZP1);
 
-    // If input bit[0] is clear, then output bit[3] is guaranteed to be zero.
-    if (BitWidth > 3 && LHS.Zero[0])
-      Res.Zero.setBit(3);
-
-    // If input % 4 == 2, then output bit[3] and bit[4] are guarantted to be
+    // If X has exactly TZ trailing zeros, then bit (2 * TZ + 2) must also be
     // zero.
-    if (BitWidth > 3 && LHS.Zero[0] && LHS.One[1]) {
-      Res.Zero.setBit(3);
-      if (BitWidth > 4)
-        Res.Zero.setBit(4);
+    if (TZ < BitWidth && LHS.One[TZ]) {
+      unsigned TwoTZP2 = TwoTZP1 + 1;
+      if (TwoTZP2 < BitWidth)
+        Res.Zero.setBit(TwoTZP2);
     }
   }
 

>From fe3b0d42de636d248bc683c2fb986cbf009d96db Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Sun, 3 Aug 2025 11:41:29 -0600
Subject: [PATCH 4/6] replace TZ with TrailZero0

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

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 377f5f13eb642..cd8955ade2a40 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -898,14 +898,13 @@ KnownBits KnownBits::mul(const KnownBits &LHS, const KnownBits &RHS,
     }
 
     // If X has TZ trailing zeroes, then bit (2 * TZ + 1) must be zero.
-    unsigned TZ = (~LHS.Zero).countr_zero();
-    unsigned TwoTZP1 = 2 * TZ + 1;
+    unsigned TwoTZP1 = 2 * TrailZero0 + 1;
     if (TwoTZP1 < BitWidth)
       Res.Zero.setBit(TwoTZP1);
 
     // If X has exactly TZ trailing zeros, then bit (2 * TZ + 2) must also be
     // zero.
-    if (TZ < BitWidth && LHS.One[TZ]) {
+    if (TrailZero0 < BitWidth && LHS.One[TrailZero0]) {
       unsigned TwoTZP2 = TwoTZP1 + 1;
       if (TwoTZP2 < BitWidth)
         Res.Zero.setBit(TwoTZP2);

>From ec4791d824a6626a68e7057a46effd309d9338ca Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Mon, 4 Aug 2025 12:10:17 -0600
Subject: [PATCH 5/6] remove redundant setBit given new generalization

---
 llvm/lib/Support/KnownBits.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index cd8955ade2a40..1cdf120d9edd8 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -890,13 +890,6 @@ KnownBits KnownBits::mul(const KnownBits &LHS, const KnownBits &RHS,
 
   // Self multiplying
   if (NoUndefSelfMultiply) {
-    // bit[1] is guaranteed to be zero.
-    if (BitWidth > 1) {
-      assert(Res.One[1] == 0 &&
-             "Self-multiplication failed Quadratic Reciprocity!");
-      Res.Zero.setBit(1);
-    }
-
     // If X has TZ trailing zeroes, then bit (2 * TZ + 1) must be zero.
     unsigned TwoTZP1 = 2 * TrailZero0 + 1;
     if (TwoTZP1 < BitWidth)

>From 8c2754ab1ab4922e9e1cc3d2afe27ac04f71dd3f Mon Sep 17 00:00:00 2001
From: Macsen Casaus <macsen at fedora.localdomain>
Date: Fri, 8 Aug 2025 18:45:34 -0600
Subject: [PATCH 6/6] reword comments

---
 llvm/lib/Support/KnownBits.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp
index 1cdf120d9edd8..bd08365a3fcdb 100644
--- a/llvm/lib/Support/KnownBits.cpp
+++ b/llvm/lib/Support/KnownBits.cpp
@@ -888,9 +888,8 @@ KnownBits KnownBits::mul(const KnownBits &LHS, const KnownBits &RHS,
   Res.Zero |= (~BottomKnown).getLoBits(ResultBitsKnown);
   Res.One = BottomKnown.getLoBits(ResultBitsKnown);
 
-  // Self multiplying
   if (NoUndefSelfMultiply) {
-    // If X has TZ trailing zeroes, then bit (2 * TZ + 1) must be zero.
+    // If X has at least TZ trailing zeroes, then bit (2 * TZ + 1) must be zero.
     unsigned TwoTZP1 = 2 * TrailZero0 + 1;
     if (TwoTZP1 < BitWidth)
       Res.Zero.setBit(TwoTZP1);



More information about the llvm-commits mailing list