[llvm] [VPlan] Optimize FindLast of (binop %IV, live-in) by sinking. (PR #183911)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 11:52:17 PDT 2026
================
@@ -5695,25 +5695,64 @@ void VPlanTransforms::addExitUsersForFirstOrderRecurrences(VPlan &Plan,
}
}
+/// Check if \p V is a binary expression of a widened IV and a loop-invariant
+/// value. Returns the widened IV if found, nullptr otherwise.
+static VPWidenIntOrFpInductionRecipe *getExpressionIV(VPValue *V) {
+ auto *BinOp = dyn_cast<VPWidenRecipe>(V);
+ if (!BinOp || !Instruction::isBinaryOp(BinOp->getOpcode()))
+ return nullptr;
+
+ VPValue *WidenIVCandidate = BinOp->getOperand(0);
+ VPValue *InvariantCandidate = BinOp->getOperand(1);
+ if (!isa<VPWidenIntOrFpInductionRecipe>(WidenIVCandidate))
+ std::swap(WidenIVCandidate, InvariantCandidate);
+
+ if (!InvariantCandidate->isDefinedOutsideLoopRegions())
+ return nullptr;
+
+ return dyn_cast<VPWidenIntOrFpInductionRecipe>(WidenIVCandidate);
+}
+
+/// Create a scalar version of \p BinOp and place it after \p ScalarIV's
+/// defining recipe, replacing \p WidenIV with \p ScalarIV.
+static VPValue *cloneBinOpForScalarIV(VPWidenRecipe *BinOp, VPValue *ScalarIV,
+ VPWidenIntOrFpInductionRecipe *WidenIV) {
+ assert(Instruction::isBinaryOp(BinOp->getOpcode()) &&
+ BinOp->getNumOperands() == 2 && "BinOp must have 2 operands");
+ auto *ClonedOp = BinOp->clone();
+ if (ClonedOp->getOperand(0) == WidenIV) {
+ ClonedOp->setOperand(0, ScalarIV);
+ } else {
+ assert(ClonedOp->getOperand(1) == WidenIV && "one operand must be WideIV");
+ ClonedOp->setOperand(1, ScalarIV);
+ }
+ ClonedOp->insertAfter(ScalarIV->getDefiningRecipe());
+ return ClonedOp;
+}
+
void VPlanTransforms::optimizeFindIVReductions(VPlan &Plan,
PredicatedScalarEvolution &PSE,
Loop &L) {
ScalarEvolution &SE = *PSE.getSE();
VPRegionBlock *VectorLoopRegion = Plan.getVectorLoopRegion();
- // Helper lambda to check if the IV range excludes the sentinel value.
- auto CheckSentinel = [&SE](const SCEV *IVSCEV, bool UseMax,
- bool Signed) -> std::optional<APInt> {
+ // Helper lambda to check if the IV range excludes the sentinel value. Try
+ // signed first, then unsigned.
+ auto CheckSentinel =
+ [&SE](const SCEV *IVSCEV,
+ bool UseMax) -> std::optional<std::pair<APInt, bool>> {
unsigned BW = IVSCEV->getType()->getScalarSizeInBits();
- APInt Sentinel =
- UseMax
- ? (Signed ? APInt::getSignedMinValue(BW) : APInt::getMinValue(BW))
- : (Signed ? APInt::getSignedMaxValue(BW) : APInt::getMaxValue(BW));
-
- ConstantRange IVRange =
- Signed ? SE.getSignedRange(IVSCEV) : SE.getUnsignedRange(IVSCEV);
- if (!IVRange.contains(Sentinel))
- return Sentinel;
+ for (bool Signed : {true, false}) {
+ APInt Sentinel = UseMax ? (Signed ? APInt::getSignedMinValue(BW)
+ : APInt::getMinValue(BW))
+ : (Signed ? APInt::getSignedMaxValue(BW)
+ : APInt::getMaxValue(BW));
+
+ ConstantRange IVRange =
----------------
fhahn wrote:
Would be good to check, but the additiona constraint woudl be that there canno be a larger/smaller value so we can use min/max. Given that, we may be able to choose a different value, but in all those cases min/max should also be valid.
https://github.com/llvm/llvm-project/pull/183911
More information about the llvm-commits
mailing list