[llvm] Fold icmp eq A, 0 | icmp ult B, sub 0, A to overflow (PR #152762)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 8 10:15:42 PDT 2025
https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/152762
None
>From ee5c15448f2adefcff8ac70fe84bac92481d920b Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 8 Aug 2025 11:43:17 -0400
Subject: [PATCH 1/2] Pre-commit tests (NFC)
---
.../Transforms/InstCombine/neg-and-zero.ll | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/neg-and-zero.ll
diff --git a/llvm/test/Transforms/InstCombine/neg-and-zero.ll b/llvm/test/Transforms/InstCombine/neg-and-zero.ll
new file mode 100644
index 0000000000000..475e9a0c70190
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/neg-and-zero.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i32 @non_overflow(i32 %0, i32 %1) {
+; CHECK-LABEL: @non_overflow(
+; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], 0
+; CHECK-NEXT: [[TMP6:%.*]] = sub i32 0, [[TMP0]]
+; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[TMP1:%.*]], [[TMP6]]
+; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP3]], [[TMP7]]
+; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP4]] to i32
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = icmp eq i32 %0, 0
+ %4 = sub i32 0, %0
+ %5 = icmp ult i32 %1, %4
+ %6 = or i1 %3, %5
+ %7 = zext i1 %6 to i32
+ ret i32 %7
+}
+
+define i32 @overflow(i32 %0, i32 %1) {
+; CHECK-LABEL: @overflow(
+; CHECK-NEXT: [[TMP3:%.*]] = sub i32 0, [[TMP0:%.*]]
+; CHECK-NEXT: [[TMP6:%.*]] = icmp ne i32 [[TMP0]], 0
+; CHECK-NEXT: [[TMP7:%.*]] = icmp uge i32 [[TMP1:%.*]], [[TMP3]]
+; CHECK-NEXT: [[TMP4:%.*]] = and i1 [[TMP6]], [[TMP7]]
+; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP4]] to i32
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = sub i32 0, %0
+ %4 = icmp ne i32 %0, 0
+ %5 = icmp uge i32 %1, %3
+ %6 = and i1 %4, %5
+ %7 = zext i1 %6 to i32
+ ret i32 %7
+}
+
+
+define i32 @non_overflow_flipped(i32 %0, i32 %1) {
+; CHECK-LABEL: @non_overflow_flipped(
+; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i32 [[TMP0:%.*]], 0
+; CHECK-NEXT: [[TMP3:%.*]] = sub i32 0, [[TMP0]]
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i32 [[TMP1:%.*]], [[TMP3]]
+; CHECK-NEXT: [[TMP6:%.*]] = or i1 [[TMP7]], [[TMP4]]
+; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP6]] to i32
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = icmp eq i32 %0, 0
+ %4 = sub i32 0, %0
+ %5 = icmp uge i32 %4, %1
+ %6 = or i1 %3, %5
+ %7 = zext i1 %6 to i32
+ ret i32 %7
+}
+
+define i32 @overflow_flipped(i32 %0, i32 %1) {
+; CHECK-LABEL: @overflow_flipped(
+; CHECK-NEXT: [[TMP3:%.*]] = sub i32 0, [[TMP0:%.*]]
+; CHECK-NEXT: [[TMP7:%.*]] = icmp ne i32 [[TMP0]], 0
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ugt i32 [[TMP1:%.*]], [[TMP3]]
+; CHECK-NEXT: [[TMP6:%.*]] = and i1 [[TMP7]], [[TMP4]]
+; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP6]] to i32
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ %3 = sub i32 0, %0
+ %4 = icmp ne i32 %0, 0
+ %5 = icmp ult i32 %3, %1
+ %6 = and i1 %4, %5
+ %7 = zext i1 %6 to i32
+ ret i32 %7
+}
>From 24f1209fd475cb8f4fadff322c4029e58017eb91 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 8 Aug 2025 13:14:44 -0400
Subject: [PATCH 2/2] Fold icmp eq A, 0 | icmp ult B, sub 0, A to overflow
---
.../InstCombine/InstCombineAndOrXor.cpp | 33 +++++++++++++++++++
.../Transforms/InstCombine/neg-and-zero.ll | 12 +++----
2 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index d7971e8e3caea..17ebc1f34740c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3359,6 +3359,39 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
}
}
+ // Canonicalize the unsigned-add overflow/non-overflow boolean patterns:
+ // (icmp eq A, 0) | (icmp ult B, sub 0, A) --> icmp ule B, ~A
+ // (icmp ne A, 0) & (icmp uge B, sub 0, A) --> icmp ugt B, ~A
+ // Accept commuted order of the two icmps within the or/and, and
+ // commuted operand order within the ULT/UGE compare.
+ {
+ Value *A, *B;
+ // Try (LHS,RHS) order
+ if (PredL == (IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ) &&
+ match(LHS0, m_Value(A)) && match(LHS1, m_Zero()) &&
+ ((PredR == (IsAnd ? ICmpInst::ICMP_UGE : ICmpInst::ICMP_ULT) &&
+ ((match(RHS0, m_Value(B)) && match(RHS1, m_Neg(m_Specific(A)))) ||
+ (match(RHS1, m_Value(B)) && match(RHS0, m_Neg(m_Specific(A)))))))) {
+ if (IsLogical && !isGuaranteedNotToBePoison(B))
+ return nullptr;
+ Value *NotA = Builder.CreateNot(A);
+ return Builder.CreateICmp(IsAnd ? ICmpInst::ICMP_UGT : ICmpInst::ICMP_ULE,
+ B, NotA);
+ }
+ // Try (RHS,LHS) order
+ if (PredR == (IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ) &&
+ match(RHS0, m_Value(A)) && match(RHS1, m_Zero()) &&
+ ((PredL == (IsAnd ? ICmpInst::ICMP_UGE : ICmpInst::ICMP_ULT) &&
+ ((match(LHS0, m_Value(B)) && match(LHS1, m_Neg(m_Specific(A)))) ||
+ (match(LHS1, m_Value(B)) && match(LHS0, m_Neg(m_Specific(A)))))))) {
+ if (IsLogical && !isGuaranteedNotToBePoison(B))
+ return nullptr;
+ Value *NotA = Builder.CreateNot(A);
+ return Builder.CreateICmp(IsAnd ? ICmpInst::ICMP_UGT : ICmpInst::ICMP_ULE,
+ B, NotA);
+ }
+ }
+
if (Value *V =
foldAndOrOfICmpEqConstantAndICmp(LHS, RHS, IsAnd, IsLogical, Builder))
return V;
diff --git a/llvm/test/Transforms/InstCombine/neg-and-zero.ll b/llvm/test/Transforms/InstCombine/neg-and-zero.ll
index 475e9a0c70190..80db22aad1dde 100644
--- a/llvm/test/Transforms/InstCombine/neg-and-zero.ll
+++ b/llvm/test/Transforms/InstCombine/neg-and-zero.ll
@@ -3,10 +3,8 @@
define i32 @non_overflow(i32 %0, i32 %1) {
; CHECK-LABEL: @non_overflow(
-; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], 0
-; CHECK-NEXT: [[TMP6:%.*]] = sub i32 0, [[TMP0]]
-; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[TMP1:%.*]], [[TMP6]]
-; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP3]], [[TMP7]]
+; CHECK-NEXT: [[TMP3:%.*]] = xor i32 [[TMP0:%.*]], -1
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i32 [[TMP1:%.*]], [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP4]] to i32
; CHECK-NEXT: ret i32 [[TMP5]]
;
@@ -20,10 +18,8 @@ define i32 @non_overflow(i32 %0, i32 %1) {
define i32 @overflow(i32 %0, i32 %1) {
; CHECK-LABEL: @overflow(
-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 0, [[TMP0:%.*]]
-; CHECK-NEXT: [[TMP6:%.*]] = icmp ne i32 [[TMP0]], 0
-; CHECK-NEXT: [[TMP7:%.*]] = icmp uge i32 [[TMP1:%.*]], [[TMP3]]
-; CHECK-NEXT: [[TMP4:%.*]] = and i1 [[TMP6]], [[TMP7]]
+; CHECK-NEXT: [[TMP3:%.*]] = xor i32 [[TMP0:%.*]], -1
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ugt i32 [[TMP1:%.*]], [[TMP3]]
; CHECK-NEXT: [[TMP5:%.*]] = zext i1 [[TMP4]] to i32
; CHECK-NEXT: ret i32 [[TMP5]]
;
More information about the llvm-commits
mailing list