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) { @@ -770,11 +778,13 @@ // constraints. auto IsCompletelyDistributable = [](unsigned BinOpc1, unsigned BinOpc2, unsigned ShOpc) { + assert(ShOpc != Instruction::AShr); return (BinOpc1 != Instruction::Add && BinOpc2 != Instruction::Add) || ShOpc == Instruction::Shl; }; auto GetInvShift = [](unsigned ShOpc) { + assert(ShOpc != Instruction::AShr); return ShOpc == Instruction::LShr ? Instruction::Shl : Instruction::LShr; }; @@ -807,14 +817,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 +847,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 @@ -554,10 +554,9 @@ define i8 @and_ashr_not(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @and_ashr_not( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr i8 [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr i8 [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor i8 [[Y_NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %x.shift = ashr i8 %x, %shamt @@ -569,10 +568,9 @@ define i8 @and_ashr_not_commuted(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @and_ashr_not_commuted( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr i8 [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr i8 [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor i8 [[Y_NOT_NOT]], -1 -; CHECK-NEXT: [[AND:%.*]] = and i8 [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[AND:%.*]] = ashr i8 [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret i8 [[AND]] ; %x.shift = ashr i8 %x, %shamt @@ -614,10 +612,9 @@ define <4 x i8> @and_ashr_not_vec(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @and_ashr_not_vec( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -629,10 +626,9 @@ define <4 x i8> @and_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @and_ashr_not_vec_commuted( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -644,10 +640,9 @@ define <4 x i8> @and_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @and_ashr_not_vec_undef_1( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[AND:%.*]] = and <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[AND:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[AND]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -672,10 +667,9 @@ define i8 @or_ashr_not(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @or_ashr_not( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr i8 [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr i8 [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor i8 [[Y_NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %x.shift = ashr i8 %x, %shamt @@ -687,10 +681,9 @@ define i8 @or_ashr_not_commuted(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @or_ashr_not_commuted( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr i8 [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr i8 [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor i8 [[Y_NOT_NOT]], -1 -; CHECK-NEXT: [[OR:%.*]] = or i8 [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], -1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[OR:%.*]] = ashr i8 [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret i8 [[OR]] ; %x.shift = ashr i8 %x, %shamt @@ -732,10 +725,9 @@ define <4 x i8> @or_ashr_not_vec(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @or_ashr_not_vec( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = or <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -747,10 +739,9 @@ define <4 x i8> @or_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @or_ashr_not_vec_commuted( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = or <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -762,10 +753,9 @@ define <4 x i8> @or_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @or_ashr_not_vec_undef_1( -; CHECK-NEXT: [[X_SHIFT:%.*]] = ashr <4 x i8> [[X:%.*]], [[SHAMT:%.*]] -; CHECK-NEXT: [[Y_NOT_NOT:%.*]] = ashr <4 x i8> [[Y:%.*]], [[SHAMT]] -; CHECK-NEXT: [[Y_NOT_SHIFT:%.*]] = xor <4 x i8> [[Y_NOT_NOT]], -; CHECK-NEXT: [[OR:%.*]] = or <4 x i8> [[X_SHIFT]], [[Y_NOT_SHIFT]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = or <4 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[OR:%.*]] = ashr <4 x i8> [[TMP2]], [[SHAMT:%.*]] ; CHECK-NEXT: ret <4 x i8> [[OR]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -791,9 +781,9 @@ define i8 @xor_ashr_not(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @xor_ashr_not( -; CHECK-NEXT: [[Y_NOT_NOT1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[Y_NOT_NOT1]], [[SHAMT:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr i8 [[TMP1]], [[SHAMT:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[DOTNOT]], -1 ; CHECK-NEXT: ret i8 [[XOR]] ; %x.shift = ashr i8 %x, %shamt @@ -805,9 +795,9 @@ define i8 @xor_ashr_not_commuted(i8 %x, i8 %y, i8 %shamt) { ; CHECK-LABEL: @xor_ashr_not_commuted( -; CHECK-NEXT: [[Y_NOT_NOT1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr i8 [[Y_NOT_NOT1]], [[SHAMT:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[TMP1]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr i8 [[TMP1]], [[SHAMT:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[DOTNOT]], -1 ; CHECK-NEXT: ret i8 [[XOR]] ; %x.shift = ashr i8 %x, %shamt @@ -849,9 +839,9 @@ define <4 x i8> @xor_ashr_not_vec(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @xor_ashr_not_vec( -; CHECK-NEXT: [[Y_NOT_NOT1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[Y_NOT_NOT1]], [[SHAMT:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[SHAMT:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -863,9 +853,9 @@ define <4 x i8> @xor_ashr_not_vec_commuted(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @xor_ashr_not_vec_commuted( -; CHECK-NEXT: [[Y_NOT_NOT1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[Y_NOT_NOT1]], [[SHAMT:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[SHAMT:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %x.shift = ashr <4 x i8> %x, %shamt @@ -877,9 +867,9 @@ define <4 x i8> @xor_ashr_not_vec_undef_1(<4 x i8> %x, <4 x i8> %y, <4 x i8> %shamt) { ; CHECK-LABEL: @xor_ashr_not_vec_undef_1( -; CHECK-NEXT: [[Y_NOT_NOT1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = ashr <4 x i8> [[Y_NOT_NOT1]], [[SHAMT:%.*]] -; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[TMP1]], +; CHECK-NEXT: [[TMP1:%.*]] = xor <4 x i8> [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[DOTNOT:%.*]] = ashr <4 x i8> [[TMP1]], [[SHAMT:%.*]] +; CHECK-NEXT: [[XOR:%.*]] = xor <4 x i8> [[DOTNOT]], ; CHECK-NEXT: ret <4 x i8> [[XOR]] ; %x.shift = ashr <4 x i8> %x, %shamt