Skip to content

Conversation

@fhahn
Copy link
Contributor

@fhahn fhahn commented Mar 9, 2025

This introduces a new VPSymbolicValueSC to be used for typed live-in VPValues without underlying IR. VPValue is updated to store either the type or an underlying value in an union. This allows keeping the size of VPValue unchanged.

The main motivation for adding the type is to not require passing the canonical IV type to VPTypeAnalysis.

While with this patch, we still need to pass the LLVMContext, this can also be removed in a future patch (see
9493c38)

@llvmbot
Copy link
Member

llvmbot commented Mar 9, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-vectorizers

Author: Florian Hahn (fhahn)

Changes

This introduces a new VPSymbolicValueSC to be used for typed live-in VPValues without underlying IR. VPValue is updated to store either the type or an underlying value in an union. This allows keeping the size of VPValue unchanged.

The main motivation for adding the type is to not require passing the canonical IV type to VPTypeAnalysis.

While with this patch, we still need to pass the LLVMContext, this can also be removed in a future patch (see
9493c38)


Full diff: https://github.com/llvm/llvm-project/pull/130507.diff

11 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/LoopVectorize.cpp (+6-4)
  • (modified) llvm/lib/Transforms/Vectorize/VPlan.cpp (+19-11)
  • (modified) llvm/lib/Transforms/Vectorize/VPlan.h (+7-5)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp (+2-7)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanAnalysis.h (+1-6)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanHelpers.h (+2-2)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp (+10-7)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp (+1-1)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanValue.h (+21-8)
  • (modified) llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp (+4-2)
  • (modified) llvm/unittests/Transforms/Vectorize/VPlanTestBase.h (+3-2)
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index b987863127994..54a4b74642e23 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4478,7 +4478,7 @@ void LoopVectorizationPlanner::emitInvalidCostRemarks(
 static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
                                 const TargetTransformInfo &TTI) {
   assert(VF.isVector() && "Checking a scalar VF?");
-  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType());
+  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType()->getContext());
   DenseSet<VPRecipeBase *> EphemeralRecipes;
   collectEphemeralRecipesForVPlan(Plan, EphemeralRecipes);
   // Set of already visited types.
@@ -9069,7 +9069,7 @@ static VPInstruction *addResumePhiRecipeForInduction(
 /// \p IVEndValues.
 static void addScalarResumePhis(VPRecipeBuilder &Builder, VPlan &Plan,
                                 DenseMap<VPValue *, VPValue *> &IVEndValues) {
-  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType());
+  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType()->getContext());
   auto *ScalarPH = Plan.getScalarPreheader();
   auto *MiddleVPBB = cast<VPBasicBlock>(ScalarPH->getSinglePredecessor());
   VPRegionBlock *VectorRegion = Plan.getVectorLoopRegion();
@@ -9312,7 +9312,8 @@ LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(VFRange &Range) {
             return !CM.requiresScalarEpilogue(VF.isVector());
           },
           Range);
-  auto Plan = std::make_unique<VPlan>(OrigLoop);
+  auto Plan =
+      std::make_unique<VPlan>(OrigLoop, Legal->getWidestInductionType());
   // Build hierarchical CFG.
   // Convert to VPlan-transform and consoliate all transforms for VPlan
   // creation.
