[llvm-branch-commits] [llvm] [InstCombine] Fold trunc(ctpop(A)) + trunc(ctpop(B)) -> trunc(ctpop(A) + ctpop(B)) (PR #231332)

Luke Lau via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 12 16:57:35 PDT 2026


https://github.com/lukel97 created https://github.com/llvm/llvm-project/pull/231332

This allows the combine added in #231321 to kick in and fold the motivating ctpop case in automotive-bitcount:

```c
static char bits[256] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};

int CDECL ntbl_bitcount(long int x)
{
      return
            bits[ (int) (x & 0x0000000FUL)       ] +
            bits[ (int)((x & 0x000000F0UL) >> 4) ] +
            bits[ (int)((x & 0x00000F00UL) >> 8) ] +
            bits[ (int)((x & 0x0000F000UL) >> 12)] +
            bits[ (int)((x & 0x000F0000UL) >> 16)] +
            bits[ (int)((x & 0x00F00000UL) >> 20)] +
            bits[ (int)((x & 0x0F000000UL) >> 24)] +
            bits[ (int)((x & 0xF0000000UL) >> 28)];
}
```




>From c8581528a14aa4c8963d37d038a48378ae3d3270 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 13 Oct 2026 00:46:04 +0100
Subject: [PATCH 1/2] Precommit tests

---
 llvm/test/Transforms/InstCombine/ctpop.ll | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/ctpop.ll b/llvm/test/Transforms/InstCombine/ctpop.ll
index 5bb8dafaf37c4..30c7ca40534e1 100644
--- a/llvm/test/Transforms/InstCombine/ctpop.ll
+++ b/llvm/test/Transforms/InstCombine/ctpop.ll
@@ -263,6 +263,23 @@ define <2 x i32> @ctpop_add_no_common_bits_vec_use2(<2 x i32> %a, <2 x i32> %b,
   ret <2 x i32> %res
 }
 
+define i32 @ctpop_add_trunc(i64 %a, i64 %b) {
+; CHECK-LABEL: @ctpop_add_trunc(
+; CHECK-NEXT:    [[CTPOP1:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[A:%.*]])
+; CHECK-NEXT:    [[T1:%.*]] = trunc nuw nsw i64 [[CTPOP1]] to i32
+; CHECK-NEXT:    [[CTPOP2:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[B:%.*]])
+; CHECK-NEXT:    [[T2:%.*]] = trunc nuw nsw i64 [[CTPOP2]] to i32
+; CHECK-NEXT:    [[RES:%.*]] = add nuw nsw i32 [[T1]], [[T2]]
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %ctpop1 = call i64 @llvm.ctpop.i64(i64 %a)
+  %t1 = trunc i64 %ctpop1 to i32
+  %ctpop2 = call i64 @llvm.ctpop.i64(i64 %b)
+  %t2 = trunc i64 %ctpop2 to i32
+  %res = add i32 %t1, %t2
+  ret i32 %res
+}
+
 define i8 @ctpop_rotate_left(i8 %a, i8 %amt)  {
 ; CHECK-LABEL: @ctpop_rotate_left(
 ; CHECK-NEXT:    [[RES:%.*]] = call range(i8 0, 9) i8 @llvm.ctpop.i8(i8 [[A:%.*]])

>From 7466db2e5c13bfafe27692db5e4a8c31aee6bf16 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 13 Oct 2026 00:17:04 +0100
Subject: [PATCH 2/2] [InstCombine] Fold trunc(ctpop(A)) + trunc(ctpop(B)) ->
 trunc(ctpop(A) + ctpop(B))

This allows the combine added in #231321 to kick in and fold the motivating ctpop case in automotive-bitcount:

```c
static char bits[256] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};

int CDECL ntbl_bitcount(long int x)
{
      return
            bits[ (int) (x & 0x0000000FUL)       ] +
            bits[ (int)((x & 0x000000F0UL) >> 4) ] +
            bits[ (int)((x & 0x00000F00UL) >> 8) ] +
            bits[ (int)((x & 0x0000F000UL) >> 12)] +
            bits[ (int)((x & 0x000F0000UL) >> 16)] +
            bits[ (int)((x & 0x00F00000UL) >> 20)] +
            bits[ (int)((x & 0x0F000000UL) >> 24)] +
            bits[ (int)((x & 0xF0000000UL) >> 28)];
}
```
---
 .../InstCombine/InstCombineAddSub.cpp          |  6 ++++++
 llvm/test/Transforms/InstCombine/ctpop.ll      | 18 +++++-------------
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 9ff6991388433..72e6fb80beb10 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2046,6 +2046,12 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
         Builder.CreateIntrinsic(Intrinsic::umax, {I.getType()}, {A, B}));
   }
 
+  // trunc(ctpop(A)) + trunc(ctpop(B)) => trunc(ctpop(A) + ctpop(B))
+  if (match(LHS, m_OneUse(m_Trunc(m_Value(A, m_Ctpop(m_Value()))))) &&
+      match(RHS, m_OneUse(m_Trunc(m_Value(B, m_Ctpop(m_Value()))))) &&
+      A->getType() == B->getType())
+    return new TruncInst(Builder.CreateAdd(A, B), I.getType());
+
   // ctpop(A) + ctpop(B) => ctpop(A | B) if A and B have no bits set in common.
   if (match(LHS, m_OneUse(m_Ctpop(m_Value(A)))) &&
       match(RHS, m_OneUse(m_Ctpop(m_Value(B)))) &&
diff --git a/llvm/test/Transforms/InstCombine/ctpop.ll b/llvm/test/Transforms/InstCombine/ctpop.ll
index 30c7ca40534e1..1886f0bbef0a9 100644
--- a/llvm/test/Transforms/InstCombine/ctpop.ll
+++ b/llvm/test/Transforms/InstCombine/ctpop.ll
@@ -266,10 +266,9 @@ define <2 x i32> @ctpop_add_no_common_bits_vec_use2(<2 x i32> %a, <2 x i32> %b,
 define i32 @ctpop_add_trunc(i64 %a, i64 %b) {
 ; CHECK-LABEL: @ctpop_add_trunc(
 ; CHECK-NEXT:    [[CTPOP1:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[A:%.*]])
-; CHECK-NEXT:    [[T1:%.*]] = trunc nuw nsw i64 [[CTPOP1]] to i32
 ; CHECK-NEXT:    [[CTPOP2:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[B:%.*]])
-; CHECK-NEXT:    [[T2:%.*]] = trunc nuw nsw i64 [[CTPOP2]] to i32
-; CHECK-NEXT:    [[RES:%.*]] = add nuw nsw i32 [[T1]], [[T2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add nuw nsw i64 [[CTPOP1]], [[CTPOP2]]
+; CHECK-NEXT:    [[RES:%.*]] = trunc nuw nsw i64 [[TMP1]] to i32
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %ctpop1 = call i64 @llvm.ctpop.i64(i64 %a)
@@ -536,19 +535,12 @@ define i32 @ctpop_add_no_common_bits_shifted(i32 %x) {
   ret i32 %r
 }
 
-; TODO: We can combine these two ctpops through the truncs
-
 define i32 @ctpop_add_no_common_bits_shifted_trunc(i64 %x) {
 ; CHECK-LABEL: @ctpop_add_no_common_bits_shifted_trunc(
-; CHECK-NEXT:    [[LO:%.*]] = and i64 [[X:%.*]], 15
-; CHECK-NEXT:    [[C0:%.*]] = call range(i64 0, 5) i64 @llvm.ctpop.i64(i64 [[LO]])
-; CHECK-NEXT:    [[T0:%.*]] = trunc nuw nsw i64 [[C0]] to i32
-; CHECK-NEXT:    [[S:%.*]] = lshr i64 [[X]], 8
-; CHECK-NEXT:    [[HI:%.*]] = and i64 [[S]], 15
-; CHECK-NEXT:    [[C1:%.*]] = call range(i64 0, 5) i64 @llvm.ctpop.i64(i64 [[HI]])
+; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[X:%.*]], 3855
+; CHECK-NEXT:    [[C1:%.*]] = call range(i64 0, 9) i64 @llvm.ctpop.i64(i64 [[TMP1]])
 ; CHECK-NEXT:    [[T1:%.*]] = trunc nuw nsw i64 [[C1]] to i32
-; CHECK-NEXT:    [[R:%.*]] = add nuw nsw i32 [[T0]], [[T1]]
-; CHECK-NEXT:    ret i32 [[R]]
+; CHECK-NEXT:    ret i32 [[T1]]
 ;
   %lo = and i64 %x, 15
   %c0 = call i64 @llvm.ctpop.i64(i64 %lo)



More information about the llvm-branch-commits mailing list