[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