@@ -9618,7 +9619,8 @@ VPlanPtr LoopVectorizationPlanner::buildVPlan(VFRange &Range) {
   assert(EnableVPlanNativePath && "VPlan-native path is not enabled.");
 
   // Create new empty VPlan
-  auto Plan = std::make_unique<VPlan>(OrigLoop);
+  auto Plan =
+      std::make_unique<VPlan>(OrigLoop, Legal->getWidestInductionType());
   // Build hierarchical CFG
   VPlanHCFGBuilder HCFGBuilder(OrigLoop, LI, *Plan);
   HCFGBuilder.buildHierarchicalCFG();
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.cpp b/llvm/lib/Transforms/Vectorize/VPlan.cpp
index 944a11b96325d..60708adfc7c55 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlan.cpp
@@ -221,7 +221,7 @@ VPTransformState::VPTransformState(const TargetTransformInfo *TTI,
                                    Loop *CurrentParentLoop, Type *CanonicalIVTy)
     : TTI(TTI), VF(VF), CFG(DT), LI(LI), Builder(Builder), ILV(ILV), Plan(Plan),
       CurrentParentLoop(CurrentParentLoop), LVer(nullptr),
-      TypeAnalysis(CanonicalIVTy) {}
+      TypeAnalysis(CanonicalIVTy->getContext()) {}
 
 Value *VPTransformState::get(const VPValue *Def, const VPLane &Lane) {
   if (Def->isLiveIn())
@@ -847,7 +847,8 @@ void VPRegionBlock::print(raw_ostream &O, const Twine &Indent,
 }
 #endif
 
-VPlan::VPlan(Loop *L) {
+VPlan::VPlan(Loop *L, Type *InductionTy)
+    : VectorTripCount(InductionTy), VF(InductionTy), VFxUF(InductionTy) {
   setEntry(createVPIRBasicBlock(L->getLoopPreheader()));
   ScalarHeader = createVPIRBasicBlock(L->getHeader());
 
@@ -858,7 +859,7 @@ VPlan::VPlan(Loop *L) {
 }
 
 VPlan::~VPlan() {
-  VPValue DummyValue;
+  VPValue DummyValue((Type *)nullptr);
 
   for (auto *VPB : CreatedBlocks) {
     if (auto *VPBB = dyn_cast<VPBasicBlock>(VPB)) {
@@ -888,10 +889,10 @@ void VPlan::prepareToExecute(Value *TripCountV, Value *VectorTripCountV,
     IRBuilder<> Builder(State.CFG.PrevBB->getTerminator());
     auto *TCMO = Builder.CreateSub(TripCountV, ConstantInt::get(TCTy, 1),
                                    "trip.count.minus.1");
-    BackedgeTakenCount->setUnderlyingValue(TCMO);
+    BackedgeTakenCount->replaceAllUsesWith(getOrAddLiveIn(TCMO));
   }
 
-  VectorTripCount.setUnderlyingValue(VectorTripCountV);
+  VectorTripCount.replaceAllUsesWith(getOrAddLiveIn(VectorTripCountV));
 
   IRBuilder<> Builder(State.CFG.PrevBB->getTerminator());
   // FIXME: Model VF * UF computation completely in VPlan.
@@ -900,12 +901,13 @@ void VPlan::prepareToExecute(Value *TripCountV, Value *VectorTripCountV,
   unsigned UF = getUF();
   if (VF.getNumUsers()) {
     Value *RuntimeVF = getRuntimeVF(Builder, TCTy, State.VF);
-    VF.setUnderlyingValue(RuntimeVF);
-    VFxUF.setUnderlyingValue(
+    VF.replaceAllUsesWith(getOrAddLiveIn(RuntimeVF));
+    VFxUF.replaceAllUsesWith(getOrAddLiveIn(
         UF > 1 ? Builder.CreateMul(RuntimeVF, ConstantInt::get(TCTy, UF))
-               : RuntimeVF);
+               : RuntimeVF));
   } else {
-    VFxUF.setUnderlyingValue(createStepForVF(Builder, TCTy, State.VF, UF));
+    VFxUF.replaceAllUsesWith(
+        getOrAddLiveIn(createStepForVF(Builder, TCTy, State.VF, UF)));
   }
 }
 
@@ -1166,7 +1168,8 @@ VPlan *VPlan::duplicate() {
         return VPIRBB && VPIRBB->getIRBasicBlock() == ScalarHeaderIRBB;
       }));
   // Create VPlan, clone live-ins and remap operands in the cloned blocks.
-  auto *NewPlan = new VPlan(cast<VPBasicBlock>(NewEntry), NewScalarHeader);
+  auto *NewPlan = new VPlan(cast<VPBasicBlock>(NewEntry), NewScalarHeader,
+                            getCanonicalIV()->getScalarType());
   DenseMap<VPValue *, VPValue *> Old2NewVPValues;
   for (VPValue *OldLiveIn : getLiveIns()) {
     Old2NewVPValues[OldLiveIn] =
@@ -1176,7 +1179,7 @@ VPlan *VPlan::duplicate() {
   Old2NewVPValues[&VF] = &NewPlan->VF;
   Old2NewVPValues[&VFxUF] = &NewPlan->VFxUF;
   if (BackedgeTakenCount) {
-    NewPlan->BackedgeTakenCount = new VPValue();
+    NewPlan->BackedgeTakenCount = new VPValue((Type *)nullptr);
     Old2NewVPValues[BackedgeTakenCount] = NewPlan->BackedgeTakenCount;
   }
   assert(TripCount && "trip count must be set");
@@ -1379,6 +1382,11 @@ static bool isDefinedInsideLoopRegions(const VPValue *VPV) {
                   DefR->getParent()->getEnclosingLoopRegion());
 }
 
+Type *VPValue::getType() const {
+  assert(isLiveIn());
+  return SubclassID == VPSymbolicValueSC ? Ty : getUnderlyingValue()->getType();
+}
+
 bool VPValue::isDefinedOutsideLoopRegions() const {
   return !isDefinedInsideLoopRegions(this);
 }
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index f68a2283c0c79..a40d4cc8c7d10 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3475,8 +3475,9 @@ class VPlan {
 
   /// Construct a VPlan with \p Entry to the plan and with \p ScalarHeader
   /// wrapping the original header of the scalar loop.
-  VPlan(VPBasicBlock *Entry, VPIRBasicBlock *ScalarHeader)
-      : Entry(Entry), ScalarHeader(ScalarHeader) {
+  VPlan(VPBasicBlock *Entry, VPIRBasicBlock *ScalarHeader, Type *InductionTy)
+      : Entry(Entry), ScalarHeader(ScalarHeader), VectorTripCount(InductionTy),
+        VF(InductionTy), VFxUF(InductionTy) {
     Entry->setPlan(this);
     assert(ScalarHeader->getNumSuccessors() == 0 &&
            "scalar header must be a leaf node");
@@ -3486,11 +3487,12 @@ class VPlan {
   /// Construct a VPlan for \p L. This will create VPIRBasicBlocks wrapping the
   /// original preheader and scalar header of \p L, to be used as entry and
   /// scalar header blocks of the new VPlan.
-  VPlan(Loop *L);
+  VPlan(Loop *L, Type *InductionTy);
 
   /// Construct a VPlan with a new VPBasicBlock as entry, a VPIRBasicBlock
   /// wrapping \p ScalarHeaderBB and a trip count of \p TC.
-  VPlan(BasicBlock *ScalarHeaderBB, VPValue *TC) {
+  VPlan(BasicBlock *ScalarHeaderBB, VPValue *TC, Type *InductionTy)
+      : VectorTripCount(InductionTy), VF(InductionTy), VFxUF(InductionTy) {
     setEntry(createVPBasicBlock("preheader"));
     ScalarHeader = createVPIRBasicBlock(ScalarHeaderBB);
     TripCount = TC;
@@ -3582,7 +3584,7 @@ class VPlan {
   /// The backedge taken count of the original loop.
   VPValue *getOrCreateBackedgeTakenCount() {
     if (!BackedgeTakenCount)
-      BackedgeTakenCount = new VPValue();
+      BackedgeTakenCount = new VPValue(getCanonicalIV()->getScalarType());
     return BackedgeTakenCount;
   }
 
diff --git a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
index 6f6875f0e5e0e..7a4cf7f0e8ca1 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
@@ -224,13 +224,8 @@ Type *VPTypeAnalysis::inferScalarType(const VPValue *V) {
   if (Type *CachedTy = CachedTypes.lookup(V))
     return CachedTy;
 
-  if (V->isLiveIn()) {
-    if (auto *IRValue = V->getLiveInIRValue())
-      return IRValue->getType();
-    // All VPValues without any underlying IR value (like the vector trip count
-    // or the backedge-taken count) have the same type as the canonical IV.
-    return CanonicalIVTy;
-  }
+  if (V->isLiveIn())
+    return V->getType();
 
   Type *ResultTy =
       TypeSwitch<const VPRecipeBase *, Type *>(V->getDefiningRecipe())
diff --git a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.h b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.h
index cc21870bee2e3..7d4771d178924 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.h
@@ -39,10 +39,6 @@ class Type;
 /// of the previously inferred types.
 class VPTypeAnalysis {
   DenseMap<const VPValue *, Type *> CachedTypes;
-  /// Type of the canonical induction variable. Used for all VPValues without
-  /// any underlying IR value (like the vector trip count or the backedge-taken
-  /// count).
-  Type *CanonicalIVTy;
   LLVMContext &Ctx;
 
   Type *inferScalarTypeForRecipe(const VPBlendRecipe *R);
@@ -55,8 +51,7 @@ class VPTypeAnalysis {
   Type *inferScalarTypeForRecipe(const VPReplicateRecipe *R);
 
 public:
-  VPTypeAnalysis(Type *CanonicalIVTy)
-      : CanonicalIVTy(CanonicalIVTy), Ctx(CanonicalIVTy->getContext()) {}
+  VPTypeAnalysis(LLVMContext &Ctx) : Ctx(Ctx) {}
 
   /// Infer the type of \p V. Returns the scalar type of \p V.
   Type *inferScalarType(const VPValue *V);
diff --git a/llvm/lib/Transforms/Vectorize/VPlanHelpers.h b/llvm/lib/Transforms/Vectorize/VPlanHelpers.h
index 8bdbf556efbb3..4a16f08192147 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanHelpers.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanHelpers.h
@@ -375,8 +375,8 @@ struct VPCostContext {
   VPCostContext(const TargetTransformInfo &TTI, const TargetLibraryInfo &TLI,
                 Type *CanIVTy, LoopVectorizationCostModel &CM,
                 TargetTransformInfo::TargetCostKind CostKind)
-      : TTI(TTI), TLI(TLI), Types(CanIVTy), LLVMCtx(CanIVTy->getContext()),
-        CM(CM), CostKind(CostKind) {}
+      : TTI(TTI), TLI(TLI), Types(CanIVTy->getContext()),
+        LLVMCtx(CanIVTy->getContext()), CM(CM), CostKind(CostKind) {}
 
   /// Return the cost for \p UI with \p VF using the legacy cost model as
   /// fallback until computing the cost of all recipes migrates to VPlan.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 7646350ca0ed2..c8d522fca46a6 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -545,7 +545,7 @@ createScalarIVSteps(VPlan &Plan, InductionDescriptor::InductionKind Kind,
 
   // Truncate base induction if needed.
   Type *CanonicalIVType = CanonicalIV->getScalarType();
-  VPTypeAnalysis TypeInfo(CanonicalIVType);
+  VPTypeAnalysis TypeInfo(CanonicalIVType->getContext());
   Type *ResultTy = TypeInfo.inferScalarType(BaseIV);
   if (TruncI) {
     Type *TruncTy = TruncI->getType();
@@ -751,7 +751,7 @@ void VPlanTransforms::optimizeInductionExitUsers(
   assert(PredVPBB == Plan.getMiddleBlock() &&
          "predecessor must be the middle block");
 
-  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType());
+  VPTypeAnalysis TypeInfo(Plan.getCanonicalIV()->getScalarType()->getContext());
   VPBuilder B(Plan.getMiddleBlock()->getTerminator());
   for (VPRecipeBase &R : *ExitVPBB) {
     auto *ExitIRI = cast<VPIRInstruction>(&R);
@@ -944,8 +944,11 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
 #ifndef NDEBUG
     // Verify that the cached type info is for both A and its users is still
     // accurate by comparing it to freshly computed types.
-    VPTypeAnalysis TypeInfo2(
-        R.getParent()->getPlan()->getCanonicalIV()->getScalarType());
+    VPTypeAnalysis TypeInfo2(R.getParent()
+                                 ->getPlan()
+                                 ->getCanonicalIV()
+                                 ->getScalarType()
+                                 ->getContext());
     assert(TypeInfo.inferScalarType(A) == TypeInfo2.inferScalarType(A));
     for (VPUser *U : A->users()) {
       auto *R = cast<VPRecipeBase>(U);
@@ -988,7 +991,7 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
 void VPlanTransforms::simplifyRecipes(VPlan &Plan, Type &CanonicalIVTy) {
   ReversePostOrderTraversal<VPBlockDeepTraversalWrapper<VPBlockBase *>> RPOT(
       Plan.getEntry());
-  VPTypeAnalysis TypeInfo(&CanonicalIVTy);
+  VPTypeAnalysis TypeInfo(CanonicalIVTy.getContext());
   for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(RPOT)) {
     for (VPRecipeBase &R : make_early_inc_range(*VPBB)) {
       simplifyRecipe(R, TypeInfo);
@@ -1338,7 +1341,7 @@ void VPlanTransforms::truncateToMinimalBitwidths(
   // typed.
   DenseMap<VPValue *, VPWidenCastRecipe *> ProcessedTruncs;
   Type *CanonicalIVType = Plan.getCanonicalIV()->getScalarType();
-  VPTypeAnalysis TypeInfo(CanonicalIVType);
+  VPTypeAnalysis TypeInfo(CanonicalIVType->getContext());
   VPBasicBlock *PH = Plan.getVectorPreheader();
   for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
            vp_depth_first_deep(Plan.getVectorLoopRegion()))) {
@@ -1730,8 +1733,8 @@ static VPRecipeBase *createEVLRecipe(VPValue *HeaderMask,
 /// Replace recipes with their EVL variants.
 static void transformRecipestoEVLRecipes(VPlan &Plan, VPValue &EVL) {
   Type *CanonicalIVType = Plan.getCanonicalIV()->getScalarType();
-  VPTypeAnalysis TypeInfo(CanonicalIVType);
   LLVMContext &Ctx = CanonicalIVType->getContext();
+  VPTypeAnalysis TypeInfo(Ctx);
   VPValue *AllOneMask = Plan.getOrAddLiveIn(ConstantInt::getTrue(Ctx));
   VPRegionBlock *LoopRegion = Plan.getVectorLoopRegion();
   VPBasicBlock *Header = LoopRegion->getEntryBasicBlock();
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp b/llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp
index 3fb6a5be79ff0..64e836e35dabb 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp
@@ -73,7 +73,7 @@ class UnrollState {
 
 public:
   UnrollState(VPlan &Plan, unsigned UF, LLVMContext &Ctx)
-      : Plan(Plan), UF(UF), TypeInfo(Plan.getCanonicalIV()->getScalarType()) {}
+      : Plan(Plan), UF(UF), TypeInfo(Ctx) {}
 
   void unrollBlock(VPBlockBase *VPB);
 
diff --git a/llvm/lib/Transforms/Vectorize/VPlanValue.h b/llvm/lib/Transforms/Vectorize/VPlanValue.h
index 0a59b137bbd79..1fa3fbc988f5b 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanValue.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanValue.h
@@ -61,8 +61,12 @@ class VPValue {
   SmallVector<VPUser *, 1> Users;
 
 protected:
-  // Hold the underlying Value, if any, attached to this VPValue.
-  Value *UnderlyingVal;
+  union {
+    // Hold the underlying Value, if any, attached to this non-symbolic VPValue.
+    Value *UnderlyingVal;
+    // Hold the type of this VPValue, if it is symbolic.
+    Type *Ty;
+  };
 
   /// Pointer to the VPDef that defines this VPValue. If it is nullptr, the
   /// VPValue is not defined by any recipe modeled in VPlan.
@@ -70,8 +74,9 @@ class VPValue {
 
   VPValue(const unsigned char SC, Value *UV = nullptr, VPDef *Def = nullptr);
 
-  /// Create a live-in VPValue.
-  VPValue(Value *UV = nullptr) : VPValue(VPValueSC, UV, nullptr) {}
+  /// Create a live-in IR VPValue.
+  VPValue(Value *UV) : VPValue(VPValueSC, UV, nullptr) {}
+  VPValue(Type *Ty) : SubclassID(VPSymbolicValueSC), Ty(Ty), Def(nullptr) {}
   /// Create a VPValue for a \p Def which is a subclass of VPValue.
   VPValue(VPDef *Def, Value *UV = nullptr) : VPValue(VPVRecipeSC, UV, Def) {}
   /// Create a VPValue for a \p Def which defines multiple values.
@@ -86,14 +91,18 @@ class VPValue {
 
 public:
   /// Return the underlying Value attached to this VPValue.
-  Value *getUnderlyingValue() const { return UnderlyingVal; }
+  Value *getUnderlyingValue() const {
+    return SubclassID == VPSymbolicValueSC ? nullptr : UnderlyingVal;
+  }
 
   /// An enumeration for keeping track of the concrete subclass of VPValue that
   /// are actually instantiated.
   enum {
-    VPValueSC, /// A generic VPValue, like live-in values or defined by a recipe
-               /// that defines multiple values.
-    VPVRecipeSC /// A VPValue sub-class that is a VPRecipeBase.
+    VPValueSC, /// A generic non-symbolic VPValue, like live-in IR values or
+               /// defined by a recipe that defines multiple values.
+    VPSymbolicValueSC, /// A generic VPValue, like live-in values or defined by
+                       /// a recipe that defines multiple values.
+    VPVRecipeSC        /// A VPValue sub-class that is a VPRecipeBase.
   };
 
   VPValue(const VPValue &) = delete;
@@ -172,6 +181,10 @@ class VPValue {
   /// Returns true if this VPValue is a live-in, i.e. defined outside the VPlan.
   bool isLiveIn() const { return !hasDefiningRecipe(); }
 
+  bool isSymbolic() const { return SubclassID == VPSymbolicValueSC; }
+
+  Type *getType() const;
+
   /// Returns the underlying IR value, if this VPValue is defined outside the
   /// scope of VPlan. Returns nullptr if the VPValue is defined by a VPDef
   /// inside a VPlan.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
index 1b3b69ea6a13d..6c8b55a86c938 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp
@@ -415,8 +415,10 @@ bool VPlanVerifier::verify(const VPlan &Plan) {
 bool llvm::verifyVPlanIsValid(const VPlan &Plan) {
   VPDominatorTree VPDT;
   VPDT.recalculate(const_cast<VPlan &>(Plan));
-  VPTypeAnalysis TypeInfo(
-      const_cast<VPlan &>(Plan).getCanonicalIV()->getScalarType());
+  VPTypeAnalysis TypeInfo(const_cast<VPlan &>(Plan)
+                              .getCanonicalIV()
+                              ->getScalarType()
+                              ->getContext());
   VPlanVerifier Verifier(VPDT, TypeInfo);
   return Verifier.verify(Plan);
 }
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h b/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h
index caf5d2357411d..85ac0c3ac8a75 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTestBase.h
@@ -71,7 +71,7 @@ class VPlanTestIRBase : public testing::Test {
 
     Loop *L = LI->getLoopFor(LoopHeader);
     PredicatedScalarEvolution PSE(*SE, *L);
-    auto Plan = std::make_unique<VPlan>(L);
+    auto Plan = std::make_unique<VPlan>(L, IntegerType::get(*Ctx, 64));
     VPlanHCFGBuilder HCFGBuilder(L, LI.get(), *Plan);
     HCFGBuilder.buildHierarchicalCFG();
     VPlanTransforms::introduceTopLevelVectorLoopRegion(
@@ -91,7 +91,8 @@ class VPlanTestBase : public testing::Test {
   }
 
   VPlan &getPlan(VPValue *TC = nullptr) {
-    Plans.push_back(std::make_unique<VPlan>(&*ScalarHeader, TC));
+    Plans.push_back(
+        std::make_unique<VPlan>(&*ScalarHeader, TC, IntegerType::get(C, 64)));
     return *Plans.back();
   }
 };

/// Return the underlying Value attached to this VPValue.
Value *getUnderlyingValue() const { return UnderlyingVal; }
Value *getUnderlyingValue() const {
return SubclassID == VPSymbolicValueSC ? nullptr : UnderlyingVal;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return SubclassID == VPSymbolicValueSC ? nullptr : UnderlyingVal;
return isSymbolic() ? nullptr : UnderlyingVal;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done thanks!

Comment on lines 184 to 186
bool isSymbolic() const { return SubclassID == VPSymbolicValueSC; }

Type *getType() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some description comments for the 2 functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done thanks. Also updated name to getScalarType

VPValue(Value *UV = nullptr) : VPValue(VPValueSC, UV, nullptr) {}
/// Create a live-in IR VPValue.
VPValue(Value *UV) : VPValue(VPValueSC, UV, nullptr) {}
VPValue(Type *Ty) : SubclassID(VPSymbolicValueSC), Ty(Ty), Def(nullptr) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe directly set Def to nullptr by default?

VPDef *Def = nullptr;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do separately, as independent of the PR?

Comment on lines 3487 to 3459
/// Construct a VPlan for \p L. This will create VPIRBasicBlocks wrapping the
/// original preheader and scalar header of \p L, to be used as entry and
/// scalar header blocks of the new VPlan.
VPlan(Loop *L);
VPlan(Loop *L, Type *InductionTy);

/// Construct a VPlan with a new VPBasicBlock as entry, a VPIRBasicBlock
/// wrapping \p ScalarHeaderBB and a trip count of \p TC.
VPlan(BasicBlock *ScalarHeaderBB, VPValue *TC) {
VPlan(BasicBlock *ScalarHeaderBB, VPValue *TC, Type *InductionTy)
: VectorTripCount(InductionTy), VF(InductionTy), VFxUF(InductionTy) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update the comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done thanks!

fhahn added 2 commits March 20, 2025 11:23
This introduces a new VPSymbolicValueSC to be used for typed live-in VPValues
without underlying IR. VPValue is updated to store either the type or
an underlying value in an union. This allows keeping the size of VPValue
unchanged.

The main motivation for adding the type is to not require passing the
canonical IV type to VPTypeAnalysis.

While with this patch, we still need to pass the LLVMContext, this can
also be removed in a future patch (see
9493c38)
@fhahn fhahn force-pushed the vplan-vpsymbolic-value branch from ed85b35 to 7f9235c Compare March 20, 2025 19:17
@github-actions
Copy link

github-actions bot commented Mar 20, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 01f04252b6711e281d9569172302ec20789e9bbe acbca4ae4a710429d21c9be1a304f3e70aa2d004 --extensions h,cpp -- llvm/lib/Transforms/Vectorize/LoopVectorize.cpp llvm/lib/Transforms/Vectorize/VPlan.cpp llvm/lib/Transforms/Vectorize/VPlan.h llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp llvm/lib/Transforms/Vectorize/VPlanAnalysis.h llvm/lib/Transforms/Vectorize/VPlanHelpers.h llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp llvm/lib/Transforms/Vectorize/VPlanUnroll.cpp llvm/lib/Transforms/Vectorize/VPlanValue.h llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp llvm/unittests/Transforms/Vectorize/VPlanTestBase.h
View the diff from clang-format here.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 2de6f3b83c..0d795d2ad8 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -802,7 +802,6 @@ void VPlanTransforms::optimizeInductionExitUsers(
       if (!isa<PHINode>(ExitIRI->getInstruction()))
         break;
 
-
       for (auto [Idx, PredVPBB] : enumerate(ExitVPBB->getPredecessors())) {
         if (PredVPBB == MiddleVPBB)
           if (VPValue *Escape = optimizeLatchExitInductionUser(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants