[llvm] [VPlan] Add visited set for onlyFirstLaneUsed() (PR #228438)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 06:33:42 PDT 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/228438

This avoids exponential recursive fan-out, and addresses the original slow compile case from #228403 (but not the reduced case).

This ends up having to thread the Visited parameter through lots of places, not sure if there is some better solution here.

AI assisted.

>From 0d40ab9cbedbbad60c746fece50c8348f30dd66c Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Fri, 2 Oct 2026 15:24:14 +0200
Subject: [PATCH] [VPlan] Add visited set for onlyFirstLaneUsed()

This avoids exponential recursive fan-out, and addresses the
original slow compile case from #228403.
---
 llvm/lib/Transforms/Vectorize/VPlan.h         | 102 +++++++++++++-----
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  27 +++--
 llvm/lib/Transforms/Vectorize/VPlanUtils.cpp  |  13 ++-
 llvm/lib/Transforms/Vectorize/VPlanUtils.h    |   5 +
 llvm/lib/Transforms/Vectorize/VPlanValue.h    |  11 +-
 5 files changed, 118 insertions(+), 40 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 55741c2ac0e42d..74156e26389cb4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -1565,11 +1565,13 @@ class LLVM_ABI_FOR_TEST VPInstruction : public VPRecipeWithIRFlags,
   bool opcodeMayReadOrWriteFromMemory() const;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override;
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override;
 
   /// Returns true if the recipe only uses scalars of operand \p Op.
   bool usesScalars(const VPValue *Op) const override {
-    return isSingleScalar() || usesFirstLaneOnly(Op);
+    return isSingleScalar() || VPUser::usesFirstLaneOnly(Op);
   }
 
   /// Returns true if the recipe only uses the first part of operand \p Op.
@@ -1758,7 +1760,9 @@ class VPIRInstruction : public VPRecipeBase {
     return true;
   }
 
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
@@ -1864,7 +1868,9 @@ class LLVM_ABI_FOR_TEST VPWidenRecipe : public VPRecipeWithIRFlags,
 #endif
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return Opcode == Instruction::Select && Op == getOperand(0) &&
@@ -2041,7 +2047,9 @@ class LLVM_ABI_FOR_TEST VPWidenIntrinsicRecipe : public VPRecipeWithIRFlags,
   /// Returns true if the intrinsic may have side-effects.
   bool mayHaveSideEffects() const { return MayHaveSideEffects; }
 
-  bool usesFirstLaneOnly(const VPValue *Op) const override;
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override;
 
 protected:
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -2146,7 +2154,9 @@ class LLVM_ABI_FOR_TEST VPWidenCallRecipe : public VPRecipeWithIRFlags,
   const_operand_range args() const { return drop_end(operands()); }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override;
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override;
 
 protected:
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -2194,7 +2204,9 @@ class VPHistogramRecipe : public VPRecipeBase, public VPIRMetadata {
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return Op == getOperand(1);
@@ -2254,7 +2266,9 @@ class LLVM_ABI_FOR_TEST VPWidenGEPRecipe : public VPRecipeWithIRFlags {
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override;
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override;
 
 protected:
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -2309,7 +2323,9 @@ class VPVectorEndPointerRecipe : public VPRecipeWithIRFlags {
 
   void execute(VPTransformState &State) override;
 
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
@@ -2381,7 +2397,9 @@ class VPVectorPointerRecipe : public VPRecipeWithIRFlags {
 
   Type *getSourceElementType() const { return SourceElementTy; }
 
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
@@ -2591,7 +2609,9 @@ class VPWidenInductionRecipe : public VPHeaderPHIRecipe {
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     // The recipe creates its own wide start value, so it only requests the
@@ -2814,7 +2834,9 @@ struct VPFirstOrderRecurrencePHIRecipe : public VPHeaderPHIRecipe {
                               VPCostContext &Ctx) const override;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return Op == getStartValue();
@@ -2928,7 +2950,9 @@ class VPReductionPHIRecipe : public VPHeaderPHIRecipe, public VPIRFlags {
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return isOrdered() || isInLoop();
@@ -3017,7 +3041,9 @@ class LLVM_ABI_FOR_TEST VPBlendRecipe : public VPRecipeWithIRFlags {
                               VPCostContext &Ctx) const override;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override;
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override;
 
 protected:
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -3110,7 +3136,9 @@ class LLVM_ABI_FOR_TEST VPInterleaveBase : public VPRecipeBase,
                               VPCostContext &Ctx) const override;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override = 0;
+  bool usesFirstLaneOnly(
+      const VPValue *Op,
+      SmallPtrSetImpl<const VPValue *> &Visited) const override = 0;
 
   /// Returns the number of stored operands of this interleave group. Returns 0
   /// for load interleave groups.
@@ -3149,7 +3177,9 @@ class LLVM_ABI_FOR_TEST VPInterleaveRecipe final : public VPInterleaveBase {
   /// Generate the wide load or store, and shuffles.
   void execute(VPTransformState &State) override;
 
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return Op == getAddr() && !llvm::is_contained(getStoredValues(), Op);
@@ -3198,7 +3228,9 @@ class LLVM_ABI_FOR_TEST VPInterleaveEVLRecipe final : public VPInterleaveBase {
   void execute(VPTransformState &State) override;
 
   /// The recipe only uses the first lane of the address, and EVL operand.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return (Op == getAddr() && !llvm::is_contained(getStoredValues(), Op)) ||
@@ -3369,7 +3401,9 @@ class LLVM_ABI_FOR_TEST VPReductionEVLRecipe : public VPReductionRecipe {
   VPValue *getEVL() const { return getOperand(2); }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return Op == getEVL();
@@ -3455,7 +3489,9 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
   bool isPredicated() const { return IsPredicated; }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return isSingleScalar();
@@ -3834,7 +3870,9 @@ struct LLVM_ABI_FOR_TEST VPWidenLoadRecipe final : public VPSingleDefRecipe,
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     // Widened, consecutive loads operations only demand the first lane of
@@ -3888,7 +3926,9 @@ struct LLVM_ABI_FOR_TEST VPWidenLoadEVLRecipe final
                               VPCostContext &Ctx) const override;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     // Widened loads only demand the first lane of EVL and consecutive loads
@@ -3940,7 +3980,9 @@ struct LLVM_ABI_FOR_TEST VPWidenStoreRecipe final : public VPRecipeBase,
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     // Widened, consecutive stores only demand the first lane of their address,
@@ -3993,7 +4035,9 @@ struct LLVM_ABI_FOR_TEST VPWidenStoreEVLRecipe final
                               VPCostContext &Ctx) const override;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     if (Op == getEVL()) {
@@ -4112,7 +4156,9 @@ class VPCurrentIterationPHIRecipe : public VPHeaderPHIRecipe {
   }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
@@ -4226,7 +4272,9 @@ class LLVM_ABI_FOR_TEST VPDerivedIVRecipe : public VPRecipeWithIRFlags {
   InductionDescriptor::InductionKind getInductionKind() const { return Kind; }
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
@@ -4302,7 +4350,9 @@ class LLVM_ABI_FOR_TEST VPScalarIVStepsRecipe : public VPRecipeWithIRFlags {
   bool doesGeneratePerAllLanes() const;
 
   /// Returns true if the recipe only uses the first lane of operand \p Op.
-  bool usesFirstLaneOnly(const VPValue *Op) const override {
+  bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const override {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return true;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index ae9001dc8c67fa..350ef7a938a073 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1758,10 +1758,11 @@ bool VPInstruction::opcodeMayReadOrWriteFromMemory() const {
   }
 }
 
-bool VPInstruction::usesFirstLaneOnly(const VPValue *Op) const {
+bool VPInstruction::usesFirstLaneOnly(
+    const VPValue *Op, SmallPtrSetImpl<const VPValue *> &Visited) const {
   assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
   if (Instruction::isBinaryOp(getOpcode()) || Instruction::isCast(getOpcode()))
-    return vputils::onlyFirstLaneUsed(this);
+    return vputils::onlyFirstLaneUsed(this, Visited);
 
   switch (getOpcode()) {
   default:
@@ -1781,7 +1782,7 @@ bool VPInstruction::usesFirstLaneOnly(const VPValue *Op) const {
   case Instruction::Freeze:
   case VPInstruction::Not:
     // TODO: Cover additional opcodes.
-    return vputils::onlyFirstLaneUsed(this);
+    return vputils::onlyFirstLaneUsed(this, Visited);
   case Instruction::Load:
   case VPInstruction::ActiveLaneMask:
   case VPInstruction::WideActiveLaneMask:
@@ -1802,7 +1803,7 @@ 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);
+    return Op == getOperand(0) || vputils::onlyFirstLaneUsed(this, Visited);
   case VPInstruction::WidePtrAdd:
     // WidePtrAdd supports scalar and vector base addresses.
     return false;
@@ -2244,7 +2245,7 @@ void VPWidenCallRecipe::execute(VPTransformState &State) {
     if (!VFTy->getParamType(I.index())->isVectorTy())
       Arg = State.get(I.value(), VPLane(0));
     else
-      Arg = State.get(I.value(), usesFirstLaneOnly(I.value()));
+      Arg = State.get(I.value(), VPUser::usesFirstLaneOnly(I.value()));
     Args.push_back(Arg);
   }
 
@@ -2276,7 +2277,8 @@ InstructionCost VPWidenCallRecipe::computeCallCost(Function *Variant,
                                   Ctx.CostKind);
 }
 
-bool VPWidenCallRecipe::usesFirstLaneOnly(const VPValue *Op) const {
+bool VPWidenCallRecipe::usesFirstLaneOnly(
+    const VPValue *Op, SmallPtrSetImpl<const VPValue *> &Visited) const {
   assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
   assert(Variant && "Variant not set");
   FunctionType *VFTy = Variant->getFunctionType();
@@ -2340,7 +2342,7 @@ CallInst *VPWidenIntrinsicRecipe::createVectorCall(VPTransformState &State) {
                                            State.TTI))
       Arg = State.get(I.value(), VPLane(0));
     else
-      Arg = State.get(I.value(), usesFirstLaneOnly(I.value()));
+      Arg = State.get(I.value(), VPUser::usesFirstLaneOnly(I.value()));
     if (isVectorIntrinsicWithOverloadTypeAtArg(VectorIntrinsicID, I.index(),
                                                State.TTI))
       TysForDecl.push_back(Arg->getType());
@@ -2433,7 +2435,8 @@ StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
   return Intrinsic::getBaseName(VectorIntrinsicID);
 }
 
-bool VPWidenIntrinsicRecipe::usesFirstLaneOnly(const VPValue *Op) const {
+bool VPWidenIntrinsicRecipe::usesFirstLaneOnly(
+    const VPValue *Op, SmallPtrSetImpl<const VPValue *> &Visited) const {
   assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
   return all_of(enumerate(operands()), [this, &Op](const auto &X) {
     auto [Idx, V] = X;
@@ -3327,7 +3330,8 @@ void VPScalarIVStepsRecipe::printRecipe(raw_ostream &O, const Twine &Indent,
 }
 #endif
 
-bool VPWidenGEPRecipe::usesFirstLaneOnly(const VPValue *Op) const {
+bool VPWidenGEPRecipe::usesFirstLaneOnly(
+    const VPValue *Op, SmallPtrSetImpl<const VPValue *> &Visited) const {
   assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
   return vputils::isSingleScalar(Op);
 }
@@ -5158,9 +5162,10 @@ void VPReductionPHIRecipe::printRecipe(raw_ostream &O, const Twine &Indent,
 }
 #endif
 
-bool VPBlendRecipe::usesFirstLaneOnly(const VPValue *Op) const {
+bool VPBlendRecipe::usesFirstLaneOnly(
+    const VPValue *Op, SmallPtrSetImpl<const VPValue *> &Visited) const {
   assert(is_contained(operands(), Op) && "Op must be an operand of the recipe");
-  return vputils::onlyFirstLaneUsed(this);
+  return vputils::onlyFirstLaneUsed(this, Visited);
 }
 
 void VPWidenPHIRecipe::execute(VPTransformState &State) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index b68040911c1429..e905f297b33af3 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -33,8 +33,17 @@ using namespace llvm::VPlanPatternMatch;
 using namespace llvm::SCEVPatternMatch;
 
 bool vputils::onlyFirstLaneUsed(const VPValue *Def) {
-  return all_of(Def->users(),
-                [Def](const VPUser *U) { return U->usesFirstLaneOnly(Def); });
+  SmallPtrSet<const VPValue *, 8> Visited;
+  return onlyFirstLaneUsed(Def, Visited);
+}
+
+bool vputils::onlyFirstLaneUsed(const VPValue *Def,
+                                SmallPtrSetImpl<const VPValue *> &Visited) {
+  if (!Visited.insert(Def).second)
+    return true;
+  return all_of(Def->users(), [Def, &Visited](const VPUser *U) {
+    return U->usesFirstLaneOnly(Def, Visited);
+  });
 }
 
 bool vputils::onlyFirstPartUsed(const VPValue *Def) {
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index c046239e2f9830..7e01e00f51ed5d 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -28,6 +28,11 @@ namespace vputils {
 /// Returns true if only the first lane of \p Def is used.
 bool onlyFirstLaneUsed(const VPValue *Def);
 
+/// Returns true if only the first lane of \p Def is used. Accepts a \p Visited
+/// set for recursive calls inside usesFirstLaneOnly().
+bool onlyFirstLaneUsed(const VPValue *Def,
+                       SmallPtrSetImpl<const VPValue *> &Visited);
+
 /// Returns true if only the first part of \p Def is used.
 bool onlyFirstPartUsed(const VPValue *Def);
 
diff --git a/llvm/lib/Transforms/Vectorize/VPlanValue.h b/llvm/lib/Transforms/Vectorize/VPlanValue.h
index 7256c3b4e1f5d3..1803ab2202d199 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanValue.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanValue.h
@@ -21,6 +21,7 @@
 #define LLVM_TRANSFORMS_VECTORIZE_VPLAN_VALUE_H
 
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/ADT/iterator_range.h"
@@ -484,12 +485,20 @@ class LLVM_ABI_FOR_TEST VPUser {
 
   /// Returns true if the VPUser only uses the first lane of operand \p Op.
   /// Conservatively returns false.
-  virtual bool usesFirstLaneOnly(const VPValue *Op) const {
+  virtual bool
+  usesFirstLaneOnly(const VPValue *Op,
+                    SmallPtrSetImpl<const VPValue *> &Visited) const {
     assert(is_contained(operands(), Op) &&
            "Op must be an operand of the recipe");
     return false;
   }
 
+  /// Convenience wrapper that creates a fresh Visited set.
+  bool usesFirstLaneOnly(const VPValue *Op) const {
+    SmallPtrSet<const VPValue *, 8> Visited;
+    return usesFirstLaneOnly(Op, Visited);
+  }
+
   /// Returns true if the VPUser only uses the first part of operand \p Op.
   /// Conservatively returns false.
   virtual bool usesFirstPartOnly(const VPValue *Op) const {



More information about the llvm-commits mailing list