[llvm] 10c3df7 - [Instcombine] Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 7 07:52:16 PST 2022
Author: chenglin.bi
Date: 2022-12-07T23:52:07+08:00
New Revision: 10c3df728cb6d445b6ab9e2d62487e4ecf55693b
URL: https://github.com/llvm/llvm-project/commit/10c3df728cb6d445b6ab9e2d62487e4ecf55693b
DIFF: https://github.com/llvm/llvm-project/commit/10c3df728cb6d445b6ab9e2d62487e4ecf55693b.diff
LOG: [Instcombine] Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
https://alive2.llvm.org/ce/z/JHN2p4
Reviewed By: spatel
Differential Revision: https://reviews.llvm.org/D139299
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
llvm/test/Transforms/InstCombine/xor2.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 032021b5806b5..5fc8fe9acbfda 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3477,14 +3477,8 @@ static Instruction *visitMaskedMerge(BinaryOperator &I,
// (~x) ^ y
// or into
// x ^ (~y)
-static Instruction *sinkNotIntoXor(BinaryOperator &I,
+static Instruction *sinkNotIntoXor(BinaryOperator &I, Value *X, Value *Y,
InstCombiner::BuilderTy &Builder) {
- Value *X, *Y;
- // FIXME: one-use check is not needed in general, but currently we are unable
- // to fold 'not' into 'icmp', if that 'icmp' has multiple uses. (D35182)
- if (!match(&I, m_Not(m_OneUse(m_Xor(m_Value(X), m_Value(Y))))))
- return nullptr;
-
// We only want to do the transform if it is free to do.
if (InstCombiner::isFreeToInvert(X, X->hasOneUse())) {
// Ok, good.
@@ -3497,6 +3491,41 @@ static Instruction *sinkNotIntoXor(BinaryOperator &I,
return BinaryOperator::CreateXor(NotX, Y, I.getName() + ".demorgan");
}
+static Instruction *foldNotXor(BinaryOperator &I,
+ InstCombiner::BuilderTy &Builder) {
+ Value *X, *Y;
+ // FIXME: one-use check is not needed in general, but currently we are unable
+ // to fold 'not' into 'icmp', if that 'icmp' has multiple uses. (D35182)
+ if (!match(&I, m_Not(m_OneUse(m_Xor(m_Value(X), m_Value(Y))))))
+ return nullptr;
+
+ if (Instruction *NewXor = sinkNotIntoXor(I, X, Y, Builder))
+ return NewXor;
+
+ auto hasCommonOperand = [](Value *A, Value *B, Value *C, Value *D) {
+ return A == C || A == D || B == C || B == D;
+ };
+
+ Value *A, *B, *C, *D;
+ // Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
+ // 4 commuted variants
+ if (match(X, m_And(m_Value(A), m_Value(B))) &&
+ match(Y, m_Or(m_Value(C), m_Value(D))) && hasCommonOperand(A, B, C, D)) {
+ Value *NotY = Builder.CreateNot(Y);
+ return BinaryOperator::CreateOr(X, NotY);
+ };
+
+ // Canonicalize ~((A | ?) ^ (A & B)) -> (A & B) | ~(A | ?)
+ // 4 commuted variants
+ if (match(Y, m_And(m_Value(A), m_Value(B))) &&
+ match(X, m_Or(m_Value(C), m_Value(D))) && hasCommonOperand(A, B, C, D)) {
+ Value *NotX = Builder.CreateNot(X);
+ return BinaryOperator::CreateOr(Y, NotX);
+ };
+
+ return nullptr;
+}
+
/// Canonicalize a shifty way to code absolute value to the more common pattern
/// that uses negation and select.
static Instruction *canonicalizeAbs(BinaryOperator &Xor,
@@ -3741,7 +3770,7 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
}
}
- if (Instruction *NewXor = sinkNotIntoXor(I, Builder))
+ if (Instruction *NewXor = foldNotXor(I, Builder))
return NewXor;
return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/xor2.ll b/llvm/test/Transforms/InstCombine/xor2.ll
index 3871e1f1fbe4e..06fd740954b99 100644
--- a/llvm/test/Transforms/InstCombine/xor2.ll
+++ b/llvm/test/Transforms/InstCombine/xor2.ll
@@ -520,14 +520,14 @@ define i8 @test16(i8 %A, i8 %B) {
ret i8 %res
}
-; TODO: Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
+; Canonicalize ~((A & B) ^ (A | ?)) -> (A & B) | ~(A | ?)
define i3 @not_xor_to_or_not1(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @not_xor_to_or_not1(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[NOT]]
;
%or = or i3 %b, %c
@@ -541,8 +541,8 @@ define i3 @not_xor_to_or_not2(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @not_xor_to_or_not2(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[NOT]]
;
%or = or i3 %c, %b
@@ -556,8 +556,8 @@ define i3 @not_xor_to_or_not3(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @not_xor_to_or_not3(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[NOT]]
;
%or = or i3 %c, %b
@@ -571,8 +571,8 @@ define i3 @not_xor_to_or_not4(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @not_xor_to_or_not4(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[XOR]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[NOT]]
;
%or = or i3 %b, %c
@@ -586,8 +586,8 @@ define <3 x i5> @not_xor_to_or_not_vector(<3 x i5> %a, <3 x i5> %b, <3 x i5> %c)
; CHECK-LABEL: @not_xor_to_or_not_vector(
; CHECK-NEXT: [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT: [[XOR:%.*]] = xor <3 x i5> [[OR]], [[AND]]
-; CHECK-NEXT: [[NOT:%.*]] = xor <3 x i5> [[XOR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[NOT:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
; CHECK-NEXT: ret <3 x i5> [[NOT]]
;
%or = or <3 x i5> %b, %c
@@ -601,8 +601,8 @@ define <3 x i5> @not_xor_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x
; CHECK-LABEL: @not_xor_to_or_not_vector_poison(
; CHECK-NEXT: [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT: [[XOR:%.*]] = xor <3 x i5> [[OR]], [[AND]]
-; CHECK-NEXT: [[NOT:%.*]] = xor <3 x i5> [[XOR]], <i5 poison, i5 -1, i5 -1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[NOT:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
; CHECK-NEXT: ret <3 x i5> [[NOT]]
;
%or = or <3 x i5> %b, %c
@@ -612,6 +612,8 @@ define <3 x i5> @not_xor_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x
ret <3 x i5> %not
}
+; negative test : not one use
+
define i3 @not_xor_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @not_xor_to_or_not_2use(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
@@ -629,14 +631,14 @@ define i3 @not_xor_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
ret i3 %not
}
-; TODO: Canonicalize ~(A & B) ^ (A | ?) -> (A & B) | ~(A | ?)
+; Canonicalize ~(A & B) ^ (A | ?) -> (A & B) | ~(A | ?)
define i3 @xor_notand_to_or_not1(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @xor_notand_to_or_not1(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[XOR]]
;
%or = or i3 %b, %c
@@ -650,8 +652,8 @@ define i3 @xor_notand_to_or_not2(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @xor_notand_to_or_not2(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[XOR]]
;
%or = or i3 %c, %b
@@ -665,8 +667,8 @@ define i3 @xor_notand_to_or_not3(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @xor_notand_to_or_not3(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[C:%.*]], [[B:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[XOR]]
;
%or = or i3 %c, %b
@@ -680,8 +682,8 @@ define i3 @xor_notand_to_or_not4(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @xor_notand_to_or_not4(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and i3 [[C]], [[A:%.*]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[TMP1]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = xor i3 [[OR]], -1
+; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]]
; CHECK-NEXT: ret i3 [[XOR]]
;
%or = or i3 %b, %c
@@ -695,8 +697,8 @@ define <3 x i5> @xor_notand_to_or_not_vector(<3 x i5> %a, <3 x i5> %b, <3 x i5>
; CHECK-LABEL: @xor_notand_to_or_not_vector(
; CHECK-NEXT: [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor <3 x i5> [[TMP1]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
; CHECK-NEXT: ret <3 x i5> [[XOR]]
;
%or = or <3 x i5> %b, %c
@@ -710,8 +712,8 @@ define <3 x i5> @xor_notand_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3
; CHECK-LABEL: @xor_notand_to_or_not_vector_poison(
; CHECK-NEXT: [[OR:%.*]] = or <3 x i5> [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <3 x i5> [[A:%.*]], [[C]]
-; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[AND]], [[OR]]
-; CHECK-NEXT: [[XOR:%.*]] = xor <3 x i5> [[TMP1]], <i5 -1, i5 poison, i5 -1>
+; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], <i5 -1, i5 -1, i5 -1>
+; CHECK-NEXT: [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]]
; CHECK-NEXT: ret <3 x i5> [[XOR]]
;
%or = or <3 x i5> %b, %c
@@ -721,6 +723,8 @@ define <3 x i5> @xor_notand_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3
ret <3 x i5> %xor
}
+; negative test : not one use
+
define i3 @xor_notand_to_or_not_2use(i3 %a, i3 %b, i3 %c) {
; CHECK-LABEL: @xor_notand_to_or_not_2use(
; CHECK-NEXT: [[OR:%.*]] = or i3 [[B:%.*]], [[C:%.*]]
More information about the llvm-commits
mailing list