[llvm] [InstCombine] Narrow the ctpop when upper bits are known zero (PR #196537)

via llvm-commits llvm-commits at lists.llvm.org
Fri May 8 06:54:11 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Rutansh Suthar (RutanshS)

<details>
<summary>Changes</summary>

Fixes #<!-- -->196434

---
Full diff: https://github.com/llvm/llvm-project/pull/196537.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+12) 
- (modified) llvm/test/Transforms/InstCombine/ctpop.ll (+39) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index da258b3b34e27..9968d2d9371d7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -696,6 +696,18 @@ static Instruction *foldCtpop(IntrinsicInst &II, InstCombinerImpl &IC) {
   KnownBits Known(BitWidth);
   IC.computeKnownBits(Op0, Known, &II);
 
+  // If the upper half of the bits are known zero, narrow:
+  // ctpop(x) --> zext(ctpop(trunc(x)))
+  if (BitWidth >= 64) {
+    unsigned HalfWidth = BitWidth / 2;
+    if (Known.countMinLeadingZeros() >= HalfWidth) {
+      Type *NarrowTy = IntegerType::get(II.getContext(), HalfWidth);
+      Value *Trunc = IC.Builder.CreateTrunc(Op0, NarrowTy);
+      Value *NarrowPop =
+          IC.Builder.CreateUnaryIntrinsic(Intrinsic::ctpop, Trunc);
+      return CastInst::Create(Instruction::ZExt, NarrowPop, Ty);
+    }
+  }
   // If all bits are zero except for exactly one fixed bit, then the result
   // must be 0 or 1, and we can get that answer by shifting to LSB:
   // ctpop (X & 32) --> (X & 32) >> 5
diff --git a/llvm/test/Transforms/InstCombine/ctpop.ll b/llvm/test/Transforms/InstCombine/ctpop.ll
index 217a3c308a2dd..f96cd1f96eea7 100644
--- a/llvm/test/Transforms/InstCombine/ctpop.ll
+++ b/llvm/test/Transforms/InstCombine/ctpop.ll
@@ -503,3 +503,42 @@ define i32 @ctpop_non_zero_with_existing_range_attr(i32 range(i32 1, 255) %x) {
   %ctpop = call range(i32 0, 9) i32 @llvm.ctpop.i32(i32 %x)
   ret i32 %ctpop
 }
+
+define i32 @ctpop_narrow_i64_to_i32_via_assume(i64 %x) {
+; CHECK-LABEL: @ctpop_narrow_i64_to_i32_via_assume(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[X:%.*]], 4294967296
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc nuw i64 [[X]] to i32
+; CHECK-NEXT:    [[TMP2:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[TMP1]])
+; CHECK-NEXT:    ret i32 [[TMP2]]
+;
+  %cmp = icmp ult i64 %x, 4294967296
+  call void @llvm.assume(i1 %cmp)
+  %pop = call i64 @llvm.ctpop.i64(i64 %x)
+  %trunc = trunc i64 %pop to i32
+  ret i32 %trunc
+}
+
+define i64 @ctpop_no_narrow_no_assume(i64 %x) {
+; CHECK-LABEL: @ctpop_no_narrow_no_assume(
+; CHECK-NEXT:    [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
+; CHECK-NEXT:    ret i64 [[POP]]
+;
+  %pop = call i64 @llvm.ctpop.i64(i64 %x)
+  ret i64 %pop
+}
+
+define i32 @ctpop_no_narrow_not_enough_zeros(i64 %x) {
+; CHECK-LABEL: @ctpop_no_narrow_not_enough_zeros(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[X:%.*]], 8589934592
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[POP:%.*]] = call range(i64 0, 34) i64 @llvm.ctpop.i64(i64 [[X]])
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw nsw i64 [[POP]] to i32
+; CHECK-NEXT:    ret i32 [[TRUNC]]
+;
+  %cmp = icmp ult i64 %x, 8589934592
+  call void @llvm.assume(i1 %cmp)
+  %pop = call i64 @llvm.ctpop.i64(i64 %x)
+  %trunc = trunc i64 %pop to i32
+  ret i32 %trunc
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/196537


More information about the llvm-commits mailing list