Index: llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1342,6 +1342,11 @@ match(&I, m_BinOp(m_c_Add(m_Not(m_Value(B)), m_Value(A)), m_One()))) return BinaryOperator::CreateSub(A, B); + // (x & y) + ~(x | y) --> ~(x ^ y) + if (match(&I, m_c_BinOp(m_And(m_Value(A), m_Value(B)), + m_Not(m_Or(m_Value(A), m_Value(B)))))) + return BinaryOperator::CreateNot(Builder.CreateXor(A, B)); + // (A + RHS) + RHS --> A + (RHS << 1) if (match(LHS, m_OneUse(m_c_Add(m_Value(A), m_Specific(RHS))))) return BinaryOperator::CreateAdd(A, Builder.CreateShl(RHS, 1, "reass.add")); Index: llvm/test/Transforms/InstCombine/pr53357.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/pr53357.ll @@ -0,0 +1,32 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Test for (x & y) + ~(x | y) -> ~(x ^ y) +; RUN: opt < %s -instcombine -S | FileCheck %s + + +; (x & y) + ~(x | y) +define dso_local i32 @src(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 { + %3 = and i32 %1, %0 + %4 = or i32 %1, %0 + %5 = xor i32 %4, -1 + %6 = add i32 %3, %5 + ret i32 %6 +} +; CHECK-LABEL: src +; CHECK-NEXT: %[[XOR:.+]] = xor i32 %1, %0 +; CHECK-NEXT: %[[XOR2:.+]] = xor i32 %[[XOR]], -1 +; CHECK-NEXT: ret i32 %[[XOR2]] + + +; (x & y) + (~x & ~y) +define dso_local i32 @src2(i32 noundef %0, i32 noundef %1) local_unnamed_addr #0 { + %3 = and i32 %1, %0 + %4 = xor i32 %0, -1 + %5 = xor i32 %1, -1 + %6 = and i32 %4, %5 + %7 = add i32 %3, %6 + ret i32 %7 +} +; CHECK-LABEL: src2 +; CHECK-NEXT: %[[XOR:.+]] = xor i32 %0, %1 +; CHECK-NEXT: %[[XOR2:.+]] = xor i32 %[[XOR]], -1 +; CHECK-NEXT: ret i32 %[[XOR2]]