diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1970,6 +1970,51 @@ return nullptr; } +// (bitwise1 (bitwise2 (logic_shift X, C), C1), (logic_shift Y, C)) +// IFF the logic_shifts match AND one of the bitwise ops is `and` +// -> (logic_shift (bitwise1 (bitwise2 X, inv_logic_shift(C1, C)), Y), C) +static Instruction * +foldLogicOpOfCAndCShiftWithCShift(BinaryOperator &I, + InstCombiner::BuilderTy &Builder) { + Constant *CMask, *CShift; + Value *X, *Y, *ShiftedY, *ShiftedX; + + // Match bitwise ops. + if (!match(&I, + m_c_BinOp(m_BitwiseLogic(m_Value(ShiftedX), m_ImmConstant(CMask)), + m_Value(ShiftedY)))) + return nullptr; + // Match shifts. + if (!match(ShiftedY, m_OneUse(m_LogicalShift(m_Value(Y), m_ImmConstant(CShift))))) + return nullptr; + if (!match(ShiftedX, m_OneUse(m_LogicalShift(m_Value(X), m_Specific(CShift))))) + return nullptr; + // LHS and RHS need same shift opcode + unsigned ShOpc = cast(ShiftedY)->getOpcode(); + if (ShOpc != cast(ShiftedX)->getOpcode()) + return nullptr; + + unsigned BWOpc = cast(I.getOperand(0))->getOpcode(); + if (!Instruction::isBitwiseLogicOp(BWOpc)) + BWOpc = cast(I.getOperand(1))->getOpcode(); + + assert(Instruction::isBitwiseLogicOp(BWOpc) && + "Expected to find bitwise logic op after match"); + + // Either the inner or outer logic op must be an And. + if (I.getOpcode() != Instruction::And && BWOpc != Instruction::And) + return nullptr; + + unsigned InvShOpc = + ShOpc == Instruction::LShr ? Instruction::Shl : Instruction::LShr; + Constant *NewCMask = ConstantExpr::get(InvShOpc, CMask, CShift); + Value *NewBitOp2 = Builder.CreateBinOp( + static_cast(BWOpc), X, NewCMask); + Value *NewBitOp1 = Builder.CreateBinOp(I.getOpcode(), Y, NewBitOp2); + return BinaryOperator::Create(static_cast(ShOpc), + NewBitOp1, CShift); +} + // Match // (X + C2) | C // (X + C2) ^ C @@ -2053,6 +2098,9 @@ if (Value *V = SimplifyBSwap(I, Builder)) return replaceInstUsesWith(I, V); + if (Instruction *R = foldLogicOpOfCAndCShiftWithCShift(I, Builder)) + return R; + Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); Value *X, *Y; @@ -3097,6 +3145,9 @@ if (Instruction *Concat = matchOrConcat(I, Builder)) return replaceInstUsesWith(I, Concat); + if (Instruction *R = foldLogicOpOfCAndCShiftWithCShift(I, Builder)) + return R; + Value *X, *Y; const APInt *CV; if (match(&I, m_c_Or(m_OneUse(m_Xor(m_Value(X), m_APInt(CV))), m_Value(Y))) && @@ -4170,6 +4221,9 @@ if (Instruction *R = foldNot(I)) return R; + if (Instruction *R = foldLogicOpOfCAndCShiftWithCShift(I, Builder)) + return R; + // Fold (X & M) ^ (Y & ~M) -> (X & M) | (Y & ~M) // This it a special case in haveNoCommonBitsSet, but the computeKnownBits // calls in there are unnecessary as SimplifyDemandedInstructionBits should diff --git a/llvm/test/Transforms/InstCombine/bitwise-and-shifts.ll b/llvm/test/Transforms/InstCombine/bitwise-and-shifts.ll --- a/llvm/test/Transforms/InstCombine/bitwise-and-shifts.ll +++ b/llvm/test/Transforms/InstCombine/bitwise-and-shifts.ll @@ -3,10 +3,9 @@ define i8 @shl_and_and(i8 %x, i8 %y) { ; CHECK-LABEL: @shl_and_and( -; CHECK-NEXT: [[SHIFT1:%.*]] = shl i8 [[X:%.*]], 4 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 [[Y:%.*]], 4 -; CHECK-NEXT: [[BW2:%.*]] = and i8 [[SHIFT2]], 80 -; CHECK-NEXT: [[BW1:%.*]] = and i8 [[SHIFT1]], [[BW2]] +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[Y:%.*]], 5 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[BW1:%.*]] = shl nuw nsw i8 [[TMP2]], 4 ; CHECK-NEXT: ret i8 [[BW1]] ; %shift1 = shl i8 %x, 4 @@ -48,10 +47,9 @@ define <2 x i8> @lshr_and_or(<2 x i8> %x, <2 x i8> %y) { ; CHECK-LABEL: @lshr_and_or( -; CHECK-NEXT: [[SHIFT1:%.*]] = lshr <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[SHIFT2:%.*]] = lshr <2 x i8> [[Y:%.*]], -; CHECK-NEXT: [[BW2:%.*]] = and <2 x i8> [[SHIFT1]], -; CHECK-NEXT: [[BW1:%.*]] = or <2 x i8> [[SHIFT2]], [[BW2]] +; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = or <2 x i8> [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[BW1:%.*]] = lshr <2 x i8> [[TMP2]], ; CHECK-NEXT: ret <2 x i8> [[BW1]] ; %shift1 = lshr <2 x i8> %x, @@ -78,10 +76,9 @@ define i8 @shl_and_xor(i8 %x, i8 %y) { ; CHECK-LABEL: @shl_and_xor( -; CHECK-NEXT: [[SHIFT1:%.*]] = shl i8 [[X:%.*]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 [[Y:%.*]], 1 -; CHECK-NEXT: [[BW2:%.*]] = and i8 [[SHIFT1]], 20 -; CHECK-NEXT: [[BW1:%.*]] = xor i8 [[SHIFT2]], [[BW2]] +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X:%.*]], 10 +; CHECK-NEXT: [[TMP2:%.*]] = xor i8 [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[BW1:%.*]] = shl i8 [[TMP2]], 1 ; CHECK-NEXT: ret i8 [[BW1]] ; %shift1 = shl i8 %x, 1 @@ -108,10 +105,9 @@ define i8 @lshr_or_and(i8 %x, i8 %y) { ; CHECK-LABEL: @lshr_or_and( -; CHECK-NEXT: [[SHIFT1:%.*]] = lshr i8 [[X:%.*]], 5 -; CHECK-NEXT: [[SHIFT2:%.*]] = lshr i8 [[Y:%.*]], 5 -; CHECK-NEXT: [[BW2:%.*]] = or i8 [[SHIFT1]], 6 -; CHECK-NEXT: [[BW1:%.*]] = and i8 [[BW2]], [[SHIFT2]] +; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[X:%.*]], -64 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[Y:%.*]] +; CHECK-NEXT: [[BW1:%.*]] = lshr i8 [[TMP2]], 5 ; CHECK-NEXT: ret i8 [[BW1]] ; %shift1 = lshr i8 %x, 5 @@ -138,10 +134,9 @@ define <2 x i8> @shl_xor_and(<2 x i8> %x, <2 x i8> %y) { ; CHECK-LABEL: @shl_xor_and( -; CHECK-NEXT: [[SHIFT1:%.*]] = shl <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> [[Y:%.*]], -; CHECK-NEXT: [[BW2:%.*]] = xor <2 x i8> [[SHIFT2]], -; CHECK-NEXT: [[BW1:%.*]] = and <2 x i8> [[BW2]], [[SHIFT1]] +; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i8> [[Y:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: [[BW1:%.*]] = shl <2 x i8> [[TMP2]], ; CHECK-NEXT: ret <2 x i8> [[BW1]] ; %shift1 = shl <2 x i8> %x, diff --git a/llvm/test/Transforms/InstCombine/or-shifted-masks.ll b/llvm/test/Transforms/InstCombine/or-shifted-masks.ll --- a/llvm/test/Transforms/InstCombine/or-shifted-masks.ll +++ b/llvm/test/Transforms/InstCombine/or-shifted-masks.ll @@ -75,16 +75,10 @@ define i32 @multiuse2(i32 %x) { ; CHECK-LABEL: @multiuse2( -; CHECK-NEXT: [[I:%.*]] = shl i32 [[X:%.*]], 1 -; CHECK-NEXT: [[I2:%.*]] = and i32 [[I]], 12 -; CHECK-NEXT: [[I6:%.*]] = shl i32 [[X]], 8 -; CHECK-NEXT: [[I7:%.*]] = and i32 [[I6]], 24576 -; CHECK-NEXT: [[I14:%.*]] = shl i32 [[X]], 8 -; CHECK-NEXT: [[I9:%.*]] = and i32 [[I14]], 7680 -; CHECK-NEXT: [[I10:%.*]] = or i32 [[I7]], [[I9]] -; CHECK-NEXT: [[I85:%.*]] = shl i32 [[X]], 1 -; CHECK-NEXT: [[I11:%.*]] = and i32 [[I85]], 240 -; CHECK-NEXT: [[I12:%.*]] = or i32 [[I2]], [[I11]] +; CHECK-NEXT: [[TMP1:%.*]] = shl i32 [[X:%.*]], 8 +; CHECK-NEXT: [[I10:%.*]] = and i32 [[TMP1]], 32256 +; CHECK-NEXT: [[TMP2:%.*]] = shl i32 [[X]], 1 +; CHECK-NEXT: [[I12:%.*]] = and i32 [[TMP2]], 252 ; CHECK-NEXT: [[I13:%.*]] = or i32 [[I10]], [[I12]] ; CHECK-NEXT: ret i32 [[I13]] ; @@ -107,15 +101,10 @@ define i32 @multiuse3(i32 %x) { ; CHECK-LABEL: @multiuse3( -; CHECK-NEXT: [[I:%.*]] = and i32 [[X:%.*]], 96 -; CHECK-NEXT: [[I1:%.*]] = shl nuw nsw i32 [[I]], 6 -; CHECK-NEXT: [[I2:%.*]] = lshr exact i32 [[I]], 1 -; CHECK-NEXT: [[I3:%.*]] = shl i32 [[X]], 6 -; CHECK-NEXT: [[I4:%.*]] = and i32 [[I3]], 1920 -; CHECK-NEXT: [[I5:%.*]] = or i32 [[I1]], [[I4]] -; CHECK-NEXT: [[I6:%.*]] = lshr i32 [[X]], 1 -; CHECK-NEXT: [[I7:%.*]] = and i32 [[I6]], 15 -; CHECK-NEXT: [[I8:%.*]] = or i32 [[I2]], [[I7]] +; CHECK-NEXT: [[TMP1:%.*]] = shl i32 [[X:%.*]], 6 +; CHECK-NEXT: [[I5:%.*]] = and i32 [[TMP1]], 8064 +; CHECK-NEXT: [[TMP2:%.*]] = lshr i32 [[X]], 1 +; CHECK-NEXT: [[I8:%.*]] = and i32 [[TMP2]], 63 ; CHECK-NEXT: [[I9:%.*]] = or i32 [[I8]], [[I5]] ; CHECK-NEXT: ret i32 [[I9]] ; @@ -134,20 +123,18 @@ define i32 @multiuse4(i32 %x) local_unnamed_addr { ; CHECK-LABEL: @multiuse4( -; CHECK-NEXT: [[I:%.*]] = and i32 [[X:%.*]], 100663296 -; CHECK-NEXT: [[I1:%.*]] = icmp sgt i32 [[X]], -1 +; CHECK-NEXT: [[I1:%.*]] = icmp sgt i32 [[X:%.*]], -1 ; CHECK-NEXT: br i1 [[I1]], label [[IF:%.*]], label [[ELSE:%.*]] ; CHECK: if: -; CHECK-NEXT: [[I2:%.*]] = lshr exact i32 [[I]], 22 +; CHECK-NEXT: [[I:%.*]] = lshr i32 [[X]], 22 +; CHECK-NEXT: [[I2:%.*]] = and i32 [[I]], 24 ; CHECK-NEXT: [[I3:%.*]] = lshr i32 [[X]], 22 ; CHECK-NEXT: [[I4:%.*]] = and i32 [[I3]], 480 ; CHECK-NEXT: [[I5:%.*]] = or i32 [[I4]], [[I2]] ; CHECK-NEXT: br label [[END:%.*]] ; CHECK: else: -; CHECK-NEXT: [[I6:%.*]] = lshr exact i32 [[I]], 17 -; CHECK-NEXT: [[I7:%.*]] = lshr i32 [[X]], 17 -; CHECK-NEXT: [[I8:%.*]] = and i32 [[I7]], 15360 -; CHECK-NEXT: [[I9:%.*]] = or i32 [[I8]], [[I6]] +; CHECK-NEXT: [[TMP1:%.*]] = lshr i32 [[X]], 17 +; CHECK-NEXT: [[I9:%.*]] = and i32 [[TMP1]], 16128 ; CHECK-NEXT: br label [[END]] ; CHECK: end: ; CHECK-NEXT: [[I10:%.*]] = phi i32 [ [[I5]], [[IF]] ], [ [[I9]], [[ELSE]] ]