Index: llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp @@ -1163,15 +1163,23 @@ } } - // Look for a "splat" mul pattern - it replicates bits across each half of - // a value, so a right shift is just a mask of the low bits: - // lshr i32 (mul nuw X, Pow2+1), 16 --> and X, Pow2-1 - // TODO: Generalize to allow more than just half-width shifts? const APInt *MulC; - if (match(Op0, m_NUWMul(m_Value(X), m_APInt(MulC))) && - ShAmtC * 2 == BitWidth && (*MulC - 1).isPowerOf2() && - MulC->logBase2() == ShAmtC) - return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *MulC - 2)); + if (match(Op0, m_NUWMul(m_Value(X), m_APInt(MulC)))) { + // Look for a "splat" mul pattern - it replicates bits across each half of + // a value, so a right shift is just a mask of the low bits: + // lshr i32 (mul nuw X, Pow2+1), 16 --> and X, Pow2-1 + // TODO: Generalize to allow more than just half-width shifts? + if (ShAmtC * 2 == BitWidth && (*MulC - 1).isPowerOf2() && + MulC->logBase2() == ShAmtC) + return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *MulC - 2)); + + // lshr exact (mul nuw x, c), ShAmtC -> mul nuw x, (c >> ShAmtC) + if (Op0->hasOneUse()) { + APInt NewMulC = MulC->lshr(ShAmtC); + if (MulC->eq(NewMulC.shl(ShAmtC))) + return BinaryOperator::CreateNUWMul(X, ConstantInt::get(Ty, NewMulC)); + } + } // Try to narrow a bswap: // (bswap (zext X)) >> C --> zext (bswap X >> C') Index: llvm/test/Transforms/InstCombine/shift-logic.ll =================================================================== --- llvm/test/Transforms/InstCombine/shift-logic.ll +++ llvm/test/Transforms/InstCombine/shift-logic.ll @@ -254,3 +254,50 @@ %t27 = ashr exact i32 %t0, 16 ret i32 %t27 } + +define i64 @lshr_mul(i64 %0) { +; CHECK-LABEL: @lshr_mul( +; CHECK-NEXT: [[TMP2:%.*]] = mul nuw i64 [[TMP0:%.*]], 13 +; CHECK-NEXT: ret i64 [[TMP2]] +; + %2 = mul nuw i64 %0, 52 + %3 = lshr i64 %2, 2 + ret i64 %3 +} + +define i64 @lshr_mul_negative_noexact(i64 %0) { +; CHECK-LABEL: @lshr_mul_negative_noexact( +; CHECK-NEXT: [[TMP2:%.*]] = mul nuw i64 [[TMP0:%.*]], 53 +; CHECK-NEXT: [[TMP3:%.*]] = lshr i64 [[TMP2]], 2 +; CHECK-NEXT: ret i64 [[TMP3]] +; + %2 = mul nuw i64 %0, 53 + %3 = lshr i64 %2, 2 + ret i64 %3 +} + +declare void @use(i64) + +define i64 @lshr_mul_negative_oneuse(i64 %0) { +; CHECK-LABEL: @lshr_mul_negative_oneuse( +; CHECK-NEXT: [[TMP2:%.*]] = mul nuw i64 [[TMP0:%.*]], 52 +; CHECK-NEXT: call void @use(i64 [[TMP2]]) +; CHECK-NEXT: [[TMP3:%.*]] = lshr exact i64 [[TMP2]], 2 +; CHECK-NEXT: ret i64 [[TMP3]] +; + %2 = mul nuw i64 %0, 52 + call void @use(i64 %2) + %3 = lshr i64 %2, 2 + ret i64 %3 +} + +define i64 @lshr_mul_negative_nonuw(i64 %0) { +; CHECK-LABEL: @lshr_mul_negative_nonuw( +; CHECK-NEXT: [[TMP2:%.*]] = mul i64 [[TMP0:%.*]], 52 +; CHECK-NEXT: [[TMP3:%.*]] = lshr exact i64 [[TMP2]], 2 +; CHECK-NEXT: ret i64 [[TMP3]] +; + %2 = mul i64 %0, 52 + %3 = lshr i64 %2, 2 + ret i64 %3 +}