[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