[llvm] InstCombine: fold select(X+umax(X,A)<X, C, umin(X+umax(X,A),C)) -> umin(X+umax(X,A),C) (PR #210609)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 19 09:26:42 PDT 2026
https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/210609
When X + umax(X, A) has nsw and A and C are non-negative, the unsigned wrap implies X + umax(X, A) >= 2^(bw-1). Since C < 2^(bw-1), umin will return C.
Alive2: https://alive2.llvm.org/ce/z/ajmtVW
Fixes #143030
>From 830f7b661a7559b87f9958a1f4b1fac6a2723595 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sat, 18 Jul 2026 14:49:14 -0400
Subject: [PATCH] InstCombine: fold select(X+umax(X,A)<X, C,
umin(X+umax(X,A),C)) -> umin(X+umax(X,A),C)
When X + umax(X, A) has nsw and A and C are non-negative, the unsigned wrap implies X + umax(X, A) >= 2^(bw-1). Since C < 2^(bw-1), umin will return C.
Alive2: https://alive2.llvm.org/ce/z/ajmtVW
Fixes #143030
---
.../InstCombine/InstCombineSelect.cpp | 26 +++++
.../InstCombine/select-umin-umax.ll | 107 ++++++++++++++++++
2 files changed, 133 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/select-umin-umax.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index f107c15304d9f..328c0113f9a69 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4850,6 +4850,32 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
if (Instruction *Mul = foldSelectZeroOrFixedOp(SI, *this))
return Mul;
+ // fold select (X + umax(X, A) < X), C, umin(X + umax(X, A), C) -> umin(X +
+ // umax(X, A), C) If X + umax(X, A) has nsw and A and C are non-negative, the
+ // unsigned wrap implies X + umax(X, A) >= 2^(bw-1). Since C < 2^(bw-1), umin
+ // will return C.
+ {
+ auto *ICI = dyn_cast<ICmpInst>(CondVal);
+ Value *X, *A, *Sum, *CVal;
+ if (ICI && ICI->hasOneUse() && match(TrueVal, m_Value(CVal)) &&
+ match(FalseVal,
+ m_c_Intrinsic<Intrinsic::umin>(m_Value(Sum), m_Specific(CVal))) &&
+ match(Sum, m_c_Add(m_Value(X), m_c_UMax(m_Deferred(X), m_Value(A))))) {
+ ICmpInst::Predicate Pred = ICI->getPredicate();
+
+ if ((Pred == ICmpInst::ICMP_ULT && ICI->getOperand(0) == Sum &&
+ ICI->getOperand(1) == X) ||
+ (Pred == ICmpInst::ICMP_UGT && ICI->getOperand(0) == X &&
+ ICI->getOperand(1) == Sum)) {
+ auto *Add = cast<OverflowingBinaryOperator>(Sum);
+ if (Add->hasNoSignedWrap() &&
+ isKnownNonNegative(CVal, SQ.getWithInstruction(&SI)) &&
+ isKnownNonNegative(A, SQ.getWithInstruction(&SI)))
+ return replaceInstUsesWith(SI, FalseVal);
+ }
+ }
+ }
+
// Turn (select C, (op X, Y), (op X, Z)) -> (op X, (select C, Y, Z))
auto *TI = dyn_cast<Instruction>(TrueVal);
auto *FI = dyn_cast<Instruction>(FalseVal);
diff --git a/llvm/test/Transforms/InstCombine/select-umin-umax.ll b/llvm/test/Transforms/InstCombine/select-umin-umax.ll
new file mode 100644
index 0000000000000..d95825d571342
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-umin-umax.ll
@@ -0,0 +1,107 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+define i64 @test_pos_constant(i64 %0) {
+; CHECK-LABEL: define i64 @test_pos_constant(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 1)
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP3]], i64 329406144173384850)
+; CHECK-NEXT: ret i64 [[TMP4]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 1)
+ %3 = add nsw i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 329406144173384850)
+ %6 = select i1 %4, i64 329406144173384850, i64 %5
+ ret i64 %6
+}
+
+define i64 @test_pos_variable_A(i64 %0, i64 %A) {
+; CHECK-LABEL: define i64 @test_pos_variable_A(
+; CHECK-SAME: i64 [[TMP0:%.*]], i64 [[A:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 [[A]])
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], [[TMP0]]
+; CHECK-NEXT: [[TMP5:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP3]], i64 329406144173384850)
+; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[TMP4]], i64 329406144173384850, i64 [[TMP5]]
+; CHECK-NEXT: ret i64 [[TMP6]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 %A)
+ %3 = add nsw i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 329406144173384850)
+ %6 = select i1 %4, i64 329406144173384850, i64 %5
+ ret i64 %6
+}
+
+define i64 @test_neg_no_nsw(i64 %0) {
+; CHECK-LABEL: define i64 @test_neg_no_nsw(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 1)
+; CHECK-NEXT: [[TMP3:%.*]] = add i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], [[TMP0]]
+; CHECK-NEXT: [[TMP5:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP3]], i64 329406144173384850)
+; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[TMP4]], i64 329406144173384850, i64 [[TMP5]]
+; CHECK-NEXT: ret i64 [[TMP6]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 1)
+ %3 = add i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 329406144173384850)
+ %6 = select i1 %4, i64 329406144173384850, i64 %5
+ ret i64 %6
+}
+
+define i64 @test_neg_C_too_large(i64 %0) {
+; CHECK-LABEL: define i64 @test_neg_C_too_large(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 1)
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.uadd.sat.i64(i64 [[TMP0]], i64 [[TMP2]])
+; CHECK-NEXT: ret i64 [[TMP3]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 1)
+ %3 = add nsw i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 -1)
+ %6 = select i1 %4, i64 -1, i64 %5
+ ret i64 %6
+}
+
+define i64 @test_pos_C_max_positive(i64 %0) {
+; CHECK-LABEL: define i64 @test_pos_C_max_positive(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 1)
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP3]], i64 9223372036854775807)
+; CHECK-NEXT: ret i64 [[TMP4]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 1)
+ %3 = add nsw i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ ; 9223372036854775807 is i64_max (0x7FFFFFFFFFFFFFFF)
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 9223372036854775807)
+ %6 = select i1 %4, i64 9223372036854775807, i64 %5
+ ret i64 %6
+}
+
+define i64 @test_neg_C_min_negative(i64 %0) {
+; CHECK-LABEL: define i64 @test_neg_C_min_negative(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP0]], i64 1)
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[TMP2]], [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], [[TMP0]]
+; CHECK-NEXT: [[TMP5:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP3]], i64 -9223372036854775808)
+; CHECK-NEXT: [[TMP6:%.*]] = select i1 [[TMP4]], i64 -9223372036854775808, i64 [[TMP5]]
+; CHECK-NEXT: ret i64 [[TMP6]]
+;
+ %2 = call i64 @llvm.umax.i64(i64 %0, i64 1)
+ %3 = add nsw i64 %2, %0
+ %4 = icmp ult i64 %3, %0
+ ; -9223372036854775808 is i64_min (0x8000000000000000)
+ %5 = call i64 @llvm.umin.i64(i64 %3, i64 -9223372036854775808)
+ %6 = select i1 %4, i64 -9223372036854775808, i64 %5
+ ret i64 %6
+}
+
+declare i64 @llvm.umax.i64(i64, i64)
+declare i64 @llvm.umin.i64(i64, i64)
More information about the llvm-commits
mailing list