[llvm-branch-commits] [llvm] release/23.x: [VPlan] Avoid recursive-fan-out of usesFirstLaneOnly (NFC) (#229130) (PR #234910)
Nikita Popov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Oct 23 03:56:44 PDT 2026
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/234910
(cherry picked from commit 7a1922aff2176215675b94bb1b3dca331eda2d9a)
>From c319cd18e8d014442635ecd631844c807a3b023b Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 23 Oct 2026 11:02:04 +0100
Subject: [PATCH] [VPlan] Avoid recursive-fan-out of usesFirstLaneOnly (NFC)
(#229130)
This fixes compile-time for the original slow case mentioned in #228403,
without affecting compile-time for smaller cases.
Ref:
https://github.com/llvm/llvm-project/pull/220730#issuecomment-5992802596.
Fixes #228403.
Co-authored-by: Florian Hahn <flo at fhahn.com>
(cherry picked from commit 7a1922aff2176215675b94bb1b3dca331eda2d9a)
---
.../lib/Transforms/Vectorize/VPlanRecipes.cpp | 20 +++--------
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 36 +++++++++++++++++--
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 8 +++--
3 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 6eb2ba6c06e182..9aea7d11a6aaae 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1657,7 +1657,7 @@ bool VPInstruction::opcodeMayReadOrWriteFromMemory() const {
bool VPInstruction::usesFirstLaneOnly(const VPValue *Op) const {
assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
- if (Instruction::isBinaryOp(getOpcode()) || Instruction::isCast(getOpcode()))
+ if (vputils::propagatesFirstLaneUse(this, Op))
return vputils::onlyFirstLaneUsed(this);
switch (getOpcode()) {
@@ -1668,15 +1668,6 @@ bool VPInstruction::usesFirstLaneOnly(const VPValue *Op) const {
case Instruction::InsertElement:
return Op == getOperand(1) || Op == getOperand(2);
case Instruction::PHI:
- return true;
- case Instruction::FCmp:
- case Instruction::ICmp:
- case Instruction::Select:
- case Instruction::Or:
- case Instruction::Freeze:
- case VPInstruction::Not:
- // TODO: Cover additional opcodes.
- return vputils::onlyFirstLaneUsed(this);
case Instruction::Load:
case VPInstruction::ActiveLaneMask:
case VPInstruction::ExplicitVectorLength:
@@ -1697,15 +1688,10 @@ bool VPInstruction::usesFirstLaneOnly(const VPValue *Op) const {
// Before replicating, it will have only a single operand.
return getNumOperands() > 1;
case VPInstruction::PtrAdd:
- return Op == getOperand(0) || vputils::onlyFirstLaneUsed(this);
- case VPInstruction::WidePtrAdd:
- // WidePtrAdd supports scalar and vector base addresses.
- return false;
case VPInstruction::ExitingIVValue:
case VPInstruction::ExtractLane:
return Op == getOperand(0);
- };
- llvm_unreachable("switch should return");
+ }
}
bool VPInstruction::usesFirstPartOnly(const VPValue *Op) const {
@@ -4925,6 +4911,8 @@ void VPReductionPHIRecipe::printRecipe(raw_ostream &O, const Twine &Indent,
bool VPBlendRecipe::usesFirstLaneOnly(const VPValue *Op) const {
assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
+ assert(vputils::propagatesFirstLaneUse(this, Op) &&
+ "Blend should propagate first lane use");
return vputils::onlyFirstLaneUsed(this);
}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 7b4ffce87434c2..4eb6d377b56c54 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -23,9 +23,41 @@ using namespace llvm;
using namespace llvm::VPlanPatternMatch;
using namespace llvm::SCEVPatternMatch;
+bool vputils::propagatesFirstLaneUse(const VPUser *U, const VPValue *Op) {
+ if (isa<VPBlendRecipe>(U))
+ return true;
+ auto *VPI = dyn_cast<VPInstruction>(U);
+ if (!VPI)
+ return false;
+ unsigned Opcode = VPI->getOpcode();
+ switch (Opcode) {
+ case Instruction::FCmp:
+ case Instruction::ICmp:
+ case Instruction::Select:
+ case Instruction::Freeze:
+ case VPInstruction::Not:
+ // TODO: Cover additional opcodes.
+ return true;
+ case VPInstruction::PtrAdd:
+ return Op != VPI->getOperand(0);
+ default:
+ return Instruction::isBinaryOp(Opcode) || Instruction::isCast(Opcode);
+ }
+}
+
bool vputils::onlyFirstLaneUsed(const VPValue *Def) {
- return all_of(Def->users(),
- [Def](const VPUser *U) { return U->usesFirstLaneOnly(Def); });
+ SmallSetVector<const VPValue *, 8> Worklist;
+ Worklist.insert(Def);
+ for (unsigned I = 0; I != Worklist.size(); ++I) {
+ const VPValue *Cur = Worklist[I];
+ for (const VPUser *U : Cur->users()) {
+ if (propagatesFirstLaneUse(U, Cur))
+ Worklist.insert(cast<VPSingleDefRecipe>(U));
+ else if (!U->usesFirstLaneOnly(Cur))
+ return false;
+ }
+ }
+ return true;
}
bool vputils::onlyFirstPartUsed(const VPValue *Def) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 2980b704ec8dac..1aa404cf1714ac 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -23,10 +23,14 @@ class PredicatedScalarEvolution;
namespace llvm {
namespace vputils {
-/// Returns true if only the first lane of \p Def is used.
+/// Returns true if checking whether \p U uses the first lane of \p Op requires
+/// recursively checking \p Op's users.
+bool propagatesFirstLaneUse(const VPUser *U, const VPValue *Op);
+
+/// Returns true if only the first lane of \p Def is used by all users.
bool onlyFirstLaneUsed(const VPValue *Def);
-/// Returns true if only the first part of \p Def is used.
+/// Returns true if only the first part of \p Def is used by all users.
bool onlyFirstPartUsed(const VPValue *Def);
/// Returns true if only scalar values of \p Def are used by all users.
More information about the llvm-branch-commits
mailing list