From 0529946b5bafafd10d77b946ee9fa96f388860ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= Date: Tue, 12 Jan 2021 19:28:01 +0100 Subject: [PATCH] [instCombine] Add (A ^ B) | ~(A | B) -> ~(A & B) define i32 @src(i32 %x, i32 %y) { %0: %xor = xor i32 %y, %x %or = or i32 %y, %x %neg = xor i32 %or, 4294967295 %or1 = or i32 %xor, %neg ret i32 %or1 } => define i32 @tgt(i32 %x, i32 %y) { %0: %and = and i32 %x, %y %neg = xor i32 %and, 4294967295 ret i32 %neg } Transformation seems to be correct! https://alive2.llvm.org/ce/z/Cvca4a --- .../InstCombine/InstCombineAndOrXor.cpp | 8 ++++ llvm/test/Transforms/InstCombine/or.ll | 39 +++++++------------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index 15dcf2d19c15..352126fa07ca 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1627,6 +1627,14 @@ static Instruction *foldOrToXor(BinaryOperator &I, match(Op1, m_Not(m_c_Or(m_Specific(A), m_Specific(B))))) return BinaryOperator::CreateNot(Builder.CreateXor(A, B)); + // Operand complexity canonicalization guarantees that the 'xor' is Op0. + // (A ^ B) | ~(A | B) --> ~(A & B) + // (A ^ B) | ~(B | A) --> ~(A & B) + if (Op0->hasOneUse() || Op1->hasOneUse()) + if (match(Op0, m_Xor(m_Value(A), m_Value(B))) && + match(Op1, m_Not(m_c_Or(m_Specific(A), m_Specific(B))))) + return BinaryOperator::CreateNot(Builder.CreateAnd(A, B)); + // (A & ~B) | (~A & B) --> A ^ B // (A & ~B) | (B & ~A) --> A ^ B // (~B & A) | (~A & B) --> A ^ B diff --git a/llvm/test/Transforms/InstCombine/or.ll b/llvm/test/Transforms/InstCombine/or.ll index d41b8d53dd40..b5da1734c102 100644 --- a/llvm/test/Transforms/InstCombine/or.ll +++ b/llvm/test/Transforms/InstCombine/or.ll @@ -1004,10 +1004,8 @@ end: define i32 @test1(i32 %x, i32 %y) { ; CHECK-LABEL: @test1( -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y]], [[X]] -; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[OR]], -1 -; CHECK-NEXT: [[OR1:%.*]] = or i32 [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[OR1:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[OR1]] ; %xor = xor i32 %y, %x @@ -1019,13 +1017,11 @@ define i32 @test1(i32 %x, i32 %y) { define i32 @test2(i32 %x, i32 %y) { ; CHECK-LABEL: @test2( -; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[OR]], -1 -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y]], [[X]] -; CHECK-NEXT: [[OR1:%.*]] = or i32 [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[OR1:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[OR1]] ; - %or = or i32 %y, %x + %or = or i32 %x, %y %neg = xor i32 %or, -1 %xor = xor i32 %y, %x %or1 = or i32 %xor, %neg @@ -1034,25 +1030,21 @@ define i32 @test2(i32 %x, i32 %y) { define i32 @test3(i32 %x, i32 %y) { ; CHECK-LABEL: @test3( -; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[OR]], -1 -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y]], [[X]] -; CHECK-NEXT: [[OR1:%.*]] = or i32 [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[OR1:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[OR1]] ; %or = or i32 %y, %x %neg = xor i32 %or, -1 - %xor = xor i32 %y, %x + %xor = xor i32 %x, %y %or1 = or i32 %xor, %neg ret i32 %or1 } define <2 x i32> @test4_vec(<2 x i32> %x, <2 x i32> %y) { ; CHECK-LABEL: @test4_vec( -; CHECK-NEXT: [[OR:%.*]] = or <2 x i32> [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[NEG:%.*]] = xor <2 x i32> [[OR]], -; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i32> [[Y]], [[X]] -; CHECK-NEXT: [[OR1:%.*]] = or <2 x i32> [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i32> [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[OR1:%.*]] = xor <2 x i32> [[TMP1]], ; CHECK-NEXT: ret <2 x i32> [[OR1]] ; %or = or <2 x i32> %y, %x @@ -1066,9 +1058,9 @@ define i32 @test5_use(i32 %x, i32 %y) { ; CHECK-LABEL: @test5_use( ; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], [[X:%.*]] ; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[OR]], -1 -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y]], [[X]] ; CHECK-NEXT: call void @use(i32 [[NEG]]) -; CHECK-NEXT: [[OR1:%.*]] = or i32 [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[Y]], [[X]] +; CHECK-NEXT: [[OR1:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[OR1]] ; %or = or i32 %y, %x @@ -1081,11 +1073,10 @@ define i32 @test5_use(i32 %x, i32 %y) { define i32 @test5_use2(i32 %x, i32 %y) { ; CHECK-LABEL: @test5_use2( -; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[NEG:%.*]] = xor i32 [[OR]], -1 -; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y]], [[X]] +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[Y:%.*]], [[X:%.*]] ; CHECK-NEXT: call void @use(i32 [[XOR]]) -; CHECK-NEXT: [[OR1:%.*]] = or i32 [[XOR]], [[NEG]] +; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[Y]], [[X]] +; CHECK-NEXT: [[OR1:%.*]] = xor i32 [[TMP1]], -1 ; CHECK-NEXT: ret i32 [[OR1]] ; %or = or i32 %y, %x