diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index ba5568b00441b..821086879339c 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1982,6 +1982,11 @@ static Instruction *foldOrToXor(BinaryOperator &I, match(Op1, m_c_And(m_Not(m_Specific(A)), m_Specific(B)))) return BinaryOperator::CreateXor(A, B); + // (A & ~B) | (A ^ B) -> A ^ B + if (match(Op0, m_c_And(m_Value(A), m_Not(m_Value(B)))) && + match(Op1, m_c_Xor(m_Specific(A), m_Specific(B)))) + return replaceInstUsesWith(I, Op1); + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/or-xor-fold.ll b/llvm/test/Transforms/InstCombine/or-xor-fold.ll new file mode 100644 index 0000000000000..57797a33335b8 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/or-xor-fold.ll @@ -0,0 +1,58 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @use(i32) + +; (A & ~B) | (A ^ B) -> A ^ B + +define i32 @test_basic(i32 %a, i32 %b) { +; CHECK-LABEL: @test_basic( +; CHECK-NEXT: [[RES:%.*]] = xor i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; + %not_b = xor i32 %b, -1 + %and = and i32 %a, %not_b + %xor = xor i32 %a, %b + %res = or i32 %and, %xor + ret i32 %res +} + +define <2 x i32> @test_vector(<2 x i32> %a, <2 x i32> %b) { +; CHECK-LABEL: @test_vector( +; CHECK-NEXT: [[RES:%.*]] = xor <2 x i32> [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret <2 x i32> [[RES]] +; + %not_b = xor <2 x i32> %b, + %and = and <2 x i32> %a, %not_b + %xor = xor <2 x i32> %a, %b + %res = or <2 x i32> %and, %xor + ret <2 x i32> %res +} + +define i32 @test_commute(i32 %a, i32 %b) { +; CHECK-LABEL: @test_commute( +; CHECK-NEXT: [[RES:%.*]] = xor i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; + %not_b = xor i32 %b, -1 + %and = and i32 %a, %not_b + %xor = xor i32 %a, %b + %res = or i32 %xor, %and ; + ret i32 %res +} + +define i32 @test_multiuse(i32 %a, i32 %b) { +; CHECK-LABEL: @test_multiuse( +; CHECK-NEXT: [[NOT_B:%.*]] = xor i32 [[B:%.*]], -1 +; CHECK-NEXT: [[AND:%.*]] = and i32 [[A:%.*]], [[NOT_B]] +; CHECK-NEXT: call void @use(i32 [[AND]]) +; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A]], [[B]] +; CHECK-NEXT: ret i32 [[XOR]] +; + %not_b = xor i32 %b, -1 + %and = and i32 %a, %not_b + call void @use(i32 %and) + %xor = xor i32 %a, %b + %res = or i32 %and, %xor + ret i32 %res +}