[llvm] [InstCombine] Fold redundant select guards for ctpop (PR #195443)
Sai Sanjay Chikne via llvm-commits
llvm-commits at lists.llvm.org
Sun May 3 06:07:55 PDT 2026
https://github.com/sai18022001 updated https://github.com/llvm/llvm-project/pull/195443
>From 15bb21f7c3c5ebc4501ee476f60a1e68be9167b5 Mon Sep 17 00:00:00 2001
From: Sai Sanjay Chikne <saichikne180201 at gmail.com>
Date: Sat, 2 May 2026 16:18:23 +0530
Subject: [PATCH 1/4] [InstCombine] Fold redundant select guards for ctpop
Fixes: [X86][AArch64] backend didn't revert early exit for native popcnt #194207
Folds select patterns that guard ctpop for edge cases since ctpop(0)==0,
ctpop(1)==1, and ctpop(-1)==BitWidth already make the guards redundant.
---
.../InstCombine/InstCombineSelect.cpp | 20 +++
.../InstCombine/select-ctpop-fold.ll | 117 ++++++++++++++++++
2 files changed, 137 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 82954ee6f2457..af5d9e6f482b4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4317,6 +4317,26 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
if (Instruction *I = canonicalizeScalarSelectOfVecs(SI, *this))
return I;
+ // Fold: select (icmp ult X, 2), X, ctpop(X) --> ctpop(X)
+ // Fold: select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
+ // ctpop(0)==0, ctpop(1)==1, ctpop(-1)==BitWidth, so guards are redundant.
+ {
+ Value *X;
+ CmpPredicate CtpopPred;
+ Value *CtpopCmpLHS, *CtpopCmpRHS;
+ unsigned CtpopBitWidth = SelType->getScalarSizeInBits();
+ if (match(FalseVal, m_Intrinsic<Intrinsic::ctpop>(m_Value(X))) &&
+ match(CondVal, m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS)))) {
+ if (CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_ULT &&
+ match(CtpopCmpRHS, m_SpecificInt(2)) && TrueVal == X)
+ return replaceInstUsesWith(SI, FalseVal);
+ if (CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_EQ &&
+ match(CtpopCmpRHS, m_AllOnes()) &&
+ match(TrueVal, m_SpecificInt(CtpopBitWidth)))
+ return replaceInstUsesWith(SI, FalseVal);
+ }
+ }
+
// If the type of select is not an integer type or if the condition and
// the selection type are not both scalar nor both vector types, there is no
// point in attempting to match these patterns.
diff --git a/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
new file mode 100644
index 0000000000000..a2dae53a22e3f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
@@ -0,0 +1,117 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+;
+; Test folding of redundant early-exit guards around ctpop:
+; select (icmp ule X, 1), X, ctpop(X) --> ctpop(X)
+; select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
+;
+; These folds are unconditionally valid because ctpop already returns the
+; correct answer for edge cases: ctpop(0)==0, ctpop(1)==1, ctpop(-1)==BitWidth.
+; The guards only existed to skip slow software emulation -- they are never
+; needed for correctness and InstCombine removes them regardless of target.
+
+; 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: select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
+; ctpop(SIZE_MAX) == BitWidth already, so the guard is always redundant.
+;------------------------------------------------------------------------------
+
+define i64 @fold_allones_i64(i64 %x) {
+; CHECK-LABEL: @fold_allones_i64(
+; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
+; CHECK-NEXT: ret i64 [[POP]]
+;
+ %cmp = icmp eq i64 %x, -1
+ %pop = call i64 @llvm.ctpop.i64(i64 %x)
+ %res = select i1 %cmp, i64 64, i64 %pop
+ ret i64 %res
+}
+
+define i32 @fold_allones_i32(i32 %x) {
+; CHECK-LABEL: @fold_allones_i32(
+; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X:%.*]])
+; CHECK-NEXT: ret i32 [[POP]]
+;
+ %cmp = icmp eq i32 %x, -1
+ %pop = call i32 @llvm.ctpop.i32(i32 %x)
+ %res = select i1 %cmp, i32 32, i32 %pop
+ ret i32 %res
+}
+
+;------------------------------------------------------------------------------
+; Pattern 3: the full mi_popcount chain -- both guards combined.
+; select (ule X,1), X, (select (eq X,-1), BitWidth, ctpop(X))
+; Both selects should fold away leaving just ctpop(X).
+;------------------------------------------------------------------------------
+
+define i64 @fold_mi_popcount_chain(i64 %x) {
+; CHECK-LABEL: @fold_mi_popcount_chain(
+; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
+; CHECK-NEXT: ret i64 [[POP]]
+;
+ %cmp1 = icmp ule i64 %x, 1
+ %cmp2 = icmp eq i64 %x, -1
+ %pop = call i64 @llvm.ctpop.i64(i64 %x)
+ %sel2 = select i1 %cmp2, i64 64, i64 %pop
+ %res = select i1 %cmp1, i64 %x, i64 %sel2
+ ret i64 %res
+}
+
+;------------------------------------------------------------------------------
+; Pattern 4: 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
+}
+
+define <2 x i64> @fold_vector_allones(<2 x i64> %x) {
+; CHECK-LABEL: @fold_vector_allones(
+; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) <2 x i64> @llvm.ctpop.v2i64(<2 x i64> [[X:%.*]])
+; CHECK-NEXT: ret <2 x i64> [[POP]]
+;
+ %cmp = icmp eq <2 x i64> %x, <i64 -1, i64 -1>
+ %pop = call <2 x i64> @llvm.ctpop.v2i64(<2 x i64> %x)
+ %res = select <2 x i1> %cmp, <2 x i64> <i64 64, i64 64>, <2 x i64> %pop
+ ret <2 x i64> %res
+}
>From 8d846f05c3fc7aea0963a94302249680d9cc3b3e Mon Sep 17 00:00:00 2001
From: Sai Sanjay Chikne <saichikne180201 at gmail.com>
Date: Sat, 2 May 2026 17:38:37 +0530
Subject: [PATCH 2/4] clang-format fix
---
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index af5d9e6f482b4..53a4b3c30da88 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4326,7 +4326,8 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
Value *CtpopCmpLHS, *CtpopCmpRHS;
unsigned CtpopBitWidth = SelType->getScalarSizeInBits();
if (match(FalseVal, m_Intrinsic<Intrinsic::ctpop>(m_Value(X))) &&
- match(CondVal, m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS)))) {
+ match(CondVal,
+ m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS)))) {
if (CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_ULT &&
match(CtpopCmpRHS, m_SpecificInt(2)) && TrueVal == X)
return replaceInstUsesWith(SI, FalseVal);
>From 30605cdeffaf0bb920d8ff29f17df9db5bb405f0 Mon Sep 17 00:00:00 2001
From: Sai Sanjay Chikne <saichikne180201 at gmail.com>
Date: Sat, 2 May 2026 19:53:13 +0530
Subject: [PATCH 3/4] address review: drop allones fold, add
dropPoisonGeneratingAnnotations
---
.../InstCombine/InstCombineSelect.cpp | 18 ++---
.../InstCombine/select-ctpop-fold.ll | 81 +++++--------------
2 files changed, 26 insertions(+), 73 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 53a4b3c30da88..fd3664e0ba66f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4318,23 +4318,19 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
return I;
// Fold: select (icmp ult X, 2), X, ctpop(X) --> ctpop(X)
- // Fold: select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
- // ctpop(0)==0, ctpop(1)==1, ctpop(-1)==BitWidth, so guards are redundant.
+ // ctpop(0)==0 and ctpop(1)==1, so the guard is redundant.
{
Value *X;
CmpPredicate CtpopPred;
Value *CtpopCmpLHS, *CtpopCmpRHS;
- unsigned CtpopBitWidth = SelType->getScalarSizeInBits();
if (match(FalseVal, m_Intrinsic<Intrinsic::ctpop>(m_Value(X))) &&
match(CondVal,
- m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS)))) {
- if (CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_ULT &&
- match(CtpopCmpRHS, m_SpecificInt(2)) && TrueVal == X)
- return replaceInstUsesWith(SI, FalseVal);
- if (CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_EQ &&
- match(CtpopCmpRHS, m_AllOnes()) &&
- match(TrueVal, m_SpecificInt(CtpopBitWidth)))
- return replaceInstUsesWith(SI, FalseVal);
+ m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS))) &&
+ CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_ULT &&
+ match(CtpopCmpRHS, m_SpecificInt(2)) && TrueVal == X) {
+ cast<Instruction>(FalseVal)->dropPoisonGeneratingAnnotations();
+ addToWorklist(cast<Instruction>(FalseVal));
+ return replaceInstUsesWith(SI, FalseVal);
}
}
diff --git a/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
index a2dae53a22e3f..05eadc57a8678 100644
--- a/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
+++ b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
@@ -1,13 +1,10 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
;
-; Test folding of redundant early-exit guards around ctpop:
-; select (icmp ule X, 1), X, ctpop(X) --> ctpop(X)
-; select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
+; Test folding of redundant early-exit guard around ctpop:
+; select (icmp ule X, 1), X, ctpop(X) --> ctpop(X)
;
-; These folds are unconditionally valid because ctpop already returns the
-; correct answer for edge cases: ctpop(0)==0, ctpop(1)==1, ctpop(-1)==BitWidth.
-; The guards only existed to skip slow software emulation -- they are never
-; needed for correctness and InstCombine removes them regardless of target.
+; 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
@@ -44,53 +41,7 @@ define i32 @fold_ule1_i32(i32 %x) {
}
;------------------------------------------------------------------------------
-; Pattern 2: select (icmp eq X, -1), BitWidth, ctpop(X) --> ctpop(X)
-; ctpop(SIZE_MAX) == BitWidth already, so the guard is always redundant.
-;------------------------------------------------------------------------------
-
-define i64 @fold_allones_i64(i64 %x) {
-; CHECK-LABEL: @fold_allones_i64(
-; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
-; CHECK-NEXT: ret i64 [[POP]]
-;
- %cmp = icmp eq i64 %x, -1
- %pop = call i64 @llvm.ctpop.i64(i64 %x)
- %res = select i1 %cmp, i64 64, i64 %pop
- ret i64 %res
-}
-
-define i32 @fold_allones_i32(i32 %x) {
-; CHECK-LABEL: @fold_allones_i32(
-; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X:%.*]])
-; CHECK-NEXT: ret i32 [[POP]]
-;
- %cmp = icmp eq i32 %x, -1
- %pop = call i32 @llvm.ctpop.i32(i32 %x)
- %res = select i1 %cmp, i32 32, i32 %pop
- ret i32 %res
-}
-
-;------------------------------------------------------------------------------
-; Pattern 3: the full mi_popcount chain -- both guards combined.
-; select (ule X,1), X, (select (eq X,-1), BitWidth, ctpop(X))
-; Both selects should fold away leaving just ctpop(X).
-;------------------------------------------------------------------------------
-
-define i64 @fold_mi_popcount_chain(i64 %x) {
-; CHECK-LABEL: @fold_mi_popcount_chain(
-; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) i64 @llvm.ctpop.i64(i64 [[X:%.*]])
-; CHECK-NEXT: ret i64 [[POP]]
-;
- %cmp1 = icmp ule i64 %x, 1
- %cmp2 = icmp eq i64 %x, -1
- %pop = call i64 @llvm.ctpop.i64(i64 %x)
- %sel2 = select i1 %cmp2, i64 64, i64 %pop
- %res = select i1 %cmp1, i64 %x, i64 %sel2
- ret i64 %res
-}
-
-;------------------------------------------------------------------------------
-; Pattern 4: vector -- fold is unconditional.
+; Pattern 2: vector -- fold is unconditional.
; All SIMD lanes wait regardless, so the branch never saves work.
;------------------------------------------------------------------------------
@@ -105,13 +56,19 @@ define <4 x i32> @fold_vector_ule1(<4 x i32> %x) {
ret <4 x i32> %res
}
-define <2 x i64> @fold_vector_allones(<2 x i64> %x) {
-; CHECK-LABEL: @fold_vector_allones(
-; CHECK-NEXT: [[POP:%.*]] = call range(i64 0, 65) <2 x i64> @llvm.ctpop.v2i64(<2 x i64> [[X:%.*]])
-; CHECK-NEXT: ret <2 x i64> [[POP]]
+;------------------------------------------------------------------------------
+; Negative test: select (icmp eq X, -1), BitWidth, ctpop(X) should NOT be
+; folded here -- it is handled by foldSelectValueEquivalence, and folding
+; when ctpop has a range annotation that excludes BitWidth would be incorrect.
+;------------------------------------------------------------------------------
+
+define i32 @no_fold_allones_i32(i32 %x) {
+; CHECK-LABEL: @no_fold_allones_i32(
+; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X:%.*]])
+; CHECK-NEXT: ret i32 [[POP]]
;
- %cmp = icmp eq <2 x i64> %x, <i64 -1, i64 -1>
- %pop = call <2 x i64> @llvm.ctpop.v2i64(<2 x i64> %x)
- %res = select <2 x i1> %cmp, <2 x i64> <i64 64, i64 64>, <2 x i64> %pop
- ret <2 x i64> %res
+ %cmp = icmp eq i32 %x, -1
+ %pop = call i32 @llvm.ctpop.i32(i32 %x)
+ %res = select i1 %cmp, i32 32, i32 %pop
+ ret i32 %res
}
>From 776f04411894f1c136ed17bf508548da0ffcab7c Mon Sep 17 00:00:00 2001
From: Sai Sanjay Chikne <saichikne180201 at gmail.com>
Date: Sun, 3 May 2026 17:24:24 +0530
Subject: [PATCH 4/4] address review: use m_SpecificICmp, add negative tests,
drop intrinsic declares
---
.../InstCombine/InstCombineSelect.cpp | 21 +++----
.../InstCombine/select-ctpop-fold.ll | 60 ++++++++++++-------
2 files changed, 47 insertions(+), 34 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index fd3664e0ba66f..ff6b42a9a170b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4318,20 +4318,13 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
return I;
// Fold: select (icmp ult X, 2), X, ctpop(X) --> ctpop(X)
- // ctpop(0)==0 and ctpop(1)==1, so the guard is redundant.
- {
- Value *X;
- CmpPredicate CtpopPred;
- Value *CtpopCmpLHS, *CtpopCmpRHS;
- if (match(FalseVal, m_Intrinsic<Intrinsic::ctpop>(m_Value(X))) &&
- match(CondVal,
- m_ICmp(CtpopPred, m_Value(CtpopCmpLHS), m_Value(CtpopCmpRHS))) &&
- CtpopCmpLHS == X && CtpopPred == ICmpInst::ICMP_ULT &&
- match(CtpopCmpRHS, m_SpecificInt(2)) && TrueVal == X) {
- cast<Instruction>(FalseVal)->dropPoisonGeneratingAnnotations();
- addToWorklist(cast<Instruction>(FalseVal));
- return replaceInstUsesWith(SI, FalseVal);
- }
+ // ctpop(0)==0 and ctpop(1)==1, so the guard is always redundant.
+ if (match(FalseVal, m_Intrinsic<Intrinsic::ctpop>(m_Specific(TrueVal))) &&
+ match(CondVal,
+ m_SpecificICmp(ICmpInst::ICMP_ULT, m_Specific(TrueVal), m_SpecificInt(2)))) {
+ cast<Instruction>(FalseVal)->dropPoisonGeneratingAnnotations();
+ addToWorklist(cast<Instruction>(FalseVal));
+ return replaceInstUsesWith(SI, FalseVal);
}
// If the type of select is not an integer type or if the condition and
diff --git a/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
index 05eadc57a8678..d42fd3605264f 100644
--- a/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
+++ b/llvm/test/Transforms/InstCombine/select-ctpop-fold.ll
@@ -6,16 +6,12 @@
; 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.
+; Positive tests: select (icmp ule X, 1), X, ctpop(X) --> ctpop(X)
;------------------------------------------------------------------------------
define i64 @fold_ule1_i64(i64 %x) {
@@ -40,35 +36,59 @@ define i32 @fold_ule1_i32(i32 %x) {
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>
+ %cmp = icmp ule <4 x i32> %x, splat(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
-; folded here -- it is handled by foldSelectValueEquivalence, and folding
-; when ctpop has a range annotation that excludes BitWidth would be incorrect.
+; Negative tests: should NOT fold.
;------------------------------------------------------------------------------
-define i32 @no_fold_allones_i32(i32 %x) {
-; CHECK-LABEL: @no_fold_allones_i32(
+; Wrong predicate (ugt instead of ult/ule)
+define i32 @no_fold_wrong_pred(i32 %x) {
+; CHECK-LABEL: @no_fold_wrong_pred(
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X:%.*]], 1
+; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[X]], i32 [[POP]]
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %cmp = icmp ugt i32 %x, 1
+ %pop = call i32 @llvm.ctpop.i32(i32 %x)
+ %res = select i1 %cmp, i32 %x, i32 %pop
+ ret i32 %res
+}
+
+; Wrong constant (ult 3 instead of ult 2)
+define i32 @no_fold_wrong_const(i32 %x) {
+; CHECK-LABEL: @no_fold_wrong_const(
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X:%.*]], 3
+; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[X]], i32 [[POP]]
+; CHECK-NEXT: ret i32 [[RES]]
+;
+ %cmp = icmp ult i32 %x, 3
+ %pop = call i32 @llvm.ctpop.i32(i32 %x)
+ %res = select i1 %cmp, i32 %x, i32 %pop
+ ret i32 %res
+}
+
+; Mismatched variables (condition uses Y, ctpop uses X)
+define i32 @no_fold_mismatch_var(i32 %x, i32 %y) {
+; CHECK-LABEL: @no_fold_mismatch_var(
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[Y:%.*]], 2
; CHECK-NEXT: [[POP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X:%.*]])
-; CHECK-NEXT: ret i32 [[POP]]
+; CHECK-NEXT: [[RES:%.*]] = select i1 [[CMP]], i32 [[Y]], i32 [[POP]]
+; CHECK-NEXT: ret i32 [[RES]]
;
- %cmp = icmp eq i32 %x, -1
+ %cmp = icmp ult i32 %y, 2
%pop = call i32 @llvm.ctpop.i32(i32 %x)
- %res = select i1 %cmp, i32 32, i32 %pop
+ %res = select i1 %cmp, i32 %y, i32 %pop
ret i32 %res
}
More information about the llvm-commits
mailing list