[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