diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -751,6 +751,15 @@ // 2) BinOp1 == BinOp2 (if BinOp == `add`, then also requires `shl`). // // -> (BinOp (logic_shift (BinOp X, Y)), Mask) +// +// (Binop1 (Binop2 (arithmetic_shift X, Amt), Mask), (arithmetic_shift Y, Amt)) +// IFF +// 1) the arithmetic_shift match +// 1) Binop1 is bitwise logical operator `and`, `or` or `xor` +// 2) Binop2 is `not` +// +// -> (arithmetic_shift Binop1((not X), Y), Amt) + Instruction *InstCombinerImpl::foldBinOpShiftWithShift(BinaryOperator &I) { auto IsValidBinOpc = [](unsigned Opc) { switch (Opc) { @@ -807,14 +816,13 @@ Constant *CMask, *CShift; Value *X, *Y, *ShiftedX, *Mask, *Shift; if (!match(I.getOperand(ShOpnum), - m_OneUse(m_LogicalShift(m_Value(Y), m_Value(Shift))))) + m_OneUse(m_Shift(m_Value(Y), m_Value(Shift))))) return nullptr; if (!match(I.getOperand(1 - ShOpnum), m_BinOp(m_Value(ShiftedX), m_Value(Mask)))) return nullptr; - if (!match(ShiftedX, - m_OneUse(m_LogicalShift(m_Value(X), m_Specific(Shift))))) + if (!match(ShiftedX, m_OneUse(m_Shift(m_Value(X), m_Specific(Shift))))) return nullptr; // Make sure we are matching instruction shifts and not ConstantExpr @@ -838,6 +846,22 @@ if (!IsValidBinOpc(I.getOpcode()) || !IsValidBinOpc(BinOpc)) return nullptr; + CMask = dyn_cast(Mask); + + if (ShOpc == Instruction::AShr) { + if (Instruction::isBitwiseLogicOp(I.getOpcode()) && + BinOpc == Instruction::Xor && CMask && CMask->isAllOnesValue()) { + Value *NotX = Builder.CreateBinOp( + static_cast(BinOpc), X, Mask); + Value *NewBinOp = Builder.CreateBinOp( + static_cast(I.getOpcode()), Y, NotX); + return BinaryOperator::Create( + static_cast(ShOpc), NewBinOp, Shift); + } + + return nullptr; + } + // If BinOp1 == BinOp2 and it's bitwise or shl with add, then just // distribute to drop the shift irrelevant of constants. if (BinOpc == I.getOpcode() && @@ -852,7 +876,7 @@ // ensure we have constants. if (!match(Shift, m_ImmConstant(CShift))) return nullptr; - if (!match(Mask, m_ImmConstant(CMask))) + if (!CMask) return nullptr; // Check if we can distribute the binops. diff --git a/llvm/test/Transforms/InstCombine/binop-and-shifts.ll b/llvm/test/Transforms/InstCombine/binop-and-shifts.ll --- a/llvm/test/Transforms/InstCombine/binop-and-shifts.ll +++ b/llvm/test/Transforms/InstCombine/binop-and-shifts.ll @@ -555,10 +555,9 @@ define i8 @and_ashr_not(i8 %x, i8 %y) { ; CHECK-LABEL: @and_ashr_not( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %sub = sub nsw i8 0, %x @@ -572,10 +571,9 @@ define i8 @and_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @and_ashr_not_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %sub = sub nsw i8 0, %x @@ -623,10 +621,9 @@ define <4 x i8> @and_ashr_not_vec(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @and_ashr_not_vec( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[TMP2:%.*]] = and <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %sub = sub nsw <4 x i8> , %x @@ -640,10 +637,9 @@ define <4 x i8> @and_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @and_ashr_not_vec_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[TMP2:%.*]] = and <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %sub = sub nsw <4 x i8> , %x @@ -659,10 +655,9 @@ define i8 @or_ashr_not(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_not( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %sub = sub nsw i8 0, %x @@ -676,10 +671,9 @@ define i8 @or_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_not_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr i8 [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor i8 [[NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %sub = sub nsw i8 0, %x @@ -727,10 +721,9 @@ define <4 x i8> @or_ashr_not_vec(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @or_ashr_not_vec( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[TMP2:%.*]] = or <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %sub = sub nsw <4 x i8> , %x @@ -744,10 +737,9 @@ define <4 x i8> @or_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @or_ashr_not_vec_commuted( ; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[SUB]], [[Y:%.*]] -; CHECK-NEXT: [[NOT_NOT:%.*]] = ashr <4 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[SHR1:%.*]] = xor <4 x i8> [[NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[SHR]], [[SHR1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[X]], +; CHECK-NEXT: [[TMP2:%.*]] = or <4 x i8> [[SUB]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %sub = sub nsw <4 x i8> , %x @@ -762,10 +754,9 @@ define i8 @xor_ashr_not(i8 %x, i8 %y) { ; CHECK-LABEL: @xor_ashr_not( -; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor i8 [[SUB]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[SUB_NOT:%.*]] = add i8 [[X:%.*]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[SUB_NOT]], [[X]] +; CHECK-NEXT: [[XOR:%.*]] = ashr i8 [[TMP1]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[XOR]] ; %sub = sub nsw i8 0, %x @@ -778,10 +769,9 @@ define i8 @xor_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @xor_ashr_not_commuted( -; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 0, [[X:%.*]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor i8 [[SUB]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[SUB_NOT:%.*]] = add i8 [[X:%.*]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[SUB_NOT]], [[X]] +; CHECK-NEXT: [[XOR:%.*]] = ashr i8 [[TMP1]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[XOR]] ; %sub = sub nsw i8 0, %x @@ -828,10 +818,9 @@ define <4 x i8> @xor_ashr_not_vec(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @xor_ashr_not_vec( -; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor <4 x i8> [[SUB]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[SUB_NOT:%.*]] = add <4 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[SUB_NOT]], [[X]] +; CHECK-NEXT: [[XOR:%.*]] = ashr <4 x i8> [[TMP1]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %sub = sub nsw <4 x i8> , %x @@ -844,10 +833,9 @@ define <4 x i8> @xor_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @xor_ashr_not_vec_commuted( -; CHECK-NEXT: [[SUB:%.*]] = sub nsw <4 x i8> zeroinitializer, [[X:%.*]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor <4 x i8> [[SUB]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[SUB_NOT:%.*]] = add <4 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[SUB_NOT]], [[X]] +; CHECK-NEXT: [[XOR:%.*]] = ashr <4 x i8> [[TMP1]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %sub = sub nsw <4 x i8> , %x