[llvm] [InstCombine] Turn (A | B) != 0 & (A == 0 | B == 0) -> (A == 0) ^ (B == 0) (PR #207257)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 2 13:49:58 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/207257

>From 0e6370e0d60914726a7c927ba610c9518f760862 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Thu, 2 Jul 2026 15:04:44 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)

---
 .../InstCombine/logical-xor-of-icmps.ll       | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll

diff --git a/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll b/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll
new file mode 100644
index 0000000000000..c35c3f7b16f23
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @logical_xor_of_icmps(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps(
+; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
+; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP2]], [[CMP3]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[OR_CMP23]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %or_ab = or i32 %a, %b
+  %cmp1 = icmp ne i32 %or_ab, 0
+  %cmp2 = icmp eq i32 %a, 0
+  %cmp3 = icmp eq i32 %b, 0
+  %or_cmp23 = or i1 %cmp2, %cmp3
+  %and = and i1 %cmp1, %or_cmp23
+  ret i1 %and
+}
+
+define i1 @logical_xor_of_icmps_swapped_and(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps_swapped_and(
+; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
+; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP2]], [[CMP3]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[OR_CMP23]], [[CMP1]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %or_ab = or i32 %a, %b
+  %cmp1 = icmp ne i32 %or_ab, 0
+  %cmp2 = icmp eq i32 %a, 0
+  %cmp3 = icmp eq i32 %b, 0
+  %or_cmp23 = or i1 %cmp2, %cmp3
+  %and = and i1 %or_cmp23, %cmp1
+  ret i1 %and
+}
+
+define i1 @logical_xor_of_icmps_swapped_or(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps_swapped_or(
+; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
+; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP3]], [[CMP2]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[OR_CMP23]]
+; CHECK-NEXT:    ret i1 [[AND]]
+;
+  %or_ab = or i32 %b, %a
+  %cmp1 = icmp ne i32 %or_ab, 0
+  %cmp2 = icmp eq i32 %a, 0
+  %cmp3 = icmp eq i32 %b, 0
+  %or_cmp23 = or i1 %cmp3, %cmp2
+  %and = and i1 %cmp1, %or_cmp23
+  ret i1 %and
+}

>From cc082ef8d38bdee2898e0ab12b4f49b4d2a1a37b Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Thu, 2 Jul 2026 15:09:58 -0400
Subject: [PATCH 2/2] Turn (A | B) != 0 & (A == 0 | B == 0) -> (A == 0) ^ (B ==
 0)

This is the (a || b) && !(a && b) in C.

Alive2: https://alive2.llvm.org/ce/z/8sR8mS
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 31 ++++++++
 .../InstCombine/logical-xor-of-icmps.ll       | 75 ++++++++++++++-----
 2 files changed, 88 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b6f4a55c07e8a..89738babb1b62 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1940,6 +1940,21 @@ static Instruction *foldAndToXor(BinaryOperator &I,
                           m_c_Or(m_Not(m_Deferred(A)), m_Deferred(B)))))
       return BinaryOperator::CreateNot(Builder.CreateXor(A, B));
 
+  // (A | B) != 0 & (A == 0 | B == 0) -> (A == 0) ^ (B == 0)
+  // This is effectively (A | B) & ~(A & B) where A and B are conditions.
+  Value *CmpA, *CmpB;
+  if (match(&I,
+            m_c_And(
+                m_SpecificICmp(ICmpInst::ICMP_NE, m_Or(m_Value(A), m_Value(B)),
+                               m_Zero()),
+                m_c_Or(m_CombineAnd(m_Value(CmpA),
+                                    m_SpecificICmp(ICmpInst::ICMP_EQ,
+                                                   m_Deferred(A), m_Zero())),
+                       m_CombineAnd(m_Value(CmpB),
+                                    m_SpecificICmp(ICmpInst::ICMP_EQ,
+                                                   m_Deferred(B), m_Zero()))))))
+    return BinaryOperator::CreateXor(CmpA, CmpB);
+
   return nullptr;
 }
 
@@ -1974,6 +1989,22 @@ static Instruction *foldOrToXor(BinaryOperator &I,
       match(Op1, m_c_And(m_Not(m_Specific(A)), m_Specific(B))))
     return BinaryOperator::CreateXor(A, B);
 
