[llvm] [Instcombine]: `llvm.ucmp` and `llvm.scmp` recognition (PR #168505)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 20 22:34:18 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 1/6] [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 2/6] [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 3/6] [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 4/6] [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 5/6] [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 6/6] [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)
More information about the llvm-commits
mailing list