From 899d75305387bc12c3e56540ef5a391b10f77e05 Mon Sep 17 00:00:00 2001 From: Kevin Per 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 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 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 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 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 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(m_Specific(X), m_Specific(Y)); + auto scmp = m_Intrinsic(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)