[llvm] [InstCombine] Fold shift of a constant into a reverse shift (PR #192982)
Piotr Fusik via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 22 23:06:32 PDT 2026
================
@@ -460,42 +460,69 @@ Instruction *InstCombinerImpl::commonShiftTransforms(BinaryOperator &I) {
unsigned BitWidth = Ty->getScalarSizeInBits();
- const APInt *AC, *AddC;
- // Try to pre-shift a constant shifted by a variable amount added with a
- // negative number:
- // C << (X - AddC) --> (C >> AddC) << X
- // and
- // C >> (X - AddC) --> (C << AddC) >> X
- if (match(Op0, m_APInt(AC)) && match(Op1, m_Add(m_Value(A), m_APInt(AddC))) &&
- AddC->isNegative() && (-*AddC).ult(BitWidth)) {
+ const APInt *AC;
+ if (match(Op0, m_APInt(AC))) {
assert(!AC->isZero() && "Expected simplify of shifted zero");
- unsigned PosOffset = (-*AddC).getZExtValue();
- auto isSuitableForPreShift = [PosOffset, &I, AC]() {
- switch (I.getOpcode()) {
- default:
- return false;
- case Instruction::Shl:
- return (I.hasNoSignedWrap() || I.hasNoUnsignedWrap()) &&
- AC->eq(AC->lshr(PosOffset).shl(PosOffset));
- case Instruction::LShr:
- return I.isExact() && AC->eq(AC->shl(PosOffset).lshr(PosOffset));
- case Instruction::AShr:
- return I.isExact() && AC->eq(AC->shl(PosOffset).ashr(PosOffset));
+ // Try to pre-shift a constant shifted by a variable amount added with a
+ // negative number:
+ // C << (X - AddC) --> (C >> AddC) << X
+ // and
+ // C >> (X - AddC) --> (C << AddC) >> X
+ const APInt *AddC;
+ if (match(Op1, m_Add(m_Value(A), m_APInt(AddC))) && AddC->isNegative() &&
+ (-*AddC).ult(BitWidth)) {
+ unsigned PosOffset = (-*AddC).getZExtValue();
+
+ auto isSuitableForPreShift = [PosOffset, &I, AC]() {
+ switch (I.getOpcode()) {
+ default:
+ return false;
+ case Instruction::Shl:
+ return (I.hasNoSignedWrap() || I.hasNoUnsignedWrap()) &&
+ AC->eq(AC->lshr(PosOffset).shl(PosOffset));
+ case Instruction::LShr:
+ return I.isExact() && AC->eq(AC->shl(PosOffset).lshr(PosOffset));
+ case Instruction::AShr:
+ return I.isExact() && AC->eq(AC->shl(PosOffset).ashr(PosOffset));
+ }
+ };
+ if (isSuitableForPreShift()) {
+ Constant *NewC = ConstantInt::get(Ty, I.getOpcode() == Instruction::Shl
+ ? AC->lshr(PosOffset)
+ : AC->shl(PosOffset));
+ BinaryOperator *NewShiftOp =
+ BinaryOperator::Create(I.getOpcode(), NewC, A);
+ if (I.getOpcode() == Instruction::Shl) {
+ NewShiftOp->setHasNoUnsignedWrap(I.hasNoUnsignedWrap());
+ } else {
+ NewShiftOp->setIsExact();
+ }
+ return NewShiftOp;
}
- };
- if (isSuitableForPreShift()) {
- Constant *NewC = ConstantInt::get(Ty, I.getOpcode() == Instruction::Shl
- ? AC->lshr(PosOffset)
- : AC->shl(PosOffset));
- BinaryOperator *NewShiftOp =
- BinaryOperator::Create(I.getOpcode(), NewC, A);
+ }
+
+ // C1 << (C2 - X) -> (C1 << C2) >> X
+ // C1 >> (C2 - X) -> (C1 >> C2) << X
+ // X must be >=0 (checked by NUWSub).
+ // Also match (X ^ C2) if equivalent to (C2 - X).
+ uint64_t C2;
+ Value *X;
+ if (match(Op1, m_NUWSub(m_ConstantInt(C2), m_Value(X))) ||
+ (match(Op1, m_Xor(m_Value(X), m_ConstantInt(C2))) &&
+ (C2 | computeKnownBits(X, &I).Zero).isAllOnes())) {
if (I.getOpcode() == Instruction::Shl) {
- NewShiftOp->setHasNoUnsignedWrap(I.hasNoUnsignedWrap());
- } else {
- NewShiftOp->setIsExact();
+ if (AC->countl_zero() >= C2)
+ return BinaryOperator::CreateExactLShr(
+ ConstantInt::get(Ty, AC->shl(C2)), X);
+ if (AC->countl_one() > C2)
+ return BinaryOperator::CreateExactAShr(
+ ConstantInt::get(Ty, AC->shl(C2)), X);
+ } else if (AC->countr_zero() >= C2) {
+ APInt C =
+ I.getOpcode() == Instruction::LShr ? AC->lshr(C2) : AC->ashr(C2);
+ return BinaryOperator::CreateShl(ConstantInt::get(Ty, C), X);
----------------
pfusik wrote:
Indeed. If C1>=0, we can set both nuw and nsw.
https://github.com/llvm/llvm-project/pull/192982
More information about the llvm-commits
mailing list