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,14 @@ // 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) 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 +815,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 +845,18 @@ if (!IsValidBinOpc(I.getOpcode()) || !IsValidBinOpc(BinOpc)) return nullptr; + if (ShOpc == Instruction::AShr) { + if (Instruction::isBitwiseLogicOp(I.getOpcode()) && + BinOpc == Instruction::Xor && match(Mask, m_AllOnes())) { + Value *NotX = Builder.CreateNot(X); + Value *NewBinOp = Builder.CreateBinOp(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() && 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: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[MUL]], [[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 [[MUL]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %mul = mul i8 %x, %x @@ -572,10 +571,9 @@ define i8 @and_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @and_ashr_not_commuted( ; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[MUL]], [[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 [[MUL]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %mul = mul i8 %x, %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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %mul = mul <4 x i8> %x, %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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %mul = mul <4 x i8> %x, %x @@ -657,10 +653,9 @@ define <4 x i8> @and_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @and_ashr_not_vec_undef_1( ; CHECK-NEXT: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %mul = mul <4 x i8> %x, %x @@ -688,10 +683,9 @@ define i8 @or_ashr_not(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_not( ; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[MUL]], [[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 [[MUL]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %mul = mul i8 %x, %x @@ -705,10 +699,9 @@ define i8 @or_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ashr_not_commuted( ; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr i8 [[MUL]], [[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 [[MUL]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %mul = mul i8 %x, %x @@ -756,10 +749,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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %mul = mul <4 x i8> %x, %x @@ -773,10 +765,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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %mul = mul <4 x i8> %x, %x @@ -790,10 +781,9 @@ define <4 x i8> @or_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @or_ashr_not_vec_undef_1( ; CHECK-NEXT: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[SHR:%.*]] = ashr <4 x i8> [[MUL]], [[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> [[MUL]], [[TMP1]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[Y:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %mul = mul <4 x i8> %x, %x @@ -823,9 +813,9 @@ define i8 @xor_ashr_not(i8 %x, i8 %y) { ; CHECK-LABEL: @xor_ashr_not( ; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor i8 [[MUL]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[MUL]], [[X]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr i8 [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[DOTNOT]], -1 ; CHECK-NEXT: ret i8 [[XOR]] ; %mul = mul i8 %x, %x @@ -839,9 +829,9 @@ define i8 @xor_ashr_not_commuted(i8 %x, i8 %y) { ; CHECK-LABEL: @xor_ashr_not_commuted( ; CHECK-NEXT: [[MUL:%.*]] = mul i8 [[X:%.*]], [[X]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor i8 [[MUL]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[MUL]], [[X]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr i8 [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[DOTNOT]], -1 ; CHECK-NEXT: ret i8 [[XOR]] ; %mul = mul i8 %x, %x @@ -889,9 +879,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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor <4 x i8> [[MUL]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[MUL]], [[X]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %mul = mul <4 x i8> %x, %x @@ -905,9 +895,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: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor <4 x i8> [[MUL]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[MUL]], [[X]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %mul = mul <4 x i8> %x, %x @@ -921,9 +911,9 @@ define <4 x i8> @xor_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y) { ; CHECK-LABEL: @xor_ashr_not_vec_undef_1( ; CHECK-NEXT: [[MUL:%.*]] = mul <4 x i8> [[X:%.*]], [[X]] -; CHECK-NEXT: [[NOT_NOT1:%.*]] = xor <4 x i8> [[MUL]], [[X]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[NOT_NOT1]], [[Y:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[MUL]], [[X]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %mul = mul <4 x i8> %x, %x