Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2117,6 +2117,41 @@ ConstantInt::get(Ty, *C2), Add); } +// binop(shift(ShiftedC1, ShAmt), shift(ShiftedC2, add(ShAmt, AddC))) -> +// shift(binop(ShiftedC1, shift(ShiftedC2, AddC)), ShAmt) +// where both shifts are the same and AddC is a valid shift amount. +static Value *foldBinOpOfDisplacedShifts(BinaryOperator &I, + IRBuilderBase &Builder) { + assert(I.isBitwiseLogicOp() && "Unexpected opcode"); + + Value *ShAmt; + Constant *ShiftedC1, *ShiftedC2, *AddC; + Type *Ty = I.getType(); + unsigned BitWidth = Ty->getScalarSizeInBits(); + if (!match(&I, m_c_BinOp( + m_Shift(m_ImmConstant(ShiftedC1), m_Value(ShAmt)), + m_Shift(m_ImmConstant(ShiftedC2), + m_Add(m_Deferred(ShAmt), m_ImmConstant(AddC))))) || + !match(AddC, + m_SpecificInt_ICMP(ICmpInst::ICMP_ULT, APInt(BitWidth, BitWidth)))) + return nullptr; + + // Avoid constant expressions. + auto *Op0Inst = dyn_cast(I.getOperand(0)); + auto *Op1Inst = dyn_cast(I.getOperand(1)); + if (!Op0Inst || !Op1Inst) + return nullptr; + + // Both shifts must be the same. + Instruction::BinaryOps ShiftOp = (Instruction::BinaryOps)Op0Inst->getOpcode(); + if (ShiftOp != Op1Inst->getOpcode()) + return nullptr; + + Value *NewC = Builder.CreateBinOp( + I.getOpcode(), ShiftedC1, Builder.CreateBinOp(ShiftOp, ShiftedC2, AddC)); + return Builder.CreateBinOp(ShiftOp, NewC, ShAmt); +} + // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches // here. We should standardize that construct where it is needed or choose some // other way to ensure that commutated variants of patterns are not missed. @@ -2601,6 +2636,9 @@ if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1)) return Folded; + if (Value *Res = foldBinOpOfDisplacedShifts(I, Builder)) + return replaceInstUsesWith(I, Res); + return nullptr; } @@ -3626,6 +3664,9 @@ if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1)) return Folded; + if (Value *Res = foldBinOpOfDisplacedShifts(I, Builder)) + return replaceInstUsesWith(I, Res); + return nullptr; } @@ -4543,5 +4584,8 @@ if (Instruction *Folded = canonicalizeConditionalNegationViaMathToSelect(I)) return Folded; + if (Value *Res = foldBinOpOfDisplacedShifts(I, Builder)) + return replaceInstUsesWith(I, Res); + return nullptr; } Index: llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll =================================================================== --- llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll +++ llvm/test/Transforms/InstCombine/binop-of-displaced-shifts.ll @@ -4,10 +4,7 @@ define i8 @shl_or(i8 %x) { ; CHECK-LABEL: define i8 @shl_or ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl i8 16, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 3, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl i8 22, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = shl i8 16, %x @@ -20,10 +17,7 @@ define i8 @lshr_or(i8 %x) { ; CHECK-LABEL: define i8 @lshr_or ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = lshr i8 16, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = lshr i8 3, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = lshr i8 17, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = lshr i8 16, %x @@ -36,10 +30,7 @@ define i8 @ashr_or(i8 %x) { ; CHECK-LABEL: define i8 @ashr_or ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = ashr i8 -64, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = ashr i8 -128, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = ashr i8 -64, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = ashr i8 -64, %x @@ -52,10 +43,7 @@ define i8 @shl_xor(i8 %x) { ; CHECK-LABEL: define i8 @shl_xor ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl i8 16, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 3, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl i8 22, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = shl i8 16, %x @@ -68,10 +56,7 @@ define i8 @lshr_xor(i8 %x) { ; CHECK-LABEL: define i8 @lshr_xor ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = lshr i8 16, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = lshr i8 3, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = lshr i8 17, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = lshr i8 16, %x @@ -84,10 +69,7 @@ define i8 @ashr_xor(i8 %x) { ; CHECK-LABEL: define i8 @ashr_xor ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = ashr i8 -128, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = ashr i8 -64, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = xor i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = lshr i8 96, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = ashr i8 -128, %x @@ -100,10 +82,7 @@ define i8 @shl_and(i8 %x) { ; CHECK-LABEL: define i8 @shl_and ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl i8 48, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 8, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl i8 16, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = shl i8 48, %x @@ -116,10 +95,7 @@ define i8 @lshr_and(i8 %x) { ; CHECK-LABEL: define i8 @lshr_and ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = lshr i8 48, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = lshr i8 64, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = lshr i8 32, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = lshr i8 48, %x @@ -132,10 +108,7 @@ define i8 @ashr_and(i8 %x) { ; CHECK-LABEL: define i8 @ashr_and ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = ashr i8 -64, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = ashr i8 -128, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = and i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = ashr i8 -64, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = ashr i8 -64, %x @@ -148,10 +121,7 @@ define i8 @shl_or_commuted(i8 %x) { ; CHECK-LABEL: define i8 @shl_or_commuted ; CHECK-SAME: (i8 [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl i8 16, [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 -; CHECK-NEXT: [[SHIFT2:%.*]] = shl i8 3, [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or i8 [[SHIFT2]], [[SHIFT]] +; CHECK-NEXT: [[BINOP:%.*]] = shl i8 22, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = shl i8 16, %x @@ -164,10 +134,7 @@ define <2 x i8> @shl_or_splat(<2 x i8> %x) { ; CHECK-LABEL: define <2 x i8> @shl_or_splat ; CHECK-SAME: (<2 x i8> [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl <2 x i8> , [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i8> [[X]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> , [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl <2 x i8> , [[X]] ; CHECK-NEXT: ret <2 x i8> [[BINOP]] ; %shift = shl <2 x i8> , %x @@ -180,10 +147,7 @@ define <2 x i8> @shl_or_non_splat(<2 x i8> %x) { ; CHECK-LABEL: define <2 x i8> @shl_or_non_splat ; CHECK-SAME: (<2 x i8> [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl <2 x i8> , [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i8> [[X]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> , [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl <2 x i8> , [[X]] ; CHECK-NEXT: ret <2 x i8> [[BINOP]] ; %shift = shl <2 x i8> , %x @@ -196,10 +160,7 @@ define <2 x i8> @shl_or_undef_in_add(<2 x i8> %x) { ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_add ; CHECK-SAME: (<2 x i8> [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl <2 x i8> , [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i8> [[X]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> , [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl <2 x i8> , [[X]] ; CHECK-NEXT: ret <2 x i8> [[BINOP]] ; %shift = shl <2 x i8> , %x @@ -212,10 +173,7 @@ define <2 x i8> @shl_or_undef_in_shift1(<2 x i8> %x) { ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_shift1 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl <2 x i8> , [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i8> [[X]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> , [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl <2 x i8> , [[X]] ; CHECK-NEXT: ret <2 x i8> [[BINOP]] ; %shift = shl <2 x i8> , %x @@ -228,10 +186,7 @@ define <2 x i8> @shl_or_undef_in_shift2(<2 x i8> %x) { ; CHECK-LABEL: define <2 x i8> @shl_or_undef_in_shift2 ; CHECK-SAME: (<2 x i8> [[X:%.*]]) { -; CHECK-NEXT: [[SHIFT:%.*]] = shl <2 x i8> , [[X]] -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i8> [[X]], -; CHECK-NEXT: [[SHIFT2:%.*]] = shl <2 x i8> , [[ADD]] -; CHECK-NEXT: [[BINOP:%.*]] = or <2 x i8> [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl <2 x i8> , [[X]] ; CHECK-NEXT: ret <2 x i8> [[BINOP]] ; %shift = shl <2 x i8> , %x @@ -252,7 +207,7 @@ ; CHECK-NEXT: call void @use(i8 [[SHIFT]]) ; CHECK-NEXT: call void @use(i8 [[ADD]]) ; CHECK-NEXT: call void @use(i8 [[SHIFT2]]) -; CHECK-NEXT: [[BINOP:%.*]] = or i8 [[SHIFT]], [[SHIFT2]] +; CHECK-NEXT: [[BINOP:%.*]] = shl i8 22, [[X]] ; CHECK-NEXT: ret i8 [[BINOP]] ; %shift = shl i8 16, %x