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 @@ -519,3 +519,116 @@ %res = mul i8 %and, %xor2 ; to increase the use count for the xor ret i8 %res } + +; 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: [[TMP1:%.*]] = xor i3 [[OR]], -1 +; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[NOT]] +; + %or = or i3 %b, %c + %and = and i3 %a, %c + %xor = xor i3 %and, %or + %not = xor i3 %xor, -1 + ret i3 %not +} + +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: [[TMP1:%.*]] = xor i3 [[OR]], -1 +; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[NOT]] +; + %or = or i3 %c, %b + %and = and i3 %a, %c + %xor = xor i3 %and, %or + %not = xor i3 %xor, -1 + ret i3 %not +} + +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: [[TMP1:%.*]] = xor i3 [[OR]], -1 +; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[NOT]] +; + %or = or i3 %c, %b + %and = and i3 %c, %a + %xor = xor i3 %and, %or + %not = xor i3 %xor, -1 + ret i3 %not +} + +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: [[TMP1:%.*]] = xor i3 [[OR]], -1 +; CHECK-NEXT: [[NOT:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[NOT]] +; + %or = or i3 %b, %c + %and = and i3 %c, %a + %xor = xor i3 %and, %or + %not = xor i3 %xor, -1 + ret i3 %not +} + +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: [[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 + %and = and <3 x i5> %a, %c + %xor = xor <3 x i5> %or, %and + %not = xor <3 x i5> %xor, + ret <3 x i5> %not +} + +define <3 x i5> @not_xor_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x i5> %c) { +; 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: [[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 + %and = and <3 x i5> %a, %c + %xor = xor <3 x i5> %or, %and + %not = xor <3 x i5> %xor, + ret <3 x i5> %not +} + +; negative test: 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:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]] +; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[AND]], [[OR]] +; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[XOR]], -1 +; CHECK-NEXT: call void @use3(i3 [[XOR]]) +; CHECK-NEXT: ret i3 [[NOT]] +; + %or = or i3 %b, %c + %and = and i3 %a, %c + %xor = xor i3 %and, %or + %not = xor i3 %xor, -1 + call void @use3(i3 %xor) + ret i3 %not +} + +declare void @use3(i3)