Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -3816,6 +3816,16 @@ m_c_And(m_Deferred(M), m_Value())))) return BinaryOperator::CreateOr(Op0, Op1); + Value *AndVal, *OrVal; + Value *A, *B, *C, *D; + // Canonicalize ~(A & B) ^ (A | ?) -> (A & B) | ~(A | ?) + // 8 commuted variants + if (match(&I, m_c_Xor(m_OneUse(m_Not(m_Value(AndVal))), m_Value(OrVal))) && + match(AndVal, m_And(m_Value(A), m_Value(B))) && + match(OrVal, m_Or(m_Value(C), m_Value(D))) && + hasCommonOperand(A, B, C, D)) + return BinaryOperator::CreateOr(AndVal, Builder.CreateNot(OrVal)); + if (Instruction *Xor = visitMaskedMerge(I, Builder)) return Xor; @@ -3960,7 +3970,6 @@ match(Op0, m_OneUse(m_c_And(m_Value(X), m_Specific(Op1))))) return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(X)); - Value *A, *B, *C; // (A ^ B) ^ (A | C) --> (~A & C) ^ B -- There are 4 commuted variants. if (match(&I, m_c_Xor(m_OneUse(m_Xor(m_Value(A), m_Value(B))), m_OneUse(m_c_Or(m_Deferred(A), m_Value(C)))))) @@ -4002,7 +4011,6 @@ // (A | B) ^ (A | C) --> (B ^ C) & ~A -- There are 4 commuted variants. // TODO: Loosen one-use restriction if common operand is a constant. - Value *D; if (match(Op0, m_OneUse(m_Or(m_Value(A), m_Value(B)))) && match(Op1, m_OneUse(m_Or(m_Value(C), m_Value(D))))) { if (B == C || B == D) Index: llvm/test/Transforms/InstCombine/xor2.ll =================================================================== --- llvm/test/Transforms/InstCombine/xor2.ll +++ llvm/test/Transforms/InstCombine/xor2.ll @@ -631,4 +631,115 @@ ret i3 %not } +; 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 [[OR]], -1 +; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[XOR]] +; + %or = or i3 %b, %c + %and = and i3 %a, %c + %not = xor i3 %and, -1 + %xor = xor i3 %not, %or + ret i3 %xor +} + +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 [[OR]], -1 +; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[XOR]] +; + %or = or i3 %c, %b + %and = and i3 %a, %c + %not = xor i3 %and, -1 + %xor = xor i3 %not, %or + ret i3 %xor +} + +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 [[OR]], -1 +; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[XOR]] +; + %or = or i3 %c, %b + %and = and i3 %c, %a + %not = xor i3 %and, -1 + %xor = xor i3 %not, %or + ret i3 %xor +} + +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 [[OR]], -1 +; CHECK-NEXT: [[XOR:%.*]] = or i3 [[AND]], [[TMP1]] +; CHECK-NEXT: ret i3 [[XOR]] +; + %or = or i3 %b, %c + %and = and i3 %c, %a + %not = xor i3 %and, -1 + %xor = xor i3 %not, %or + ret i3 %xor +} + +define <3 x i5> @xor_notand_to_or_not_vector(<3 x i5> %a, <3 x i5> %b, <3 x i5> %c) { +; 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> [[OR]], +; CHECK-NEXT: [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]] +; CHECK-NEXT: ret <3 x i5> [[XOR]] +; + %or = or <3 x i5> %b, %c + %and = and <3 x i5> %a, %c + %not = xor <3 x i5> %and, + %xor = xor <3 x i5> %not, %or + ret <3 x i5> %xor +} + +define <3 x i5> @xor_notand_to_or_not_vector_poison(<3 x i5> %a, <3 x i5> %b, <3 x i5> %c) { +; 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> [[OR]], +; CHECK-NEXT: [[XOR:%.*]] = or <3 x i5> [[AND]], [[TMP1]] +; CHECK-NEXT: ret <3 x i5> [[XOR]] +; + %or = or <3 x i5> %b, %c + %and = and <3 x i5> %a, %c + %not = xor <3 x i5> %and, + %xor = xor <3 x i5> %not, %or + ret <3 x i5> %xor +} + +; negative test: 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:%.*]] +; CHECK-NEXT: [[AND:%.*]] = and i3 [[A:%.*]], [[C]] +; CHECK-NEXT: [[NOT:%.*]] = xor i3 [[AND]], -1 +; CHECK-NEXT: [[XOR:%.*]] = xor i3 [[OR]], [[NOT]] +; CHECK-NEXT: call void @use3(i3 [[NOT]]) +; CHECK-NEXT: ret i3 [[XOR]] +; + %or = or i3 %b, %c + %and = and i3 %a, %c + %not = xor i3 %and, -1 + %xor = xor i3 %not, %or + call void @use3(i3 %not) + ret i3 %xor +} + declare void @use3(i3)