Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3471,6 +3471,10 @@ return nullptr; } +static bool hasCommonOperand(Value *A, Value *B, Value *C, Value *D) { + return A == C || A == D || B == C || B == D; +} + // Transform // ~(x ^ y) // into: @@ -3490,8 +3494,28 @@ // Ok, good. } else if (InstCombiner::isFreeToInvert(Y, Y->hasOneUse())) { std::swap(X, Y); - } else + } else { + 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; + } Value *NotX = Builder.CreateNot(X, X->getName() + ".not"); return BinaryOperator::CreateXor(NotX, Y, I.getName() + ".demorgan"); Index: llvm/test/Transforms/InstCombine/xor2.ll =================================================================== --- llvm/test/Transforms/InstCombine/xor2.ll +++ llvm/test/Transforms/InstCombine/xor2.ll @@ -520,14 +520,14 @@ 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], +; 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 @@ ; 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]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], +; 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 @@ 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 @@ 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], +; 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 @@ ; 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]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <3 x i5> [[OR]], +; 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 @@ 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:%.*]]