+  // (A | B) == 0 | (A != 0 & B != 0) -> (A != 0) == (B != 0) -> ~(A != 0 ^ B !=
+  // 0) This is effectively (A | B) == 0 | ~(A == 0 | B == 0) where A and B are
+  // conditions.
+  Value *CmpA, *CmpB;
+  if (match(&I,
+            m_c_Or(m_SpecificICmp(ICmpInst::ICMP_EQ,
+                                  m_Or(m_Value(A), m_Value(B)), m_Zero()),
+                   m_c_And(
+                       m_CombineAnd(m_Value(CmpA),
+                                    m_SpecificICmp(ICmpInst::ICMP_NE,
+                                                   m_Deferred(A), m_Zero())),
+                       m_CombineAnd(m_Value(CmpB),
+                                    m_SpecificICmp(ICmpInst::ICMP_NE,
+                                                   m_Deferred(B), m_Zero()))))))
+    return BinaryOperator::CreateNot(Builder.CreateXor(CmpA, CmpB));
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll b/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll
index c35c3f7b16f23..7a6dd558171cc 100644
--- a/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/logical-xor-of-icmps.ll
@@ -3,12 +3,9 @@
 
 define i1 @logical_xor_of_icmps(i32 %a, i32 %b) {
 ; CHECK-LABEL: @logical_xor_of_icmps(
-; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
-; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP2]], [[CMP3]]
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[OR_CMP23]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[AND:%.*]] = xor i1 [[CMP2]], [[CMP3]]
 ; CHECK-NEXT:    ret i1 [[AND]]
 ;
   %or_ab = or i32 %a, %b
@@ -22,12 +19,9 @@ define i1 @logical_xor_of_icmps(i32 %a, i32 %b) {
 
 define i1 @logical_xor_of_icmps_swapped_and(i32 %a, i32 %b) {
 ; CHECK-LABEL: @logical_xor_of_icmps_swapped_and(
-; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[A:%.*]], [[B:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
-; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP2]], [[CMP3]]
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[OR_CMP23]], [[CMP1]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[AND:%.*]] = xor i1 [[CMP2]], [[CMP3]]
 ; CHECK-NEXT:    ret i1 [[AND]]
 ;
   %or_ab = or i32 %a, %b
@@ -41,12 +35,9 @@ define i1 @logical_xor_of_icmps_swapped_and(i32 %a, i32 %b) {
 
 define i1 @logical_xor_of_icmps_swapped_or(i32 %a, i32 %b) {
 ; CHECK-LABEL: @logical_xor_of_icmps_swapped_or(
-; CHECK-NEXT:    [[OR_AB:%.*]] = or i32 [[B:%.*]], [[A:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[OR_AB]], 0
-; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A]], 0
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B]], 0
-; CHECK-NEXT:    [[OR_CMP23:%.*]] = or i1 [[CMP3]], [[CMP2]]
-; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP1]], [[OR_CMP23]]
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[AND:%.*]] = xor i1 [[CMP3]], [[CMP2]]
 ; CHECK-NEXT:    ret i1 [[AND]]
 ;
   %or_ab = or i32 %b, %a
@@ -57,3 +48,51 @@ define i1 @logical_xor_of_icmps_swapped_or(i32 %a, i32 %b) {
   %and = and i1 %cmp1, %or_cmp23
   ret i1 %and
 }
+
+define i1 @logical_xor_of_icmps_demorgan(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps_demorgan(
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[OR:%.*]] = xor i1 [[CMP2]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %or_ab = or i32 %a, %b
+  %cmp1 = icmp eq i32 %or_ab, 0
+  %cmp2 = icmp ne i32 %a, 0
+  %cmp3 = icmp ne i32 %b, 0
+  %and23 = and i1 %cmp2, %cmp3
+  %or = or i1 %cmp1, %and23
+  ret i1 %or
+}
+
+define i1 @logical_xor_of_icmps_demorgan_swapped_or(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps_demorgan_swapped_or(
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[OR:%.*]] = xor i1 [[CMP2]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %or_ab = or i32 %a, %b
+  %cmp1 = icmp eq i32 %or_ab, 0
+  %cmp2 = icmp ne i32 %a, 0
+  %cmp3 = icmp ne i32 %b, 0
+  %and23 = and i1 %cmp2, %cmp3
+  %or = or i1 %and23, %cmp1
+  ret i1 %or
+}
+
+define i1 @logical_xor_of_icmps_demorgan_swapped_and(i32 %a, i32 %b) {
+; CHECK-LABEL: @logical_xor_of_icmps_demorgan_swapped_and(
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne i32 [[B:%.*]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[A:%.*]], 0
+; CHECK-NEXT:    [[OR:%.*]] = xor i1 [[CMP3]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[OR]]
+;
+  %or_ab = or i32 %b, %a
+  %cmp1 = icmp eq i32 %or_ab, 0
+  %cmp2 = icmp ne i32 %a, 0
+  %cmp3 = icmp ne i32 %b, 0
+  %and23 = and i1 %cmp3, %cmp2
+  %or = or i1 %cmp1, %and23
+  ret i1 %or
+}



More information about the llvm-commits mailing list