[llvm] [InstCombine] Fold max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2 (PR #140526)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 24 10:21:38 PDT 2025
================
@@ -1174,6 +1174,109 @@ static Instruction *moveAddAfterMinMax(IntrinsicInst *II,
return IsSigned ? BinaryOperator::CreateNSWAdd(NewMinMax, Add->getOperand(1))
: BinaryOperator::CreateNUWAdd(NewMinMax, Add->getOperand(1));
}
+
+/// Returns weather the it holds for (X LOp Y) ROp Z -> (X ROp Z) LOp (Y ROp Z)
+
+static bool rightDistributesOverLeft(Instruction::BinaryOps ROp, bool HasNUW,
+ bool HasNSW, Intrinsic::ID LOp) {
+ switch (LOp) {
+ case Intrinsic::umax:
+ if (HasNUW && (ROp == Instruction::AShr || ROp == Instruction::LShr ||
+ ROp == Instruction::UDiv || ROp == Instruction::Mul ||
+ ROp == Instruction::Add))
+ return true;
+ return false;
+ case Intrinsic::umin:
+ if (HasNUW && (ROp == Instruction::AShr || ROp == Instruction::LShr ||
+ ROp == Instruction::UDiv || ROp == Instruction::Sub))
+ return true;
+ return false;
+ case Intrinsic::smax:
+ case Intrinsic::smin:
+ if (HasNSW && ROp == Instruction::AShr)
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+/// Try canonicalize max(max(X,C1) binop C2, C3) -> max(X binop C2, max(C1
+/// binop C2, C3))
+/// -> max(X binop C2, C4) //
+
+static Instruction *reduceMinMax(IntrinsicInst *II,
+ InstCombiner::BuilderTy &Builder,
+ const DataLayout &DL) {
+ Intrinsic::ID MinMaxID = II->getIntrinsicID();
+ assert(isa<MinMaxIntrinsic>(II) && "Expected a min or max intrinsic");
+
+ Value *Op0 = II->getArgOperand(0), *Op1 = II->getArgOperand(1);
+ Value *InnerMax;
+ Constant *C2, *C3;
+ if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_ImmConstant(C2)))) ||
+ !match(Op1, m_ImmConstant(C3)))
+ return nullptr;
+
+ auto *BinOpInst = cast<BinaryOperator>(Op0);
+ Instruction::BinaryOps BinOp = BinOpInst->getOpcode();
+
+ InnerMax = BinOpInst->getOperand(0);
+
+ auto *InnerMinMaxInst = dyn_cast<MinMaxIntrinsic>(BinOpInst->getOperand(0));
+ if (!InnerMinMaxInst || !InnerMinMaxInst->hasOneUse())
+ return nullptr;
+
+ bool IsSigned = InnerMinMaxInst->isSigned();
+ if (InnerMinMaxInst->getIntrinsicID() != MinMaxID)
+ return nullptr;
+
+ if ((IsSigned && !BinOpInst->hasNoSignedWrap()) ||
+ (!IsSigned && !BinOpInst->hasNoUnsignedWrap()))
+ return nullptr;
+
+ if (!rightDistributesOverLeft(BinOp, !BinOpInst->hasNoUnsignedWrap(),
+ !BinOpInst->hasNoSignedWrap(),
+ InnerMinMaxInst->getIntrinsicID()))
+ return nullptr;
+
+ Constant *C1;
+ if (!match(InnerMinMaxInst->getRHS(), m_ImmConstant(C1)))
+ return nullptr;
+ Constant *C1BinOpC2 = ConstantFoldBinaryOpOperands(BinOp, C1, C2, DL);
+ Constant *C4 = ConstantFoldBinaryIntrinsic(MinMaxID, C1BinOpC2, C3,
+ C3->getType(), nullptr);
+
+ // Create new X binop C2
+ Value *NewBinOp = Builder.CreateBinOp(BinOp, InnerMinMaxInst->getOperand(0),
+ BinOpInst->getOperand(1));
+
+ // Set overflow flags on new binary operation
+ if (auto *NewBinInst = dyn_cast<Instruction>(NewBinOp)) {
----------------
dtcxzyw wrote:
It will assert when the binop is not an OverflowingBinaryOperator (e.g, a shift instruction).
https://github.com/llvm/llvm-project/pull/140526
More information about the llvm-commits
mailing list