[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:39:10 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

https://alive2.llvm.org/ce/z/-4HvMR

---
Full diff: https://github.com/llvm/llvm-project/pull/152762.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+33) 
- (added) llvm/test/Transforms/InstCombine/neg-and-zero.ll (+63) 


``````````diff
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
new file mode 100644
index 0000000000000..bb96a865a656a
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/neg-and-zero.ll
@@ -0,0 +1,63 @@
+; 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:%.*]] = 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]]
+;
+  %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:%.*]] = 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]]
+;
+  %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:    [[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]]
+;
+  %3 = icmp eq i32 %0, 0
+  %4 = sub i32 0, %0
+  %5 = icmp ugt 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:%.*]] = 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]]
+;
+  %3 = sub i32 0, %0
+  %4 = icmp ne i32 %0, 0
+  %5 = icmp ule i32 %3, %1
+  %6 = and i1 %4, %5
+  %7 = zext i1 %6 to i32
+  ret i32 %7
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/152762


More information about the llvm-commits mailing list