[llvm] [InstCombine] Fold `min(X+1, Y) - min(X, Y) --> zext X < Y` (PR #157782)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 10 13:04:55 PDT 2025
https://github.com/benwu25 updated https://github.com/llvm/llvm-project/pull/157782
>From 72eb14a268c5ec3e33260ff2b8393b38867cbb21 Mon Sep 17 00:00:00 2001
From: benwu25 <soggysocks206 at gmail.com>
Date: Tue, 9 Sep 2025 18:34:46 -0700
Subject: [PATCH 1/4] [InstCombine] New test (#157524)
---
llvm/test/Transforms/InstCombine/min-zext.ll | 112 +++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/min-zext.ll
diff --git a/llvm/test/Transforms/InstCombine/min-zext.ll b/llvm/test/Transforms/InstCombine/min-zext.ll
new file mode 100644
index 0000000000000..4e56aebd9fc68
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/min-zext.ll
@@ -0,0 +1,112 @@
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @test_smin(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i32 @test_smin(
+; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
+;
+ %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+ %v1 = add nsw i32 %arg0, 1
+ %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+ %v3 = sub i32 %v2, %v0
+ ret i32 %v3
+}
+
+define i32 @test_umin(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i32 @test_umin(
+; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add nuw i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
+;
+ %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+ %v1 = add nuw i32 %arg0, 1
+ %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+ %v3 = sub i32 %v2, %v0
+ ret i32 %v3
+}
+
+define i1 @test_smin_i1(i1 %arg0, i1 %arg1) {
+; CHECK-LABEL: define i1 @test_smin_i1(
+; CHECK-NEXT: %v0 = or i1 %arg0, %arg1
+; CHECK-NEXT: %v3 = xor i1 %v0, true
+; CHECK-NEXT: ret i1 %v3
+;
+ %v0 = tail call i1 @llvm.smin.i1(i1 %arg0, i1 %arg1)
+ %v1 = add nsw i1 %arg0, 1
+ %v2 = tail call i1 @llvm.smin.i1(i1 %v1, i1 %arg1)
+ %v3 = sub i1 %v2, %v0
+ ret i1 %v3
+}
+
+declare void @use(i2)
+
+define i2 @test_smin_use_operands(i2 %arg0, i2 %arg1) {
+; CHECK-LABEL: define i2 @test_smin_use_operands(
+; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+; CHECK-NEXT: %v3 = sub i2 %v2, %v0
+; CHECK-NEXT: call void @use(i2 %v2)
+; CHECK-NEXT: call void @use(i2 %v0)
+; CHECK-NEXT: ret i2 %v3
+;
+ %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+ %v1 = add nsw i2 %arg0, 1
+ %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+ %v3 = sub i2 %v2, %v0
+ call void @use(i2 %v2)
+ call void @use(i2 %v0)
+ ret i2 %v3
+}
+
+define i2 @test_smin_use_operand(i2 %arg0, i2 %arg1) {
+; CHECK-LABEL: define i2 @test_smin_use_operand(
+; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+; CHECK-NEXT: %v3 = sub i2 %v2, %v0
+; CHECK-NEXT: call void @use(i2 %v2)
+; CHECK-NEXT: ret i2 %v3
+;
+ %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+ %v1 = add nsw i2 %arg0, 1
+ %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+ %v3 = sub i2 %v2, %v0
+ call void @use(i2 %v2)
+ ret i2 %v3
+}
+
+define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i32 @test_smin_missing_nsw(
+; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
+;
+ %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+ %v1 = add i32 %arg0, 1
+ %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+ %v3 = sub i32 %v2, %v0
+ ret i32 %v3
+}
+
+define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i32 @test_umin_missing_nuw(
+; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
+;
+ %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+ %v1 = add i32 %arg0, 1
+ %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+ %v3 = sub i32 %v2, %v0
+ ret i32 %v3
+}
>From b8d16acadffdf1e20f8bb75f953447be2a7feb4d Mon Sep 17 00:00:00 2001
From: benwu25 <soggysocks206 at gmail.com>
Date: Tue, 9 Sep 2025 19:28:53 -0700
Subject: [PATCH 2/4] [InstCombine] Fold min(X+1, Y) - min(X, Y) --> zext X < Y
(#157524)
This fold is invalid for @llvm.smin.i1, since smin(-1, 0) == -1
(take X = Y = 0). Otherwise, if X+1 has the appropriate nsw or nuw,
this transform replaces a sub and at least one min with an icmp and
a zext. It is also invalid for i1 in general, but it seems that other
folds take care of i1. In #157524, this expression was folded to a
select, but it seems that select X < Y, 1, 0 can be canonicalized to
zext X < Y.
---
.../InstCombine/InstCombineAddSub.cpp | 18 ++++
llvm/test/Transforms/InstCombine/min-zext.ll | 84 ++++++++++---------
2 files changed, 62 insertions(+), 40 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index d934638c15e75..63c6fc52322d6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2719,6 +2719,24 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
return BinaryOperator::CreateSub(X, Not);
}
+ // min(X+1, Y) - min(X, Y) --> zext X < Y
+ // Replacing a sub and at least one min with an icmp
+ // and a zext is a potential improvement.
+ if (match(Op0, m_c_SMin(m_c_NSWAdd(m_Value(X), m_One()), m_Value(Y))) &&
+ match(Op1, m_c_SMin(m_Value(X), m_Value(Y))) &&
+ I.getType()->getScalarSizeInBits() != 1 &&
+ (Op0->hasOneUse() || Op1->hasOneUse())) {
+ Value *Cond = Builder.CreateICmpSLT(X, Y);
+ return new ZExtInst(Cond, I.getType());
+ }
+ if (match(Op0, m_c_UMin(m_c_NUWAdd(m_Value(X), m_One()), m_Value(Y))) &&
+ match(Op1, m_c_UMin(m_Value(X), m_Value(Y))) &&
+ I.getType()->getScalarSizeInBits() != 1 &&
+ (Op0->hasOneUse() || Op1->hasOneUse())) {
+ Value *Cond = Builder.CreateICmpULT(X, Y);
+ return new ZExtInst(Cond, I.getType());
+ }
+
// Optimize pointer differences into the same array into a size. Consider:
// &A[10] - &A[0]: we should compile this to "10".
Value *LHSOp, *RHSOp;
diff --git a/llvm/test/Transforms/InstCombine/min-zext.ll b/llvm/test/Transforms/InstCombine/min-zext.ll
index 4e56aebd9fc68..43af1f48bcfed 100644
--- a/llvm/test/Transforms/InstCombine/min-zext.ll
+++ b/llvm/test/Transforms/InstCombine/min-zext.ll
@@ -1,12 +1,12 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define i32 @test_smin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add nsw i32 %arg0, 1
@@ -17,11 +17,10 @@ define i32 @test_smin(i32 %arg0, i32 %arg1) {
define i32 @test_umin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add nuw i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add nuw i32 %arg0, 1
@@ -32,9 +31,10 @@ define i32 @test_umin(i32 %arg0, i32 %arg1) {
define i1 @test_smin_i1(i1 %arg0, i1 %arg1) {
; CHECK-LABEL: define i1 @test_smin_i1(
-; CHECK-NEXT: %v0 = or i1 %arg0, %arg1
-; CHECK-NEXT: %v3 = xor i1 %v0, true
-; CHECK-NEXT: ret i1 %v3
+; CHECK-SAME: i1 [[ARG0:%.*]], i1 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = or i1 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = xor i1 [[V0]], true
+; CHECK-NEXT: ret i1 [[V3]]
;
%v0 = tail call i1 @llvm.smin.i1(i1 %arg0, i1 %arg1)
%v1 = add nsw i1 %arg0, 1
@@ -47,47 +47,50 @@ declare void @use(i2)
define i2 @test_smin_use_operands(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operands(
-; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
-; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
-; CHECK-NEXT: %v3 = sub i2 %v2, %v0
-; CHECK-NEXT: call void @use(i2 %v2)
-; CHECK-NEXT: call void @use(i2 %v0)
-; CHECK-NEXT: ret i2 %v3
+; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i2 @llvm.smin.i2(i2 [[ARG0]], i2 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i2 [[V2]], [[V0]]
+; CHECK-NEXT: call void @use(i2 [[V2]])
+; CHECK-NEXT: call void @use(i2 [[V0]])
+; CHECK-NEXT: ret i2 [[V3]]
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
call void @use(i2 %v0)
- ret i2 %v3
+ ret i2 %v3
}
define i2 @test_smin_use_operand(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operand(
-; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
-; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
-; CHECK-NEXT: %v3 = sub i2 %v2, %v0
-; CHECK-NEXT: call void @use(i2 %v2)
-; CHECK-NEXT: ret i2 %v3
+; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i2 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i2
+; CHECK-NEXT: call void @use(i2 [[V2]])
+; CHECK-NEXT: ret i2 [[V3]]
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
- ret i2 %v3
+ ret i2 %v3
}
define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin_missing_nsw(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.smin.i32(i32 [[ARG0]], i32 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
@@ -98,11 +101,12 @@ define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin_missing_nuw(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.umin.i32(i32 [[ARG0]], i32 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.umin.i32(i32 [[V1]], i32 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
>From e6a44697d2d09def667aae2526c5436566073faa Mon Sep 17 00:00:00 2001
From: benwu25 <soggysocks206 at gmail.com>
Date: Wed, 10 Sep 2025 12:56:22 -0700
Subject: [PATCH 3/4] [InstCombine] Add mismatched operands test (#157524)
In case I misinterpreted, this can be changed to mismatch the
arguments of one of the min calls instead.
---
llvm/test/Transforms/InstCombine/min-zext.ll | 99 +++++++++++---------
1 file changed, 55 insertions(+), 44 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/min-zext.ll b/llvm/test/Transforms/InstCombine/min-zext.ll
index 43af1f48bcfed..8f44a5f9cc5c4 100644
--- a/llvm/test/Transforms/InstCombine/min-zext.ll
+++ b/llvm/test/Transforms/InstCombine/min-zext.ll
@@ -1,12 +1,12 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define i32 @test_smin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin(
-; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[ARG0]], [[ARG1]]
-; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
-; CHECK-NEXT: ret i32 [[V3]]
+; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add nsw i32 %arg0, 1
@@ -17,10 +17,11 @@ define i32 @test_smin(i32 %arg0, i32 %arg1) {
define i32 @test_umin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin(
-; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ARG0]], [[ARG1]]
-; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
-; CHECK-NEXT: ret i32 [[V3]]
+; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add nuw i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add nuw i32 %arg0, 1
@@ -31,10 +32,9 @@ define i32 @test_umin(i32 %arg0, i32 %arg1) {
define i1 @test_smin_i1(i1 %arg0, i1 %arg1) {
; CHECK-LABEL: define i1 @test_smin_i1(
-; CHECK-SAME: i1 [[ARG0:%.*]], i1 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[V0:%.*]] = or i1 [[ARG0]], [[ARG1]]
-; CHECK-NEXT: [[V3:%.*]] = xor i1 [[V0]], true
-; CHECK-NEXT: ret i1 [[V3]]
+; CHECK-NEXT: %v0 = or i1 %arg0, %arg1
+; CHECK-NEXT: %v3 = xor i1 %v0, true
+; CHECK-NEXT: ret i1 %v3
;
%v0 = tail call i1 @llvm.smin.i1(i1 %arg0, i1 %arg1)
%v1 = add nsw i1 %arg0, 1
@@ -47,50 +47,47 @@ declare void @use(i2)
define i2 @test_smin_use_operands(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operands(
-; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[V0:%.*]] = tail call i2 @llvm.smin.i2(i2 [[ARG0]], i2 [[ARG1]])
-; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
-; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
-; CHECK-NEXT: [[V3:%.*]] = sub i2 [[V2]], [[V0]]
-; CHECK-NEXT: call void @use(i2 [[V2]])
-; CHECK-NEXT: call void @use(i2 [[V0]])
-; CHECK-NEXT: ret i2 [[V3]]
+; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+; CHECK-NEXT: %v3 = sub i2 %v2, %v0
+; CHECK-NEXT: call void @use(i2 %v2)
+; CHECK-NEXT: call void @use(i2 %v0)
+; CHECK-NEXT: ret i2 %v3
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
call void @use(i2 %v0)
- ret i2 %v3
+ ret i2 %v3
}
define i2 @test_smin_use_operand(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operand(
-; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
-; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
-; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i2 [[ARG0]], [[ARG1]]
-; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i2
-; CHECK-NEXT: call void @use(i2 [[V2]])
-; CHECK-NEXT: ret i2 [[V3]]
+; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
+; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
+; CHECK-NEXT: %v3 = sub i2 %v2, %v0
+; CHECK-NEXT: call void @use(i2 %v2)
+; CHECK-NEXT: ret i2 %v3
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
- ret i2 %v3
+ ret i2 %v3
}
define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin_missing_nsw(
-; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.smin.i32(i32 [[ARG0]], i32 [[ARG1]])
-; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
-; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 [[ARG1]])
-; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
-; CHECK-NEXT: ret i32 [[V3]]
+; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
@@ -101,12 +98,11 @@ define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin_missing_nuw(
-; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
-; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.umin.i32(i32 [[ARG0]], i32 [[ARG1]])
-; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
-; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.umin.i32(i32 [[V1]], i32 [[ARG1]])
-; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
-; CHECK-NEXT: ret i32 [[V3]]
+; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v2, %v0
+; CHECK-NEXT: ret i32 %v3
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
@@ -114,3 +110,18 @@ define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
%v3 = sub i32 %v2, %v0
ret i32 %v3
}
+
+define i32 @test_mismatched_operands(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i32 @test_mismatched_operands(
+; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
+; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+; CHECK-NEXT: %v3 = sub i32 %v0, %v2
+; CHECK-NEXT: ret i32 %v3
+;
+ %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
+ %v1 = add nsw i32 %arg0, 1
+ %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
+ %v3 = sub i32 %v0, %v2
+ ret i32 %v3
+}
>From fb65223b604c5f1c45ef813391fd97909aef18d3 Mon Sep 17 00:00:00 2001
From: benwu25 <soggysocks206 at gmail.com>
Date: Wed, 10 Sep 2025 13:02:32 -0700
Subject: [PATCH 4/4] [InstCombine] Use m_Specific rather than m_Value
(#157524)
---
.../InstCombine/InstCombineAddSub.cpp | 4 +-
llvm/test/Transforms/InstCombine/min-zext.ll | 95 ++++++++++---------
2 files changed, 52 insertions(+), 47 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 63c6fc52322d6..b798d839efa43 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2723,14 +2723,14 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
// Replacing a sub and at least one min with an icmp
// and a zext is a potential improvement.
if (match(Op0, m_c_SMin(m_c_NSWAdd(m_Value(X), m_One()), m_Value(Y))) &&
- match(Op1, m_c_SMin(m_Value(X), m_Value(Y))) &&
+ match(Op1, m_c_SMin(m_Specific(X), m_Specific(Y))) &&
I.getType()->getScalarSizeInBits() != 1 &&
(Op0->hasOneUse() || Op1->hasOneUse())) {
Value *Cond = Builder.CreateICmpSLT(X, Y);
return new ZExtInst(Cond, I.getType());
}
if (match(Op0, m_c_UMin(m_c_NUWAdd(m_Value(X), m_One()), m_Value(Y))) &&
- match(Op1, m_c_UMin(m_Value(X), m_Value(Y))) &&
+ match(Op1, m_c_UMin(m_Specific(X), m_Specific(Y))) &&
I.getType()->getScalarSizeInBits() != 1 &&
(Op0->hasOneUse() || Op1->hasOneUse())) {
Value *Cond = Builder.CreateICmpULT(X, Y);
diff --git a/llvm/test/Transforms/InstCombine/min-zext.ll b/llvm/test/Transforms/InstCombine/min-zext.ll
index 8f44a5f9cc5c4..254ad1ece59d1 100644
--- a/llvm/test/Transforms/InstCombine/min-zext.ll
+++ b/llvm/test/Transforms/InstCombine/min-zext.ll
@@ -1,12 +1,12 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define i32 @test_smin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add nsw i32 %arg0, 1
@@ -17,11 +17,10 @@ define i32 @test_smin(i32 %arg0, i32 %arg1) {
define i32 @test_umin(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add nuw i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add nuw i32 %arg0, 1
@@ -32,9 +31,10 @@ define i32 @test_umin(i32 %arg0, i32 %arg1) {
define i1 @test_smin_i1(i1 %arg0, i1 %arg1) {
; CHECK-LABEL: define i1 @test_smin_i1(
-; CHECK-NEXT: %v0 = or i1 %arg0, %arg1
-; CHECK-NEXT: %v3 = xor i1 %v0, true
-; CHECK-NEXT: ret i1 %v3
+; CHECK-SAME: i1 [[ARG0:%.*]], i1 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = or i1 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = xor i1 [[V0]], true
+; CHECK-NEXT: ret i1 [[V3]]
;
%v0 = tail call i1 @llvm.smin.i1(i1 %arg0, i1 %arg1)
%v1 = add nsw i1 %arg0, 1
@@ -47,47 +47,50 @@ declare void @use(i2)
define i2 @test_smin_use_operands(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operands(
-; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
-; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
-; CHECK-NEXT: %v3 = sub i2 %v2, %v0
-; CHECK-NEXT: call void @use(i2 %v2)
-; CHECK-NEXT: call void @use(i2 %v0)
-; CHECK-NEXT: ret i2 %v3
+; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i2 @llvm.smin.i2(i2 [[ARG0]], i2 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i2 [[V2]], [[V0]]
+; CHECK-NEXT: call void @use(i2 [[V2]])
+; CHECK-NEXT: call void @use(i2 [[V0]])
+; CHECK-NEXT: ret i2 [[V3]]
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
call void @use(i2 %v0)
- ret i2 %v3
+ ret i2 %v3
}
define i2 @test_smin_use_operand(i2 %arg0, i2 %arg1) {
; CHECK-LABEL: define i2 @test_smin_use_operand(
-; CHECK-NEXT: %v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
-; CHECK-NEXT: %v1 = add nsw i2 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
-; CHECK-NEXT: %v3 = sub i2 %v2, %v0
-; CHECK-NEXT: call void @use(i2 %v2)
-; CHECK-NEXT: ret i2 %v3
+; CHECK-SAME: i2 [[ARG0:%.*]], i2 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V1:%.*]] = add nsw i2 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i2 @llvm.smin.i2(i2 [[V1]], i2 [[ARG1]])
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i2 [[ARG0]], [[ARG1]]
+; CHECK-NEXT: [[V3:%.*]] = zext i1 [[TMP1]] to i2
+; CHECK-NEXT: call void @use(i2 [[V2]])
+; CHECK-NEXT: ret i2 [[V3]]
;
%v0 = tail call i2 @llvm.smin.i2(i2 %arg0, i2 %arg1)
%v1 = add nsw i2 %arg0, 1
%v2 = tail call i2 @llvm.smin.i2(i2 %v1, i2 %arg1)
- %v3 = sub i2 %v2, %v0
+ %v3 = sub i2 %v2, %v0
call void @use(i2 %v2)
- ret i2 %v3
+ ret i2 %v3
}
define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_smin_missing_nsw(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.smin.i32(i32 [[ARG0]], i32 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
@@ -98,11 +101,12 @@ define i32 @test_smin_missing_nsw(i32 %arg0, i32 %arg1) {
define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_umin_missing_nuw(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v2, %v0
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.umin.i32(i32 [[ARG0]], i32 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add i32 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.umin.i32(i32 [[V1]], i32 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V2]], [[V0]]
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1)
%v1 = add i32 %arg0, 1
@@ -113,11 +117,12 @@ define i32 @test_umin_missing_nuw(i32 %arg0, i32 %arg1) {
define i32 @test_mismatched_operands(i32 %arg0, i32 %arg1) {
; CHECK-LABEL: define i32 @test_mismatched_operands(
-; CHECK-NEXT: %v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
-; CHECK-NEXT: %v1 = add nsw i32 %arg0, 1
-; CHECK-NEXT: %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 %arg1)
-; CHECK-NEXT: %v3 = sub i32 %v0, %v2
-; CHECK-NEXT: ret i32 %v3
+; CHECK-SAME: i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) {
+; CHECK-NEXT: [[V0:%.*]] = tail call i32 @llvm.smin.i32(i32 [[ARG0]], i32 [[ARG1]])
+; CHECK-NEXT: [[V1:%.*]] = add nsw i32 [[ARG0]], 1
+; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 [[ARG1]])
+; CHECK-NEXT: [[V3:%.*]] = sub i32 [[V0]], [[V2]]
+; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = tail call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1)
%v1 = add nsw i32 %arg0, 1
More information about the llvm-commits
mailing list