Skip to content

[LV] Add support for cmp reductions with decreasing IVs. #140451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions llvm/include/llvm/Analysis/IVDescriptors.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ enum class RecurKind {
FMulAdd, ///< Sum of float products with llvm.fmuladd(a * b + sum).
AnyOf, ///< AnyOf reduction with select(cmp(),x,y) where one of (x,y) is
///< loop invariant, and both x and y are integer type.
FindFirstIVSMin, /// FindFirst reduction with select(icmp(),x,y) where one of
///< (x,y) is a decreasing loop induction, and both x and y
///< are integer type, producing a SMin reduction.
FindLastIVSMax, ///< FindLast reduction with select(cmp(),x,y) where one of
///< (x,y) is increasing loop induction, and both x and y
///< are integer type, producing a SMax reduction.
Expand Down Expand Up @@ -165,13 +168,13 @@ class RecurrenceDescriptor {
/// Returns a struct describing whether the instruction is either a
/// Select(ICmp(A, B), X, Y), or
/// Select(FCmp(A, B), X, Y)
/// where one of (X, Y) is an increasing loop induction variable, and the
/// other is a PHI value.
/// where one of (X, Y) is an increasing (FindLast) or decreasing (FindFirst)
/// loop induction variable, and the other is a PHI value.
// TODO: Support non-monotonic variable. FindLast does not need be restricted
// to increasing loop induction variables.
LLVM_ABI static InstDesc isFindLastIVPattern(Loop *TheLoop, PHINode *OrigPhi,
Instruction *I,
ScalarEvolution &SE);
LLVM_ABI static InstDesc isFindIVPattern(RecurKind Kind, Loop *TheLoop,
PHINode *OrigPhi, Instruction *I,
ScalarEvolution &SE);

/// Returns a struct describing if the instruction is a
/// Select(FCmp(X, Y), (Z = X op PHINode), PHINode) instruction pattern.
Expand Down Expand Up @@ -259,6 +262,12 @@ class RecurrenceDescriptor {
return Kind == RecurKind::AnyOf;
}

/// Returns true if the recurrence kind is of the form
/// select(cmp(),x,y) where one of (x,y) is decreasing loop induction.
static bool isFindFirstIVRecurrenceKind(RecurKind Kind) {
return Kind == RecurKind::FindFirstIVSMin;
}

/// Returns true if the recurrence kind is of the form
/// select(cmp(),x,y) where one of (x,y) is increasing loop induction.
static bool isFindLastIVRecurrenceKind(RecurKind Kind) {
Expand All @@ -269,22 +278,35 @@ class RecurrenceDescriptor {
/// Returns true if recurrece kind is a signed redux kind.
static bool isSignedRecurrenceKind(RecurKind Kind) {
return Kind == RecurKind::SMax || Kind == RecurKind::SMin ||
Kind == RecurKind::FindFirstIVSMin ||
Kind == RecurKind::FindLastIVSMax;
}

/// Returns true if the recurrence kind is of the form
/// select(cmp(),x,y) where one of (x,y) is an increasing or decreasing loop
/// induction.
static bool isFindIVRecurrenceKind(RecurKind Kind) {
return isFindFirstIVRecurrenceKind(Kind) ||
isFindLastIVRecurrenceKind(Kind);
}

/// Returns the type of the recurrence. This type can be narrower than the
/// actual type of the Phi if the recurrence has been type-promoted.
Type *getRecurrenceType() const { return RecurrenceType; }

/// Returns the sentinel value for FindLastIV recurrences to replace the start
/// value.
/// Returns the sentinel value for FindFirstIV & FindLastIV recurrences to
/// replace the start value.
Value *getSentinelValue() const {
assert(isFindLastIVRecurrenceKind(Kind) && "Unexpected recurrence kind");
Type *Ty = StartValue->getType();
unsigned BW = Ty->getIntegerBitWidth();
if (isFindLastIVRecurrenceKind(Kind)) {
return ConstantInt::get(Ty, isSignedRecurrenceKind(Kind)
? APInt::getSignedMinValue(BW)
: APInt::getMinValue(BW));
}
return ConstantInt::get(Ty, isSignedRecurrenceKind(Kind)
? APInt::getSignedMinValue(BW)
: APInt::getMinValue(BW));
? APInt::getSignedMaxValue(BW)
: APInt::getMaxValue(BW));
}

/// Returns a reference to the instructions used for type-promoting the
Expand Down
61 changes: 47 additions & 14 deletions llvm/lib/Analysis/IVDescriptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bool RecurrenceDescriptor::isIntegerRecurrenceKind(RecurKind Kind) {
case RecurKind::UMax:
case RecurKind::UMin:
case RecurKind::AnyOf:
case RecurKind::FindFirstIVSMin:
case RecurKind::FindLastIVSMax:
case RecurKind::FindLastIVUMax:
return true;
Expand Down Expand Up @@ -684,8 +685,9 @@ RecurrenceDescriptor::isAnyOfPattern(Loop *Loop, PHINode *OrigPhi,
// value of the data type or a non-constant value by using mask and multiple
// reduction operations.
RecurrenceDescriptor::InstDesc
RecurrenceDescriptor::isFindLastIVPattern(Loop *TheLoop, PHINode *OrigPhi,
Instruction *I, ScalarEvolution &SE) {
RecurrenceDescriptor::isFindIVPattern(RecurKind Kind, Loop *TheLoop,
PHINode *OrigPhi, Instruction *I,
ScalarEvolution &SE) {
// TODO: Support the vectorization of FindLastIV when the reduction phi is
// used by more than one select instruction. This vectorization is only
// performed when the SCEV of each increasing induction variable used by the
Expand Down Expand Up @@ -713,36 +715,61 @@ RecurrenceDescriptor::isFindLastIVPattern(Loop *TheLoop, PHINode *OrigPhi,
return std::nullopt;

const SCEV *Step = AR->getStepRecurrence(SE);
if (!SE.isKnownPositive(Step))
if ((isFindFirstIVRecurrenceKind(Kind) && !SE.isKnownNegative(Step)) ||
(isFindLastIVRecurrenceKind(Kind) && !SE.isKnownPositive(Step)))
return std::nullopt;

// Keep the minimum value of the recurrence type as the sentinel value.
// The maximum acceptable range for the increasing induction variable,
// called the valid range, will be defined as

// Keep the minimum (FindLast) or maximum (FindFirst) value of the
// recurrence type as the sentinel value. The maximum acceptable range for
// the induction variable, called the valid range, will be defined as
// [<sentinel value> + 1, <sentinel value>)
// where <sentinel value> is [Signed|Unsigned]Min(<recurrence type>)
// where <sentinel value> is [Signed|Unsigned]Min(<recurrence type>) for
// FindLastIV or [Signed|Unsigned]Max(<recurrence type>) for FindFirstIV.
// TODO: This range restriction can be lifted by adding an additional
// virtual OR reduction.
auto CheckRange = [&](bool IsSigned) {
const ConstantRange IVRange =
IsSigned ? SE.getSignedRange(AR) : SE.getUnsignedRange(AR);
unsigned NumBits = Ty->getIntegerBitWidth();
const APInt Sentinel = IsSigned ? APInt::getSignedMinValue(NumBits)
: APInt::getMinValue(NumBits);
const ConstantRange ValidRange =
ConstantRange::getNonEmpty(Sentinel + 1, Sentinel);
LLVM_DEBUG(dbgs() << "LV: FindLastIV valid range is " << ValidRange
ConstantRange ValidRange = ConstantRange::getEmpty(NumBits);
if (isFindLastIVRecurrenceKind(Kind)) {
APInt Sentinel = IsSigned ? APInt::getSignedMinValue(NumBits)
: APInt::getMinValue(NumBits);
ValidRange = ConstantRange::getNonEmpty(Sentinel + 1, Sentinel);
} else {
assert(IsSigned && "Only FindFirstIV with SMax is supported currently");
ValidRange =
ConstantRange::getNonEmpty(APInt::getSignedMinValue(NumBits),
APInt::getSignedMaxValue(NumBits) - 1);
}

LLVM_DEBUG(dbgs() << "LV: "
<< (isFindLastIVRecurrenceKind(Kind) ? "FindLastIV"
: "FindFirstIV")
<< " valid range is " << ValidRange
<< ", and the range of " << *AR << " is " << IVRange
<< "\n");

// Ensure the induction variable does not wrap around by verifying that
// its range is fully contained within the valid range.
return ValidRange.contains(IVRange);
};
if (isFindLastIVRecurrenceKind(Kind)) {
if (CheckRange(true))
return RecurKind::FindLastIVSMax;
if (CheckRange(false))
return RecurKind::FindLastIVUMax;
return std::nullopt;
}
assert(isFindFirstIVRecurrenceKind(Kind) &&
"Kind must either be a FindLastIV or FindFirstIV");

if (CheckRange(true))
return RecurKind::FindLastIVSMax;
if (CheckRange(false))
return RecurKind::FindLastIVUMax;
return RecurKind::FindFirstIVSMin;
return std::nullopt;
};

Expand Down Expand Up @@ -888,8 +915,8 @@ RecurrenceDescriptor::InstDesc RecurrenceDescriptor::isRecurrenceInstr(
if (Kind == RecurKind::FAdd || Kind == RecurKind::FMul ||
Kind == RecurKind::Add || Kind == RecurKind::Mul)
return isConditionalRdxPattern(I);
if (isFindLastIVRecurrenceKind(Kind) && SE)
return isFindLastIVPattern(L, OrigPhi, I, *SE);
if (isFindIVRecurrenceKind(Kind) && SE)
return isFindIVPattern(Kind, L, OrigPhi, I, *SE);
[[fallthrough]];
case Instruction::FCmp:
case Instruction::ICmp:
Expand Down Expand Up @@ -1003,6 +1030,11 @@ bool RecurrenceDescriptor::isReductionPHI(PHINode *Phi, Loop *TheLoop,
LLVM_DEBUG(dbgs() << "Found a FindLastIV reduction PHI." << *Phi << "\n");
return true;
}
if (AddReductionVar(Phi, RecurKind::FindFirstIVSMin, TheLoop, FMF, RedDes, DB,
AC, DT, SE)) {
LLVM_DEBUG(dbgs() << "Found a FindFirstIV reduction PHI." << *Phi << "\n");
return true;
}
if (AddReductionVar(Phi, RecurKind::FMul, TheLoop, FMF, RedDes, DB, AC, DT,
SE)) {
LLVM_DEBUG(dbgs() << "Found an FMult reduction PHI." << *Phi << "\n");
Expand Down Expand Up @@ -1150,6 +1182,7 @@ unsigned RecurrenceDescriptor::getOpcode(RecurKind Kind) {
case RecurKind::Mul:
return Instruction::Mul;
case RecurKind::AnyOf:
case RecurKind::FindFirstIVSMin:
case RecurKind::FindLastIVSMax:
case RecurKind::FindLastIVUMax:
case RecurKind::Or:
Expand Down
8 changes: 5 additions & 3 deletions llvm/lib/Transforms/Utils/LoopUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1227,8 +1227,10 @@ Value *llvm::createFindLastIVReduction(IRBuilderBase &Builder, Value *Src,
RecurKind RdxKind, Value *Start,
Value *Sentinel) {
bool IsSigned = RecurrenceDescriptor::isSignedRecurrenceKind(RdxKind);
bool IsMaxRdx = RecurrenceDescriptor::isFindLastIVRecurrenceKind(RdxKind);
Value *MaxRdx = Src->getType()->isVectorTy()
? Builder.CreateIntMaxReduce(Src, IsSigned)
? (IsMaxRdx ? Builder.CreateIntMaxReduce(Src, IsSigned)
: Builder.CreateIntMinReduce(Src, IsSigned))
: Src;
// Correct the final reduction result back to the start value if the maximum
// reduction is sentinel value.
Expand Down Expand Up @@ -1324,8 +1326,8 @@ Value *llvm::createSimpleReduction(IRBuilderBase &Builder, Value *Src,
Value *llvm::createSimpleReduction(IRBuilderBase &Builder, Value *Src,
RecurKind Kind, Value *Mask, Value *EVL) {
assert(!RecurrenceDescriptor::isAnyOfRecurrenceKind(Kind) &&
!RecurrenceDescriptor::isFindLastIVRecurrenceKind(Kind) &&
"AnyOf or FindLastIV reductions are not supported.");
!RecurrenceDescriptor::isFindIVRecurrenceKind(Kind) &&
"AnyOf and FindIV reductions are not supported.");
Intrinsic::ID Id = getReductionIntrinsicID(Kind);
auto VPID = VPIntrinsic::getForIntrinsic(Id);
assert(VPReductionIntrinsic::isVPReduction(VPID) &&
Expand Down
Loading
Loading