[llvm] [Instcombine]: Folds`llvm.ucmp` and `llvm.scmp` (PR #168505)
Kevin Per via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 29 08:42:26 PST 2025
https://github.com/kper updated https://github.com/llvm/llvm-project/pull/168505
>From 899d75305387bc12c3e56540ef5a391b10f77e05 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Tue, 18 Nov 2025 08:59:27 +0100
Subject: [PATCH 01/11] [InstCombine]: Fold select into llvm.ucmp or llvm.scmp
---
.../InstCombine/InstCombineSelect.cpp | 40 +++++++++
.../test/Transforms/InstCombine/select-cmp.ll | 90 +++++++++++++++++++
2 files changed, 130 insertions(+)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 9572f9d702e1b..a6be936f4b9e0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1955,6 +1955,43 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
return nullptr;
}
+/// Transform
+///
+/// select(icmp(eq, X, Y), Z, select(icmp(ult, X, Y), -1, 1))
+/// into select(icmp(eq, X, Y), Z, llvm.ucmp(freeze(X), freeze(Y)))
+///
+/// or
+///
+/// select(icmp(eq, X, Y), Z, select(icmp(slt, X, Y), -1, 1))
+/// into select(icmp(eq, X, Y), Z, llvm.scmp(freeze(X), freeze(Y)))
+static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
+ Value *TrueVal, Value *FalseVal,
+ InstCombiner::BuilderTy &Builder) {
+ ICmpInst::Predicate Pred = ICI->getPredicate();
+
+ if (Pred != ICmpInst::ICMP_EQ)
+ return nullptr;
+
+ CmpPredicate IPred;
+ if (match(FalseVal, m_Select(m_ICmp(IPred, m_Specific(ICI->getOperand(0)),
+ m_Specific(ICI->getOperand(1))),
+ m_AllOnes(), m_One())) &&
+ (IPred == ICmpInst::ICMP_ULT || IPred == ICmpInst::ICMP_SLT)) {
+ Value *X = ICI->getOperand(0);
+ Value *Y = ICI->getOperand(1);
+ Builder.SetInsertPoint(&SI);
+ Value *FrozenX = Builder.CreateFreeze(X, X->getName() + ".frz");
+ Value *FrozenY = Builder.CreateFreeze(Y, Y->getName() + ".frz");
+ Value *Cmp = Builder.CreateIntrinsic(
+ FrozenX->getType(),
+ IPred == ICmpInst::ICMP_ULT ? Intrinsic::ucmp : Intrinsic::scmp,
+ {FrozenX, FrozenY});
+ return Builder.CreateSelect(SI.getCondition(), TrueVal, Cmp, "select.ucmp");
+ }
+
+ return nullptr;
+}
+
/// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`.
/// This allows for better canonicalization.
Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp,
@@ -2186,6 +2223,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal))
return replaceInstUsesWith(SI, V);
+ if (Value *V = foldSelectToInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
+ return replaceInstUsesWith(SI, V);
+
return Changed ? &SI : nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index b1bd7a0ecc8ac..03d0a364a6289 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -808,5 +808,95 @@ define i1 @icmp_lt_slt(i1 %c, i32 %arg) {
ret i1 %select
}
+define i16 @icmp_fold_to_llvm_ucmp_when_eq(i16 %x, i16 %y) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_eq(
+; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
+; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
+; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 42, i16 [[TMP2]]
+; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 42, i16 %5
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_when_cmp_slt(i16 %x, i16 %y) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_cmp_slt(
+; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
+; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
+; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.scmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 42, i16 [[TMP2]]
+; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp slt i16 %x, %y ; here "ult" changed to "slt"
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 42, i16 %5
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_when_value(i16 %x, i16 %y, i16 %Z) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_value(
+; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
+; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
+; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 [[TMP2]]
+; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 %Z, i16 %5
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_when_ne(i16 %x, i16 %y) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_ne(
+; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
+; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
+; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i16 42, i16 [[TMP1]]
+; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
+;
+ %3 = icmp ne i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 %5, i16 42
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(i16 %x, i16 %y, i16 %Z) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 1
+; CHECK-NEXT: ret i16 [[TMP2]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 1, i16 1 ; invalid constant
+ %6 = select i1 %3, i16 %Z, i16 %5
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(i16 %x, i16 %y, i16 %Z) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(
+; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 -1
+; CHECK-NEXT: ret i16 [[TMP2]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 -1 ; invalid constant
+ %6 = select i1 %3, i16 %Z, i16 %5
+ ret i16 %6
+}
+
declare void @use(i1)
declare void @use.i8(i8)
>From ae6454509a4c097ebe9beef1ff27a4b155736423 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Tue, 18 Nov 2025 09:17:27 +0100
Subject: [PATCH 02/11] [InstCombine]: Handle edge case when Z is zero
---
.../InstCombine/InstCombineSelect.cpp | 13 ++++++----
.../test/Transforms/InstCombine/select-cmp.ll | 24 +++++++++++++++++++
2 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index a6be936f4b9e0..70504f8f3d45b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1980,12 +1980,17 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
Value *X = ICI->getOperand(0);
Value *Y = ICI->getOperand(1);
Builder.SetInsertPoint(&SI);
+ auto IID = IPred == ICmpInst::ICMP_ULT ? Intrinsic::ucmp : Intrinsic::scmp;
+
+ // Edge Case: if Z is the constant 0 then the select can be folded
+ // to just the instrinsic comparison.
+ if (match(TrueVal, m_Zero()))
+ return Builder.CreateIntrinsic(X->getType(), IID, {X, Y});
+
Value *FrozenX = Builder.CreateFreeze(X, X->getName() + ".frz");
Value *FrozenY = Builder.CreateFreeze(Y, Y->getName() + ".frz");
- Value *Cmp = Builder.CreateIntrinsic(
- FrozenX->getType(),
- IPred == ICmpInst::ICMP_ULT ? Intrinsic::ucmp : Intrinsic::scmp,
- {FrozenX, FrozenY});
+ Value *Cmp =
+ Builder.CreateIntrinsic(FrozenX->getType(), IID, {FrozenX, FrozenY});
return Builder.CreateSelect(SI.getCondition(), TrueVal, Cmp, "select.ucmp");
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 03d0a364a6289..bf1a6cb047c37 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -824,6 +824,30 @@ define i16 @icmp_fold_to_llvm_ucmp_when_eq(i16 %x, i16 %y) {
ret i16 %6
}
+define i16 @icmp_fold_to_llvm_ucmp_when_ult_and_Z_zero(i16 %x, i16 %y) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_ult_and_Z_zero(
+; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X:%.*]], i16 [[Y:%.*]])
+; CHECK-NEXT: ret i16 [[TMP1]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp ult i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 0, i16 %5
+ ret i16 %6
+}
+
+define i16 @icmp_fold_to_llvm_ucmp_when_slt_and_Z_zero(i16 %x, i16 %y) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_slt_and_Z_zero(
+; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.scmp.i16.i16(i16 [[X:%.*]], i16 [[Y:%.*]])
+; CHECK-NEXT: ret i16 [[TMP1]]
+;
+ %3 = icmp eq i16 %x, %y
+ %4 = icmp slt i16 %x, %y
+ %5 = select i1 %4, i16 -1, i16 1
+ %6 = select i1 %3, i16 0, i16 %5
+ ret i16 %6
+}
+
define i16 @icmp_fold_to_llvm_ucmp_when_cmp_slt(i16 %x, i16 %y) {
; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_cmp_slt(
; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
>From 0347b824053c3648f2cf9fae7564948d092f86ed Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Tue, 18 Nov 2025 10:15:48 +0100
Subject: [PATCH 03/11] [InstCombine]: Fixed condition
---
llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 70504f8f3d45b..5c8008700e181 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1985,7 +1985,7 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
// Edge Case: if Z is the constant 0 then the select can be folded
// to just the instrinsic comparison.
if (match(TrueVal, m_Zero()))
- return Builder.CreateIntrinsic(X->getType(), IID, {X, Y});
+ return Builder.CreateIntrinsic(SI.getType(), IID, {X, Y});
Value *FrozenX = Builder.CreateFreeze(X, X->getName() + ".frz");
Value *FrozenY = Builder.CreateFreeze(Y, Y->getName() + ".frz");
>From fedb38834b993005b159b7090499ac8251164555 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Wed, 19 Nov 2025 20:52:22 +0100
Subject: [PATCH 04/11] [InstCombine]: Fixed type
---
.../Transforms/InstCombine/InstCombineSelect.cpp | 2 +-
llvm/test/Transforms/InstCombine/select-cmp.ll | 16 ++++++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 5c8008700e181..04f02190ca929 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1990,7 +1990,7 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
Value *FrozenX = Builder.CreateFreeze(X, X->getName() + ".frz");
Value *FrozenY = Builder.CreateFreeze(Y, Y->getName() + ".frz");
Value *Cmp =
- Builder.CreateIntrinsic(FrozenX->getType(), IID, {FrozenX, FrozenY});
+ Builder.CreateIntrinsic(FalseVal->getType(), IID, {FrozenX, FrozenY});
return Builder.CreateSelect(SI.getCondition(), TrueVal, Cmp, "select.ucmp");
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index bf1a6cb047c37..4b3473fbbc260 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -922,5 +922,21 @@ define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(i16 %x, i16
ret i16 %6
}
+define i32 @icmp_fold_to_llvm_ucmp_mixed_types(i16 %0, i16 %1) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_mixed_types(
+; CHECK-NEXT: [[DOTFRZ1:%.*]] = freeze i16 [[TMP1:%.*]]
+; CHECK-NEXT: [[DOTFRZ:%.*]] = freeze i16 [[TMP0:%.*]]
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[DOTFRZ]], [[DOTFRZ1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.ucmp.i32.i16(i16 [[DOTFRZ]], i16 [[DOTFRZ1]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i32 1, i32 [[TMP3]]
+; CHECK-NEXT: ret i32 [[SELECT_UCMP]]
+;
+ %.not = icmp eq i16 %0, %1
+ %3 = icmp ult i16 %0, %1
+ %4 = select i1 %3, i32 -1, i32 1
+ %.1 = select i1 %.not, i32 1, i32 %4
+ ret i32 %.1
+}
+
declare void @use(i1)
declare void @use.i8(i8)
>From 239dcbddf24efe213e24f9ece66b52c08796ea2d Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Thu, 20 Nov 2025 08:07:30 +0100
Subject: [PATCH 05/11] [InstCombine]: Don't apply fold when ptr
---
.../InstCombine/InstCombineSelect.cpp | 6 +++
.../test/Transforms/InstCombine/select-cmp.ll | 47 +++++++++++++------
2 files changed, 39 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 04f02190ca929..5d834cc8f2bce 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1979,6 +1979,12 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
(IPred == ICmpInst::ICMP_ULT || IPred == ICmpInst::ICMP_SLT)) {
Value *X = ICI->getOperand(0);
Value *Y = ICI->getOperand(1);
+
+ // icmp(ult, ptr %X, ptr %Y) -> cannot be folded because
+ // there is no intrinsic for a pointer comparison.
+ if (!X->getType()->isIntegerTy() || !Y->getType()->isIntegerTy())
+ return nullptr;
+
Builder.SetInsertPoint(&SI);
auto IID = IPred == ICmpInst::ICMP_ULT ? Intrinsic::ucmp : Intrinsic::scmp;
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 4b3473fbbc260..0cb8d088590a9 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -896,6 +896,22 @@ define i16 @icmp_fold_to_llvm_ucmp_when_ne(i16 %x, i16 %y) {
ret i16 %6
}
+define i32 @icmp_fold_to_llvm_ucmp_mixed_types(i16 %0, i16 %1) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_mixed_types(
+; CHECK-NEXT: [[DOTFRZ1:%.*]] = freeze i16 [[TMP1:%.*]]
+; CHECK-NEXT: [[DOTFRZ:%.*]] = freeze i16 [[TMP0:%.*]]
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[DOTFRZ]], [[DOTFRZ1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.ucmp.i32.i16(i16 [[DOTFRZ]], i16 [[DOTFRZ1]])
+; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i32 1, i32 [[TMP3]]
+; CHECK-NEXT: ret i32 [[SELECT_UCMP]]
+;
+ %.not = icmp eq i16 %0, %1
+ %3 = icmp ult i16 %0, %1
+ %4 = select i1 %3, i32 -1, i32 1
+ %.1 = select i1 %.not, i32 1, i32 %4
+ ret i32 %.1
+}
+
define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(i16 %x, i16 %y, i16 %Z) {
; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
@@ -922,20 +938,23 @@ define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(i16 %x, i16
ret i16 %6
}
-define i32 @icmp_fold_to_llvm_ucmp_mixed_types(i16 %0, i16 %1) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_mixed_types(
-; CHECK-NEXT: [[DOTFRZ1:%.*]] = freeze i16 [[TMP1:%.*]]
-; CHECK-NEXT: [[DOTFRZ:%.*]] = freeze i16 [[TMP0:%.*]]
-; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[DOTFRZ]], [[DOTFRZ1]]
-; CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.ucmp.i32.i16(i16 [[DOTFRZ]], i16 [[DOTFRZ1]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i32 1, i32 [[TMP3]]
-; CHECK-NEXT: ret i32 [[SELECT_UCMP]]
-;
- %.not = icmp eq i16 %0, %1
- %3 = icmp ult i16 %0, %1
- %4 = select i1 %3, i32 -1, i32 1
- %.1 = select i1 %.not, i32 1, i32 %4
- ret i32 %.1
+define i8 @icmp_fold_to_llvm_ucmp_negative_test_ptr(ptr %0, ptr %1) {
+; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_ptr(
+; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
+; CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP1:%.*]], align 8
+; CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP3]], [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[TMP5]], i8 -1, i8 1
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq ptr [[TMP3]], [[TMP4]]
+; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TMP7]], i8 0, i8 [[TMP6]]
+; CHECK-NEXT: ret i8 [[TMP8]]
+;
+ %3 = load ptr, ptr %0, align 8
+ %4 = load ptr, ptr %1, align 8
+ %5 = icmp ult ptr %3, %4
+ %6 = select i1 %5, i8 -1, i8 1
+ %7 = icmp eq ptr %3, %4
+ %8 = select i1 %7, i8 0, i8 %6
+ ret i8 %8
}
declare void @use(i1)
>From aaac1a1311b8dc2b7b1165d3955436a8fe64aea1 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Thu, 20 Nov 2025 20:10:43 +0100
Subject: [PATCH 06/11] [InstCombine]: select(icmp(eq, X, Y), 0, llvm.cmp(X,
Y)) -> llvm.cmp(X, Y)
---
.../InstCombine/InstCombineSelect.cpp | 54 ++++++++++++++-----
.../test/Transforms/InstCombine/select-cmp.ll | 22 ++++++++
2 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 5d834cc8f2bce..6794e9c18e79c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1955,15 +1955,17 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
return nullptr;
}
-/// Transform
-///
-/// select(icmp(eq, X, Y), Z, select(icmp(ult, X, Y), -1, 1))
-/// into select(icmp(eq, X, Y), Z, llvm.ucmp(freeze(X), freeze(Y)))
-///
-/// or
-///
-/// select(icmp(eq, X, Y), Z, select(icmp(slt, X, Y), -1, 1))
-/// into select(icmp(eq, X, Y), Z, llvm.scmp(freeze(X), freeze(Y)))
+// Transform
+//
+// select(icmp(eq, X, Y), Z, select(icmp(ult, X, Y), -1, 1))
+// ->
+// select(icmp(eq, X, Y), Z, llvm.ucmp(freeze(X), freeze(Y)))
+//
+// or
+//
+// select(icmp(eq, X, Y), Z, select(icmp(slt, X, Y), -1, 1))
+// ->
+// select(icmp(eq, X, Y), Z, llvm.scmp(freeze(X), freeze(Y)))
static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
Value *TrueVal, Value *FalseVal,
InstCombiner::BuilderTy &Builder) {
@@ -1973,12 +1975,11 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
return nullptr;
CmpPredicate IPred;
- if (match(FalseVal, m_Select(m_ICmp(IPred, m_Specific(ICI->getOperand(0)),
- m_Specific(ICI->getOperand(1))),
+ Value *X = ICI->getOperand(0);
+ Value *Y = ICI->getOperand(1);
+ if (match(FalseVal, m_Select(m_ICmp(IPred, m_Specific(X), m_Specific(Y)),
m_AllOnes(), m_One())) &&
(IPred == ICmpInst::ICMP_ULT || IPred == ICmpInst::ICMP_SLT)) {
- Value *X = ICI->getOperand(0);
- Value *Y = ICI->getOperand(1);
// icmp(ult, ptr %X, ptr %Y) -> cannot be folded because
// there is no intrinsic for a pointer comparison.
@@ -2003,6 +2004,30 @@ static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
return nullptr;
}
+// Transform
+// select(icmp(eq, X, Y), 0, llvm.cmp(X, Y))
+// ->
+// llvm.cmp(X, Y)
+static Value *foldInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
+ Value *TrueVal, Value *FalseVal,
+ InstCombiner::BuilderTy &Builder) {
+ ICmpInst::Predicate Pred = ICI->getPredicate();
+
+ if (Pred != ICmpInst::ICMP_EQ)
+ return nullptr;
+
+ Value *X = ICI->getOperand(0);
+ Value *Y = ICI->getOperand(1);
+
+ auto ucmp = m_Intrinsic<Intrinsic::ucmp>(m_Specific(X), m_Specific(Y));
+ auto scmp = m_Intrinsic<Intrinsic::scmp>(m_Specific(X), m_Specific(Y));
+ if (match(SI.getTrueValue(), m_Zero()) &&
+ (match(SI.getFalseValue(), ucmp) || match(SI.getFalseValue(), scmp)))
+ return SI.getFalseValue();
+
+ return nullptr;
+}
+
/// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`.
/// This allows for better canonicalization.
Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp,
@@ -2237,6 +2262,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
if (Value *V = foldSelectToInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
return replaceInstUsesWith(SI, V);
+ if (Value *V = foldInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
+ return replaceInstUsesWith(SI, V);
+
return Changed ? &SI : nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 0cb8d088590a9..37dc84fe33111 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -957,5 +957,27 @@ define i8 @icmp_fold_to_llvm_ucmp_negative_test_ptr(ptr %0, ptr %1) {
ret i8 %8
}
+define i32 @fold_ucmp(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_ucmp(
+; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call i32 @llvm.ucmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
+define i32 @fold_scmp(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_scmp(
+; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.scmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call i32 @llvm.scmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
declare void @use(i1)
declare void @use.i8(i8)
>From a2c5cb496d4e8952779ec32c8c742d8cd1081eb1 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Sun, 23 Nov 2025 14:17:32 +0100
Subject: [PATCH 07/11] [InstCombine]: Removed obsolete fold
---
.../InstCombine/InstCombineSelect.cpp | 52 ------
.../test/Transforms/InstCombine/select-cmp.ll | 175 +++---------------
2 files changed, 26 insertions(+), 201 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 6794e9c18e79c..331c0c1389084 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1955,55 +1955,6 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
return nullptr;
}
-// Transform
-//
-// select(icmp(eq, X, Y), Z, select(icmp(ult, X, Y), -1, 1))
-// ->
-// select(icmp(eq, X, Y), Z, llvm.ucmp(freeze(X), freeze(Y)))
-//
-// or
-//
-// select(icmp(eq, X, Y), Z, select(icmp(slt, X, Y), -1, 1))
-// ->
-// select(icmp(eq, X, Y), Z, llvm.scmp(freeze(X), freeze(Y)))
-static Value *foldSelectToInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
- Value *TrueVal, Value *FalseVal,
- InstCombiner::BuilderTy &Builder) {
- ICmpInst::Predicate Pred = ICI->getPredicate();
-
- if (Pred != ICmpInst::ICMP_EQ)
- return nullptr;
-
- CmpPredicate IPred;
- Value *X = ICI->getOperand(0);
- Value *Y = ICI->getOperand(1);
- if (match(FalseVal, m_Select(m_ICmp(IPred, m_Specific(X), m_Specific(Y)),
- m_AllOnes(), m_One())) &&
- (IPred == ICmpInst::ICMP_ULT || IPred == ICmpInst::ICMP_SLT)) {
-
- // icmp(ult, ptr %X, ptr %Y) -> cannot be folded because
- // there is no intrinsic for a pointer comparison.
- if (!X->getType()->isIntegerTy() || !Y->getType()->isIntegerTy())
- return nullptr;
-
- Builder.SetInsertPoint(&SI);
- auto IID = IPred == ICmpInst::ICMP_ULT ? Intrinsic::ucmp : Intrinsic::scmp;
-
- // Edge Case: if Z is the constant 0 then the select can be folded
- // to just the instrinsic comparison.
- if (match(TrueVal, m_Zero()))
- return Builder.CreateIntrinsic(SI.getType(), IID, {X, Y});
-
- Value *FrozenX = Builder.CreateFreeze(X, X->getName() + ".frz");
- Value *FrozenY = Builder.CreateFreeze(Y, Y->getName() + ".frz");
- Value *Cmp =
- Builder.CreateIntrinsic(FalseVal->getType(), IID, {FrozenX, FrozenY});
- return Builder.CreateSelect(SI.getCondition(), TrueVal, Cmp, "select.ucmp");
- }
-
- return nullptr;
-}
-
// Transform
// select(icmp(eq, X, Y), 0, llvm.cmp(X, Y))
// ->
@@ -2259,9 +2210,6 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal))
return replaceInstUsesWith(SI, V);
- if (Value *V = foldSelectToInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
- return replaceInstUsesWith(SI, V);
-
if (Value *V = foldInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
return replaceInstUsesWith(SI, V);
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 37dc84fe33111..8315028ffca96 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -808,155 +808,6 @@ define i1 @icmp_lt_slt(i1 %c, i32 %arg) {
ret i1 %select
}
-define i16 @icmp_fold_to_llvm_ucmp_when_eq(i16 %x, i16 %y) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_eq(
-; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
-; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
-; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 42, i16 [[TMP2]]
-; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 42, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_when_ult_and_Z_zero(i16 %x, i16 %y) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_ult_and_Z_zero(
-; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X:%.*]], i16 [[Y:%.*]])
-; CHECK-NEXT: ret i16 [[TMP1]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 0, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_when_slt_and_Z_zero(i16 %x, i16 %y) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_slt_and_Z_zero(
-; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.scmp.i16.i16(i16 [[X:%.*]], i16 [[Y:%.*]])
-; CHECK-NEXT: ret i16 [[TMP1]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp slt i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 0, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_when_cmp_slt(i16 %x, i16 %y) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_cmp_slt(
-; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
-; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
-; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.scmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 42, i16 [[TMP2]]
-; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp slt i16 %x, %y ; here "ult" changed to "slt"
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 42, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_when_value(i16 %x, i16 %y, i16 %Z) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_value(
-; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
-; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
-; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 [[TMP2]]
-; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 %Z, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_when_ne(i16 %x, i16 %y) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_when_ne(
-; CHECK-NEXT: [[Y_FRZ:%.*]] = freeze i16 [[Y:%.*]]
-; CHECK-NEXT: [[X_FRZ:%.*]] = freeze i16 [[X:%.*]]
-; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[X_FRZ]], [[Y_FRZ]]
-; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.ucmp.i16.i16(i16 [[X_FRZ]], i16 [[Y_FRZ]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i16 42, i16 [[TMP1]]
-; CHECK-NEXT: ret i16 [[SELECT_UCMP]]
-;
- %3 = icmp ne i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 1
- %6 = select i1 %3, i16 %5, i16 42
- ret i16 %6
-}
-
-define i32 @icmp_fold_to_llvm_ucmp_mixed_types(i16 %0, i16 %1) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_mixed_types(
-; CHECK-NEXT: [[DOTFRZ1:%.*]] = freeze i16 [[TMP1:%.*]]
-; CHECK-NEXT: [[DOTFRZ:%.*]] = freeze i16 [[TMP0:%.*]]
-; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i16 [[DOTFRZ]], [[DOTFRZ1]]
-; CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.ucmp.i32.i16(i16 [[DOTFRZ]], i16 [[DOTFRZ1]])
-; CHECK-NEXT: [[SELECT_UCMP:%.*]] = select i1 [[DOTNOT]], i32 1, i32 [[TMP3]]
-; CHECK-NEXT: ret i32 [[SELECT_UCMP]]
-;
- %.not = icmp eq i16 %0, %1
- %3 = icmp ult i16 %0, %1
- %4 = select i1 %3, i32 -1, i32 1
- %.1 = select i1 %.not, i32 1, i32 %4
- ret i32 %.1
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(i16 %x, i16 %y, i16 %Z) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_1(
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 1
-; CHECK-NEXT: ret i16 [[TMP2]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 1, i16 1 ; invalid constant
- %6 = select i1 %3, i16 %Z, i16 %5
- ret i16 %6
-}
-
-define i16 @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(i16 %x, i16 %y, i16 %Z) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_invalid_constant_2(
-; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i16 [[Z:%.*]], i16 -1
-; CHECK-NEXT: ret i16 [[TMP2]]
-;
- %3 = icmp eq i16 %x, %y
- %4 = icmp ult i16 %x, %y
- %5 = select i1 %4, i16 -1, i16 -1 ; invalid constant
- %6 = select i1 %3, i16 %Z, i16 %5
- ret i16 %6
-}
-
-define i8 @icmp_fold_to_llvm_ucmp_negative_test_ptr(ptr %0, ptr %1) {
-; CHECK-LABEL: @icmp_fold_to_llvm_ucmp_negative_test_ptr(
-; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP0:%.*]], align 8
-; CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP1:%.*]], align 8
-; CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP3]], [[TMP4]]
-; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[TMP5]], i8 -1, i8 1
-; CHECK-NEXT: [[TMP7:%.*]] = icmp eq ptr [[TMP3]], [[TMP4]]
-; CHECK-NEXT: [[TMP8:%.*]] = select i1 [[TMP7]], i8 0, i8 [[TMP6]]
-; CHECK-NEXT: ret i8 [[TMP8]]
-;
- %3 = load ptr, ptr %0, align 8
- %4 = load ptr, ptr %1, align 8
- %5 = icmp ult ptr %3, %4
- %6 = select i1 %5, i8 -1, i8 1
- %7 = icmp eq ptr %3, %4
- %8 = select i1 %7, i8 0, i8 %6
- ret i8 %8
-}
-
define i32 @fold_ucmp(i32 %0, i32 %1) {
; CHECK-LABEL: @fold_ucmp(
; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
@@ -979,5 +830,31 @@ define i32 @fold_scmp(i32 %0, i32 %1) {
ret i32 %5
}
+define i32 @fold_ucmp_negative_test(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_ucmp_negative_test(
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP0]], i32 [[TMP1]])
+; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP3]], i32 1, i32 [[TMP4]]
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call i32 @llvm.ucmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 1, i32 %4 ; wrong constant
+ ret i32 %5
+}
+
+define i32 @fold_scmp_negative_test(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_scmp_negative_test(
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @llvm.scmp.i32.i32(i32 [[TMP0]], i32 [[TMP1]])
+; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP3]], i32 1, i32 [[TMP4]]
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call i32 @llvm.scmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 1, i32 %4 ; wrong constant
+ ret i32 %5
+}
+
declare void @use(i1)
declare void @use.i8(i8)
>From 9bfb9cf98b0fb0edc9d36b7cd8d74595dc11985c Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Sun, 28 Dec 2025 11:13:08 +0100
Subject: [PATCH 08/11] [InstCombine]: Moved fold to
`simplifySelectWithEquivalence`
---
llvm/lib/Analysis/InstructionSimplify.cpp | 31 +++++++++++++++++++
.../InstCombine/InstCombineSelect.cpp | 27 ----------------
2 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 6f44713bd22cd..1a9bcac9d6472 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4673,6 +4673,33 @@ static Value *simplifySelectWithBitTest(Value *CondVal, Value *TrueVal,
return nullptr;
}
+// Transform
+// select(icmp(eq, X, Y), 0, llvm.cmp(X, Y))
+// ->
+// llvm.cmp(X, Y)
+static Value *simplifySelectWhenValInstrinsic(
+ ArrayRef<std::pair<Value *, Value *>> Replacements, Value *TrueVal,
+ Value *FalseVal) {
+ auto *CTrueVal = dyn_cast<ConstantInt>(TrueVal);
+ auto *FalseInstr = dyn_cast<CallInst>(FalseVal);
+
+ if (CTrueVal && FalseInstr && CTrueVal->isZero()) {
+ Function *F = FalseInstr->getCalledFunction();
+ if (!F || !F->isIntrinsic())
+ return nullptr;
+
+ Intrinsic::ID ID = F->getIntrinsicID();
+ if (ID != Intrinsic::scmp && ID != Intrinsic::ucmp)
+ return nullptr;
+
+ if (Replacements[0].first == FalseInstr->getOperand(0) &&
+ Replacements[0].second == FalseInstr->getOperand(1))
+ return FalseInstr;
+ }
+
+ return nullptr;
+}
+
/// Try to simplify a select instruction when its condition operand is an
/// integer equality or floating-point equivalence comparison.
static Value *simplifySelectWithEquivalence(
@@ -4695,6 +4722,10 @@ static Value *simplifySelectWithEquivalence(
if (SimplifiedFalseVal == SimplifiedTrueVal)
return FalseVal;
+ if (auto *V = simplifySelectWhenValInstrinsic(Replacements, SimplifiedTrueVal,
+ SimplifiedFalseVal))
+ return V;
+
return nullptr;
}
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 331c0c1389084..9572f9d702e1b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1955,30 +1955,6 @@ static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
return nullptr;
}
-// Transform
-// select(icmp(eq, X, Y), 0, llvm.cmp(X, Y))
-// ->
-// llvm.cmp(X, Y)
-static Value *foldInstrincCmp(SelectInst &SI, const ICmpInst *ICI,
- Value *TrueVal, Value *FalseVal,
- InstCombiner::BuilderTy &Builder) {
- ICmpInst::Predicate Pred = ICI->getPredicate();
-
- if (Pred != ICmpInst::ICMP_EQ)
- return nullptr;
-
- Value *X = ICI->getOperand(0);
- Value *Y = ICI->getOperand(1);
-
- auto ucmp = m_Intrinsic<Intrinsic::ucmp>(m_Specific(X), m_Specific(Y));
- auto scmp = m_Intrinsic<Intrinsic::scmp>(m_Specific(X), m_Specific(Y));
- if (match(SI.getTrueValue(), m_Zero()) &&
- (match(SI.getFalseValue(), ucmp) || match(SI.getFalseValue(), scmp)))
- return SI.getFalseValue();
-
- return nullptr;
-}
-
/// Fold `X Pred C1 ? X BOp C2 : C1 BOp C2` to `min/max(X, C1) BOp C2`.
/// This allows for better canonicalization.
Value *InstCombinerImpl::foldSelectWithConstOpToBinOp(ICmpInst *Cmp,
@@ -2210,9 +2186,6 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal))
return replaceInstUsesWith(SI, V);
- if (Value *V = foldInstrincCmp(SI, ICI, TrueVal, FalseVal, Builder))
- return replaceInstUsesWith(SI, V);
-
return Changed ? &SI : nullptr;
}
>From ae3f66f5ce7439b6645d9c0efee7cfb18e28c184 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Mon, 29 Dec 2025 11:05:17 +0100
Subject: [PATCH 09/11] [InstCombine]: Integrate into `simplifyWithOpsReplaced`
---
llvm/lib/Analysis/InstructionSimplify.cpp | 72 +++++++++++--------
.../test/Transforms/InstCombine/select-cmp.ll | 46 ++++++++++++
2 files changed, 87 insertions(+), 31 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 1a9bcac9d6472..8d906289c4a6f 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4461,6 +4461,47 @@ static Value *simplifyWithOpsReplaced(Value *V,
return Absorber;
}
+ if (auto *CI = dyn_cast<CallInst>(I)) {
+ Function *F = CI->getCalledFunction();
+
+ // `x == y ? 0 : ucmp(x, y)` where under the replacement y -> x, `ucmp(x,
+ // x)` becomes `0`.
+ if (F && F->isIntrinsic() &&
+ (F->getIntrinsicID() == Intrinsic::scmp ||
+ F->getIntrinsicID() == Intrinsic::ucmp)) {
+ // If the call contains (an invalid) range attribute then a replacement
+ // might produce an unexpected poison value.
+ if (CI->hasRetAttr(Attribute::AttrKind::Range)) {
+ auto Attr = CI->getRetAttr(Attribute::AttrKind::Range);
+ const ConstantRange &CR = Attr.getRange();
+
+ APInt Lo = CR.getLower();
+ APInt Hi = CR.getUpper();
+
+ if (!(Lo == llvm::APInt::getAllOnes(Lo.getBitWidth()) &&
+ Hi == llvm::APInt(Hi.getBitWidth(), 2)))
+ return nullptr;
+ }
+
+ // To apply this fold, we have to do the replacement and return `0` if
+ // the arguments are equal.
+ SmallVector<Value *, 2> ReplacedArgs;
+ for (auto &NewOp : NewOps) {
+ for (auto &[A, B] : Ops) {
+ if (NewOp == A)
+ ReplacedArgs.push_back(B);
+ else
+ ReplacedArgs.push_back(A);
+ }
+ }
+
+ if (ReplacedArgs[0] == ReplacedArgs[1])
+ return llvm::ConstantInt::get(F->getReturnType(), 0);
+ else
+ return nullptr;
+ }
+ }
+
if (isa<GetElementPtrInst>(I)) {
// getelementptr x, 0 -> x.
// This never returns poison, even if inbounds is set.
@@ -4673,33 +4714,6 @@ static Value *simplifySelectWithBitTest(Value *CondVal, Value *TrueVal,
return nullptr;
}
-// Transform
-// select(icmp(eq, X, Y), 0, llvm.cmp(X, Y))
-// ->
-// llvm.cmp(X, Y)
-static Value *simplifySelectWhenValInstrinsic(
- ArrayRef<std::pair<Value *, Value *>> Replacements, Value *TrueVal,
- Value *FalseVal) {
- auto *CTrueVal = dyn_cast<ConstantInt>(TrueVal);
- auto *FalseInstr = dyn_cast<CallInst>(FalseVal);
-
- if (CTrueVal && FalseInstr && CTrueVal->isZero()) {
- Function *F = FalseInstr->getCalledFunction();
- if (!F || !F->isIntrinsic())
- return nullptr;
-
- Intrinsic::ID ID = F->getIntrinsicID();
- if (ID != Intrinsic::scmp && ID != Intrinsic::ucmp)
- return nullptr;
-
- if (Replacements[0].first == FalseInstr->getOperand(0) &&
- Replacements[0].second == FalseInstr->getOperand(1))
- return FalseInstr;
- }
-
- return nullptr;
-}
-
/// Try to simplify a select instruction when its condition operand is an
/// integer equality or floating-point equivalence comparison.
static Value *simplifySelectWithEquivalence(
@@ -4722,10 +4736,6 @@ static Value *simplifySelectWithEquivalence(
if (SimplifiedFalseVal == SimplifiedTrueVal)
return FalseVal;
- if (auto *V = simplifySelectWhenValInstrinsic(Replacements, SimplifiedTrueVal,
- SimplifiedFalseVal))
- return V;
-
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 8315028ffca96..042f0d5540d98 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -856,5 +856,51 @@ define i32 @fold_scmp_negative_test(i32 %0, i32 %1) {
ret i32 %5
}
+define i32 @fold_ucmp_default_range(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_ucmp_default_range(
+; CHECK-NEXT: [[TMP3:%.*]] = tail call range(i32 -1, 2) i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call range(i32 -1, 2) i32 @llvm.ucmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
+define i32 @fold_scmp_default_range(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_scmp_default_range(
+; CHECK-NEXT: [[TMP3:%.*]] = tail call range(i32 -1, 2) i32 @llvm.scmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call range(i32 -1, 2) i32 @llvm.scmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
+define i32 @fold_ucmp_negative_test_updated_range(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_ucmp_negative_test_updated_range(
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: ret i32 [[TMP4]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call range(i32 1, 2) i32 @llvm.ucmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
+define i32 @fold_scmp_negative_test_updated_range(i32 %0, i32 %1) {
+; CHECK-LABEL: @fold_scmp_negative_test_updated_range(
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: ret i32 [[TMP4]]
+;
+ %3 = icmp eq i32 %0, %1
+ %4 = tail call range(i32 1, 2) i32 @llvm.scmp.i32.i32(i32 %0, i32 %1)
+ %5 = select i1 %3, i32 0, i32 %4
+ ret i32 %5
+}
+
declare void @use(i1)
declare void @use.i8(i8)
>From b570c4221926ad9529c5ef426fd08a514f9bd12c Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Mon, 29 Dec 2025 12:40:54 +0100
Subject: [PATCH 10/11] [InstCombine]: Removed var
---
llvm/lib/Analysis/InstructionSimplify.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 8d906289c4a6f..9e0c3a44eb6a9 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4472,8 +4472,8 @@ static Value *simplifyWithOpsReplaced(Value *V,
// If the call contains (an invalid) range attribute then a replacement
// might produce an unexpected poison value.
if (CI->hasRetAttr(Attribute::AttrKind::Range)) {
- auto Attr = CI->getRetAttr(Attribute::AttrKind::Range);
- const ConstantRange &CR = Attr.getRange();
+ const ConstantRange &CR =
+ CI->getRetAttr(Attribute::AttrKind::Range).getRange();
APInt Lo = CR.getLower();
APInt Hi = CR.getUpper();
>From f6efe92a7c56cc669330112e21371a79c2f24da6 Mon Sep 17 00:00:00 2001
From: Kevin Per <kevin.per at protonmail.com>
Date: Mon, 29 Dec 2025 17:32:13 +0100
Subject: [PATCH 11/11] [InstCombine]: Addressed feedback
---
llvm/lib/Analysis/InstructionSimplify.cpp | 46 +++++--------------
.../test/Transforms/InstCombine/select-cmp.ll | 14 +++---
2 files changed, 17 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 9e0c3a44eb6a9..67ea5ad2f249a 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4461,44 +4461,20 @@ static Value *simplifyWithOpsReplaced(Value *V,
return Absorber;
}
- if (auto *CI = dyn_cast<CallInst>(I)) {
- Function *F = CI->getCalledFunction();
-
- // `x == y ? 0 : ucmp(x, y)` where under the replacement y -> x, `ucmp(x,
- // x)` becomes `0`.
- if (F && F->isIntrinsic() &&
- (F->getIntrinsicID() == Intrinsic::scmp ||
- F->getIntrinsicID() == Intrinsic::ucmp)) {
- // If the call contains (an invalid) range attribute then a replacement
- // might produce an unexpected poison value.
- if (CI->hasRetAttr(Attribute::AttrKind::Range)) {
- const ConstantRange &CR =
- CI->getRetAttr(Attribute::AttrKind::Range).getRange();
-
- APInt Lo = CR.getLower();
- APInt Hi = CR.getUpper();
-
- if (!(Lo == llvm::APInt::getAllOnes(Lo.getBitWidth()) &&
- Hi == llvm::APInt(Hi.getBitWidth(), 2)))
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ // `x == y ? 0 : ucmp(x, y)` where under the replacement y -> x,
+ // `ucmp(x, x)` becomes `0`.
+ if (NewOps[0] == NewOps[1] && (II->getIntrinsicID() == Intrinsic::scmp ||
+ II->getIntrinsicID() == Intrinsic::ucmp)) {
+ if (II->hasPoisonGeneratingAnnotations()) {
+ if (!DropFlags)
return nullptr;
+ else
+ DropFlags->push_back(II);
}
- // To apply this fold, we have to do the replacement and return `0` if
- // the arguments are equal.
- SmallVector<Value *, 2> ReplacedArgs;
- for (auto &NewOp : NewOps) {
- for (auto &[A, B] : Ops) {
- if (NewOp == A)
- ReplacedArgs.push_back(B);
- else
- ReplacedArgs.push_back(A);
- }
- }
-
- if (ReplacedArgs[0] == ReplacedArgs[1])
- return llvm::ConstantInt::get(F->getReturnType(), 0);
- else
- return nullptr;
+ return llvm::ConstantInt::get(II->getFunctionType()->getReturnType(),
+ 0);
}
}
diff --git a/llvm/test/Transforms/InstCombine/select-cmp.ll b/llvm/test/Transforms/InstCombine/select-cmp.ll
index 042f0d5540d98..4c8e30c10d831 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp.ll
@@ -858,7 +858,7 @@ define i32 @fold_scmp_negative_test(i32 %0, i32 %1) {
define i32 @fold_ucmp_default_range(i32 %0, i32 %1) {
; CHECK-LABEL: @fold_ucmp_default_range(
-; CHECK-NEXT: [[TMP3:%.*]] = tail call range(i32 -1, 2) i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
; CHECK-NEXT: ret i32 [[TMP3]]
;
%3 = icmp eq i32 %0, %1
@@ -869,7 +869,7 @@ define i32 @fold_ucmp_default_range(i32 %0, i32 %1) {
define i32 @fold_scmp_default_range(i32 %0, i32 %1) {
; CHECK-LABEL: @fold_scmp_default_range(
-; CHECK-NEXT: [[TMP3:%.*]] = tail call range(i32 -1, 2) i32 @llvm.scmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
+; CHECK-NEXT: [[TMP3:%.*]] = tail call i32 @llvm.scmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
; CHECK-NEXT: ret i32 [[TMP3]]
;
%3 = icmp eq i32 %0, %1
@@ -878,10 +878,9 @@ define i32 @fold_scmp_default_range(i32 %0, i32 %1) {
ret i32 %5
}
-define i32 @fold_ucmp_negative_test_updated_range(i32 %0, i32 %1) {
+define i32 @fold_ucmp_drop_range(i32 %0, i32 %1) {
; CHECK-LABEL: @fold_ucmp_negative_test_updated_range(
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0:%.*]], [[TMP1:%.*]]
-; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
; CHECK-NEXT: ret i32 [[TMP4]]
;
%3 = icmp eq i32 %0, %1
@@ -890,10 +889,9 @@ define i32 @fold_ucmp_negative_test_updated_range(i32 %0, i32 %1) {
ret i32 %5
}
-define i32 @fold_scmp_negative_test_updated_range(i32 %0, i32 %1) {
+define i32 @fold_scmp_drop_range(i32 %0, i32 %1) {
; CHECK-LABEL: @fold_scmp_negative_test_updated_range(
-; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i32 [[TMP0:%.*]], [[TMP1:%.*]]
-; CHECK-NEXT: [[TMP4:%.*]] = zext i1 [[TMP3]] to i32
+; CHECK-NEXT: [[TMP4:%.*]] = tail call i32 @llvm.scmp.i32.i32(i32 [[TMP0:%.*]], i32 [[TMP1:%.*]])
; CHECK-NEXT: ret i32 [[TMP4]]
;
%3 = icmp eq i32 %0, %1
More information about the llvm-commits
mailing list