diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1421,6 +1421,15 @@ replaceOperand(I, 1, B); return &I; } + + // (add (or (sub 0, A) A) A) --> (and (add A, -1) A) + // (add (or A (sub 0, A)) A) --> (and (add A, -1) A) + // (add A (or (sub 0, A) A)) --> (and (add A, -1) A) + // (add A (or A (sub 0, A))) --> (and (add A, -1) A) + if (match(&I, m_c_BinOp(m_Or(m_Sub(m_ZeroInt(), m_Value(A)), m_Value(A)), m_Value(A)))) { + Value *AddInst = Builder.CreateAdd(A, Constant::getAllOnesValue(A->getType())); + return BinaryOperator::CreateAnd(AddInst, A); + } // Canonicalize ((A & -A) - 1) --> ((A - 1) & ~A) // Forms all commutable operations, and simplifies ctpop -> cttz folds. diff --git a/llvm/test/Transforms/InstCombine/add_or_sub.ll b/llvm/test/Transforms/InstCombine/add_or_sub.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/add_or_sub.ll @@ -0,0 +1,26 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i32 @add_or_sub_i32_comb(i32 %x) { +; CHECK-LABEL: @add_or_sub_i32_comb( +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -1 +; CHECK-NEXT: [[ADD:%.*]] = and i32 [[TMP1]], [[X]] +; CHECK-NEXT: ret i32 [[ADD]] +; + %sub = sub i32 0, %x + %or = or i32 %sub, %x + %add = add i32 %or, %x + ret i32 %add +} + +define i8 @add_or_sub_i8_comb(i8 %x) { +; CHECK-LABEL: @add_or_sub_i8_comb( +; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X:%.*]], -1 +; CHECK-NEXT: [[ADD:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: ret i8 [[ADD]] +; + %sub = sub i8 0, %x + %or = or i8 %sub, %x + %add = add i8 %or, %x + ret i8 %add +}