[llvm] [InstCombine] Fold redundant select guards for ctpop (PR #195443)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun May 3 02:20:17 PDT 2026


================
@@ -0,0 +1,74 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; Test folding of redundant early-exit guard around ctpop:
+;   select (icmp ule X, 1), X, ctpop(X)  --> ctpop(X)
+;
+; This fold is valid because ctpop(0)==0 and ctpop(1)==1, so the guard
+; is always redundant. The guard only existed to skip slow software emulation.
+
+; RUN: opt < %s -S -passes=instcombine | FileCheck %s
+
+declare i64 @llvm.ctpop.i64(i64)
+declare i32 @llvm.ctpop.i32(i32)
+declare <4 x i32> @llvm.ctpop.v4i32(<4 x i32>)
+declare <2 x i64> @llvm.ctpop.v2i64(<2 x i64>)
+
+;------------------------------------------------------------------------------
+; Pattern 1: select (icmp ule X, 1), X, ctpop(X)  -->  ctpop(X)
+; ctpop(0)==0 and ctpop(1)==1, so the guard is always redundant.
+;------------------------------------------------------------------------------
+
+define i64 @fold_ule1_i64(i64 %x) {
+; CHECK-LABEL: @fold_ule1_i64(
+; CHECK-NEXT:    [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
+; CHECK-NEXT:    ret i64 [[POP]]
+;
+  %cmp = icmp ule i64 %x, 1
+  %pop = call i64 @llvm.ctpop.i64(i64 %x)
+  %res = select i1 %cmp, i64 %x, i64 %pop
+  ret i64 %res
+}
+
+define i32 @fold_ule1_i32(i32 %x) {
+; CHECK-LABEL: @fold_ule1_i32(
+; CHECK-NEXT:    [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X:%.*]])
+; CHECK-NEXT:    ret i32 [[POP]]
+;
+  %cmp = icmp ule i32 %x, 1
+  %pop = call i32 @llvm.ctpop.i32(i32 %x)
+  %res = select i1 %cmp, i32 %x, i32 %pop
+  ret i32 %res
+}
+
+;------------------------------------------------------------------------------
+; Pattern 2: vector -- fold is unconditional.
+; All SIMD lanes wait regardless, so the branch never saves work.
+;------------------------------------------------------------------------------
+
+define <4 x i32> @fold_vector_ule1(<4 x i32> %x) {
+; CHECK-LABEL: @fold_vector_ule1(
+; CHECK-NEXT:    [[POP:%.*]] = call range(i32 0, 33) <4 x i32> @llvm.ctpop.v4i32(<4 x i32> [[X:%.*]])
+; CHECK-NEXT:    ret <4 x i32> [[POP]]
+;
+  %cmp = icmp ule <4 x i32> %x, <i32 1, i32 1, i32 1, i32 1>
+  %pop = call <4 x i32> @llvm.ctpop.v4i32(<4 x i32> %x)
+  %res = select <4 x i1> %cmp, <4 x i32> %x, <4 x i32> %pop
+  ret <4 x i32> %res
+}
+
+;------------------------------------------------------------------------------
+; Negative test: select (icmp eq X, -1), BitWidth, ctpop(X) should NOT be
----------------
dtcxzyw wrote:

It is not a negative test. The original pattern is still folded into the expected one. Add some negative tests with mismatched predicates/constants/variables.


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


More information about the llvm-commits mailing list