[llvm] [InstCombine] Fold (X ule ~Y) & (Y ugt ~X) to false (PR #178572)
Mohammad Moeini via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 28 18:57:56 PST 2026
https://github.com/mbm6448 created https://github.com/llvm/llvm-project/pull/178572
These conditions are contradictory:
- (X ule ~Y) means X + Y does not overflow
- (Y ugt ~X) means X + Y overflows
Both cannot be true simultaneously, so fold to false.
Alive2 proof: https://alive2.llvm.org/ce/z/7BfGRs
Fixes #141479
>From c75be8964c3428e37740e3c576987898d58ee412 Mon Sep 17 00:00:00 2001
From: Mohammad Moeini <mmoeini3 at gatech.edu>
Date: Wed, 28 Jan 2026 21:49:28 -0500
Subject: [PATCH] [InstCombine] Fold (X ule ~Y) & (Y ugt ~X) to false
These conditions are contradictory:
- (X ule ~Y) means X + Y does not overflow
- (Y ugt ~X) means X + Y overflows
Both cannot be true simultaneously, so fold to false.
Fixes #141479
---
.../InstCombine/InstCombineAndOrXor.cpp | 28 +++++++
.../contradictory-overflow-check.ll | 82 +++++++++++++++++++
2 files changed, 110 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/contradictory-overflow-check.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b23519fd9f77f..d727f88a71fda 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1150,6 +1150,29 @@ static Value *foldUnsignedUnderflowCheck(ICmpInst *ZeroICmp,
return nullptr;
}
+/// Fold (X ule ~Y) & (Y ugt ~X) -> false
+/// These conditions contradict: first says X+Y won't overflow,
+/// second says X+Y will overflow.
+static Value *foldContradictoryUnsignedOverflowCheck(ICmpInst *LHS,
+ ICmpInst *RHS,
+ bool IsAnd,
+ InstCombiner::BuilderTy &Builder) {
+ if (!IsAnd)
+ return nullptr;
+
+ CmpPredicate Pred1, Pred2;
+ Value *A, *B;
+
+ if (match(LHS, m_ICmp(Pred1, m_Value(B), m_Not(m_Value(A)))) &&
+ match(RHS, m_ICmp(Pred2, m_Specific(A), m_Not(m_Specific(B)))) &&
+ Pred1 == ICmpInst::ICMP_ULE &&
+ Pred2 == ICmpInst::ICMP_UGT) {
+ return ConstantInt::getFalse(LHS->getType());
+ }
+
+ return nullptr;
+}
+
struct IntPart {
Value *From;
unsigned StartBit;
@@ -3461,6 +3484,11 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
return X;
if (Value *X = foldUnsignedUnderflowCheck(RHS, LHS, IsAnd, Q, Builder))
return X;
+
+ if (Value *X = foldContradictoryUnsignedOverflowCheck(LHS, RHS, IsAnd, Builder))
+ return X;
+ if (Value *X = foldContradictoryUnsignedOverflowCheck(RHS, LHS, IsAnd, Builder))
+ return X;
}
// (icmp ne A, 0) | (icmp ne B, 0) --> (icmp ne (A|B), 0)
diff --git a/llvm/test/Transforms/InstCombine/contradictory-overflow-check.ll b/llvm/test/Transforms/InstCombine/contradictory-overflow-check.ll
new file mode 100644
index 0000000000000..4a925af3d6cef
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/contradictory-overflow-check.ll
@@ -0,0 +1,82 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; (B ule ~A) && (A ugt ~B) -> false
+; These conditions contradict each other.
+
+define i1 @test_basic(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_basic(
+; CHECK-NEXT: ret i1 false
+;
+ %nota = xor i32 %a, -1
+ %cmp1 = icmp ule i32 %b, %nota
+ %notb = xor i32 %b, -1
+ %cmp2 = icmp ugt i32 %a, %notb
+ %ret = and i1 %cmp1, %cmp2
+ ret i1 %ret
+}
+
+; Commuted: (A ugt ~B) && (B ule ~A) -> false
+define i1 @test_commuted(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_commuted(
+; CHECK-NEXT: ret i1 false
+;
+ %nota = xor i32 %a, -1
+ %notb = xor i32 %b, -1
+ %cmp1 = icmp ugt i32 %a, %notb
+ %cmp2 = icmp ule i32 %b, %nota
+ %ret = and i1 %cmp1, %cmp2
+ ret i1 %ret
+}
+
+; Different types: i64
+define i1 @test_i64(i64 %a, i64 %b) {
+; CHECK-LABEL: @test_i64(
+; CHECK-NEXT: ret i1 false
+;
+ %nota = xor i64 %a, -1
+ %cmp1 = icmp ule i64 %b, %nota
+ %notb = xor i64 %b, -1
+ %cmp2 = icmp ugt i64 %a, %notb
+ %ret = and i1 %cmp1, %cmp2
+ ret i1 %ret
+}
+
+; Different types: i16
+define i1 @test_i16(i16 %a, i16 %b) {
+; CHECK-LABEL: @test_i16(
+; CHECK-NEXT: ret i1 false
+;
+ %nota = xor i16 %a, -1
+ %cmp1 = icmp ule i16 %b, %nota
+ %notb = xor i16 %b, -1
+ %cmp2 = icmp ugt i16 %a, %notb
+ %ret = and i1 %cmp1, %cmp2
+ ret i1 %ret
+}
+
+; Negative test: OR instead of AND (should NOT fold)
+define i1 @test_negative_or(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_negative_or(
+; CHECK-NOT: ret i1 false
+;
+ %nota = xor i32 %a, -1
+ %cmp1 = icmp ule i32 %b, %nota
+ %notb = xor i32 %b, -1
+ %cmp2 = icmp ugt i32 %a, %notb
+ %ret = or i1 %cmp1, %cmp2
+ ret i1 %ret
+}
+
+; Negative test: wrong predicate (ult instead of ule)
+define i1 @test_negative_wrong_pred(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_negative_wrong_pred(
+; CHECK-NOT: ret i1 false
+;
+ %nota = xor i32 %a, -1
+ %cmp1 = icmp ult i32 %b, %nota
+ %notb = xor i32 %b, -1
+ %cmp2 = icmp ugt i32 %a, %notb
+ %ret = and i1 %cmp1, %cmp2
+ ret i1 %ret
+}
More information about the llvm-commits
mailing list