[llvm] [VPlan] Support isa/dyn_cast from VPRecipeBase to VPIRMetadata (NFC). (PR #166245)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 17 07:04:46 PST 2025


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/166245

>From cc8841336030fd342e32971b79f3c63eeadce461 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 3 Nov 2025 21:51:33 +0000
Subject: [PATCH 1/2] [VPlan] Support isa/dyn_cast from VPRecipeBase to
 VPIRMetadata (NFC).

Implement CastInfo from VPRecipeBase to VPIRMetadata to support
isa/dyn_Cast. This is similar to CastInfoVPPhiAccessors, supporting
dyn_cast by down-casting to the concrete recipe types inheriting from
VPIRMetadata.

Can be used for more generalized VPIRMetadata printing following
https://github.com/llvm/llvm-project/pull/165825.
---
 llvm/lib/Transforms/Vectorize/VPlan.h | 70 +++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index cfe1f1e9d7528..2c47aa3f35e02 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3815,6 +3815,76 @@ template <>
 struct CastInfo<VPPhiAccessors, const VPRecipeBase *>
     : CastInfoVPPhiAccessors<const VPRecipeBase *> {};
 
+/// Casting from VPRecipeBase -> VPIRMetadata is supported for all recipe types
+/// implementing VPIRMetadata. Used by isa<> & co.
+template <> struct CastIsPossible<VPIRMetadata, const VPRecipeBase *> {
+  static inline bool isPossible(const VPRecipeBase *R) {
+    return isa<VPInstruction, VPWidenRecipe, VPWidenCastRecipe,
+               VPWidenIntrinsicRecipe, VPWidenCallRecipe, VPWidenSelectRecipe,
+               VPReplicateRecipe, VPInterleaveRecipe, VPInterleaveEVLRecipe,
+               VPWidenLoadRecipe, VPWidenLoadEVLRecipe, VPWidenStoreRecipe,
+               VPWidenStoreEVLRecipe>(R);
+  }
+};
+
+/// Support casting from VPRecipeBase -> VPIRMetadata, by down-casting to the
+/// recipe types implementing VPIRMetadata. Used by cast<>, dyn_cast<> & co.
+template <typename SrcTy>
+struct CastInfoVPIRMetadata : public CastIsPossible<VPIRMetadata, SrcTy> {
+
+  using Self = CastInfo<VPIRMetadata, SrcTy>;
+
+  /// doCast is used by cast<>.
+  static inline VPIRMetadata *doCast(SrcTy R) {
+    return const_cast<VPIRMetadata *>([R]() -> const VPIRMetadata * {
+      switch (R->getVPDefID()) {
+      case VPDef::VPInstructionSC:
+        return cast<VPInstruction>(R);
+      case VPDef::VPWidenSC:
+        return cast<VPWidenRecipe>(R);
+      case VPDef::VPWidenCastSC:
+        return cast<VPWidenCastRecipe>(R);
+      case VPDef::VPWidenIntrinsicSC:
+        return cast<VPWidenIntrinsicRecipe>(R);
+      case VPDef::VPWidenCallSC:
+        return cast<VPWidenCallRecipe>(R);
+      case VPDef::VPWidenSelectSC:
+        return cast<VPWidenSelectRecipe>(R);
+      case VPDef::VPReplicateSC:
+        return cast<VPReplicateRecipe>(R);
+      case VPDef::VPInterleaveSC:
+        return cast<VPInterleaveRecipe>(R);
+      case VPDef::VPInterleaveEVLSC:
+        return cast<VPInterleaveEVLRecipe>(R);
+      case VPDef::VPWidenLoadSC:
+        return cast<VPWidenLoadRecipe>(R);
+      case VPDef::VPWidenLoadEVLSC:
+        return cast<VPWidenLoadEVLRecipe>(R);
+      case VPDef::VPWidenStoreSC:
+        return cast<VPWidenStoreRecipe>(R);
+      case VPDef::VPWidenStoreEVLSC:
+        return cast<VPWidenStoreEVLRecipe>(R);
+      default:
+        llvm_unreachable("invalid recipe for VPIRMetadata cast");
+      }
+    }());
+  }
+
+  /// doCastIfPossible is used by dyn_cast<>.
+  static inline VPIRMetadata *doCastIfPossible(SrcTy f) {
+    if (!Self::isPossible(f))
+      return nullptr;
+    return doCast(f);
+  }
+};
+
+template <>
+struct CastInfo<VPIRMetadata, VPRecipeBase *>
+    : CastInfoVPIRMetadata<VPRecipeBase *> {};
+template <>
+struct CastInfo<VPIRMetadata, const VPRecipeBase *>
+    : CastInfoVPIRMetadata<const VPRecipeBase *> {};
+
 /// VPBasicBlock serves as the leaf of the Hierarchical Control-Flow Graph. It
 /// holds a sequence of zero or more VPRecipe's each representing a sequence of
 /// output IR instructions. All PHI-like recipes must come before any non-PHI recipes.

>From 723d7d07a80b82058ae3630461c2f36d5487ec87 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 6 Nov 2025 17:22:07 +0000
Subject: [PATCH 2/2] !fixup add unit testing, avoid const_cast

---
 llvm/lib/Transforms/Vectorize/VPlan.h         | 97 ++++++++++---------
 .../Transforms/Vectorize/VPlanTest.cpp        | 27 +++---
 2 files changed, 63 insertions(+), 61 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 5c57c688ad663..f391e5709673c 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3819,67 +3819,68 @@ struct CastInfo<VPPhiAccessors, const VPRecipeBase *>
 
 /// Casting from VPRecipeBase -> VPIRMetadata is supported for all recipe types
 /// implementing VPIRMetadata. Used by isa<> & co.
-template <> struct CastIsPossible<VPIRMetadata, const VPRecipeBase *> {
-  static inline bool isPossible(const VPRecipeBase *R) {
-    return isa<VPInstruction, VPWidenRecipe, VPWidenCastRecipe,
-               VPWidenIntrinsicRecipe, VPWidenCallRecipe, VPWidenSelectRecipe,
-               VPReplicateRecipe, VPInterleaveRecipe, VPInterleaveEVLRecipe,
-               VPWidenLoadRecipe, VPWidenLoadEVLRecipe, VPWidenStoreRecipe,
-               VPWidenStoreEVLRecipe>(R);
+namespace detail {
+/// Returns const VPIRMetadata* if input is const, and VPIRMetadata* otherwise.
+template <typename RecipeBasePtrTy>
+static inline auto castToVPIRMetadata(RecipeBasePtrTy R) -> std::conditional_t<
+    std::is_const_v<std::remove_pointer_t<RecipeBasePtrTy>>,
+    const VPIRMetadata *, VPIRMetadata *> {
+  switch (R->getVPDefID()) {
+  case VPDef::VPInstructionSC:
+    return cast<VPInstruction>(R);
+  case VPDef::VPWidenSC:
+    return cast<VPWidenRecipe>(R);
+  case VPDef::VPWidenCastSC:
+    return cast<VPWidenCastRecipe>(R);
+  case VPDef::VPWidenIntrinsicSC:
+    return cast<VPWidenIntrinsicRecipe>(R);
+  case VPDef::VPWidenCallSC:
+    return cast<VPWidenCallRecipe>(R);
+  case VPDef::VPWidenSelectSC:
+    return cast<VPWidenSelectRecipe>(R);
+  case VPDef::VPReplicateSC:
+    return cast<VPReplicateRecipe>(R);
+  case VPDef::VPInterleaveSC:
+    return cast<VPInterleaveRecipe>(R);
+  case VPDef::VPInterleaveEVLSC:
+    return cast<VPInterleaveEVLRecipe>(R);
+  case VPDef::VPWidenLoadSC:
+    return cast<VPWidenLoadRecipe>(R);
+  case VPDef::VPWidenLoadEVLSC:
+    return cast<VPWidenLoadEVLRecipe>(R);
+  case VPDef::VPWidenStoreSC:
+    return cast<VPWidenStoreRecipe>(R);
+  case VPDef::VPWidenStoreEVLSC:
+    return cast<VPWidenStoreEVLRecipe>(R);
+  default:
+    llvm_unreachable("invalid recipe for VPIRMetadata cast");
   }
-};
+}
+} // namespace detail
 
 /// Support casting from VPRecipeBase -> VPIRMetadata, by down-casting to the
 /// recipe types implementing VPIRMetadata. Used by cast<>, dyn_cast<> & co.
 template <typename SrcTy>
 struct CastInfoVPIRMetadata : public CastIsPossible<VPIRMetadata, SrcTy> {
+  static inline bool isPossible(SrcTy R) {
+    return isa<VPInstruction, VPWidenRecipe, VPWidenCastRecipe,
+               VPWidenIntrinsicRecipe, VPWidenCallRecipe, VPWidenSelectRecipe,
+               VPReplicateRecipe, VPInterleaveRecipe, VPInterleaveEVLRecipe,
+               VPWidenLoadRecipe, VPWidenLoadEVLRecipe, VPWidenStoreRecipe,
+               VPWidenStoreEVLRecipe>(R);
+  }
 
   using Self = CastInfo<VPIRMetadata, SrcTy>;
+  using RetTy = decltype(detail::castToVPIRMetadata(std::declval<SrcTy>()));
 
-  /// doCast is used by cast<>.
-  static inline VPIRMetadata *doCast(SrcTy R) {
-    return const_cast<VPIRMetadata *>([R]() -> const VPIRMetadata * {
-      switch (R->getVPDefID()) {
-      case VPDef::VPInstructionSC:
-        return cast<VPInstruction>(R);
-      case VPDef::VPWidenSC:
-        return cast<VPWidenRecipe>(R);
-      case VPDef::VPWidenCastSC:
-        return cast<VPWidenCastRecipe>(R);
-      case VPDef::VPWidenIntrinsicSC:
-        return cast<VPWidenIntrinsicRecipe>(R);
-      case VPDef::VPWidenCallSC:
-        return cast<VPWidenCallRecipe>(R);
-      case VPDef::VPWidenSelectSC:
-        return cast<VPWidenSelectRecipe>(R);
-      case VPDef::VPReplicateSC:
-        return cast<VPReplicateRecipe>(R);
-      case VPDef::VPInterleaveSC:
-        return cast<VPInterleaveRecipe>(R);
-      case VPDef::VPInterleaveEVLSC:
-        return cast<VPInterleaveEVLRecipe>(R);
-      case VPDef::VPWidenLoadSC:
-        return cast<VPWidenLoadRecipe>(R);
-      case VPDef::VPWidenLoadEVLSC:
-        return cast<VPWidenLoadEVLRecipe>(R);
-      case VPDef::VPWidenStoreSC:
-        return cast<VPWidenStoreRecipe>(R);
-      case VPDef::VPWidenStoreEVLSC:
-        return cast<VPWidenStoreEVLRecipe>(R);
-      default:
-        llvm_unreachable("invalid recipe for VPIRMetadata cast");
-      }
-    }());
-  }
+  static inline RetTy doCast(SrcTy R) { return detail::castToVPIRMetadata(R); }
 
-  /// doCastIfPossible is used by dyn_cast<>.
-  static inline VPIRMetadata *doCastIfPossible(SrcTy f) {
-    if (!Self::isPossible(f))
+  static inline RetTy doCastIfPossible(SrcTy R) {
+    if (!Self::isPossible(R))
       return nullptr;
-    return doCast(f);
+    return doCast(R);
   }
 };
-
 template <>
 struct CastInfo<VPIRMetadata, VPRecipeBase *>
     : CastInfoVPIRMetadata<VPRecipeBase *> {};
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 82ecc16074a8f..c9184be93a29d 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -1002,7 +1002,7 @@ TEST_F(VPRecipeTest, CastVPInstructionToVPUser) {
   VPValue *Op2 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2));
   VPInstruction Recipe(Instruction::Add, {Op1, Op2});
 
-  checkVPRecipeCastImpl<VPInstruction, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPInstruction, VPUser, VPIRMetadata>(&Recipe);
 }
 
 TEST_F(VPRecipeTest, CastVPWidenRecipeToVPUser) {
@@ -1017,7 +1017,7 @@ TEST_F(VPRecipeTest, CastVPWidenRecipeToVPUser) {
   Args.push_back(Op2);
   VPWidenRecipe WidenR(*AI, make_range(Args.begin(), Args.end()));
 
-  checkVPRecipeCastImpl<VPWidenRecipe, VPUser>(&WidenR);
+  checkVPRecipeCastImpl<VPWidenRecipe, VPUser, VPIRMetadata>(&WidenR);
   delete AI;
 }
 
@@ -1036,7 +1036,7 @@ TEST_F(VPRecipeTest, CastVPWidenCallRecipeToVPUserAndVPDef) {
   Args.push_back(CalledFn);
   VPWidenCallRecipe Recipe(Call, Fn, Args);
 
-  checkVPRecipeCastImpl<VPWidenCallRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenCallRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   VPValue *VPV = &Recipe;
   EXPECT_TRUE(VPV->getDefiningRecipe());
@@ -1062,7 +1062,8 @@ TEST_F(VPRecipeTest, CastVPWidenSelectRecipeToVPUserAndVPDef) {
   VPWidenSelectRecipe WidenSelectR(*SelectI,
                                    make_range(Args.begin(), Args.end()));
 
-  checkVPRecipeCastImpl<VPWidenSelectRecipe, VPUser>(&WidenSelectR);
+  checkVPRecipeCastImpl<VPWidenSelectRecipe, VPUser, VPIRMetadata>(
+      &WidenSelectR);
 
   VPValue *VPV = &WidenSelectR;
   EXPECT_EQ(&WidenSelectR, VPV->getDefiningRecipe());
@@ -1100,7 +1101,7 @@ TEST_F(VPRecipeTest, CastVPWidenCastRecipeToVPUser) {
   VPValue *Op1 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1));
   VPWidenCastRecipe Recipe(Instruction::ZExt, Op1, Int64, *Cast);
 
-  checkVPRecipeCastImpl<VPWidenCastRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenCastRecipe, VPUser, VPIRMetadata>(&Recipe);
   delete Cast;
 }
 
@@ -1111,7 +1112,7 @@ TEST_F(VPRecipeTest, CastVPWidenIntrinsicRecipeToVPUser) {
   VPValue *Op2 = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2));
   VPWidenIntrinsicRecipe Recipe(Intrinsic::smax, {Op1, Op2}, Int32);
 
-  checkVPRecipeCastImpl<VPWidenIntrinsicRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenIntrinsicRecipe, VPUser, VPIRMetadata>(&Recipe);
 }
 
 TEST_F(VPRecipeTest, CastVPBlendRecipeToVPUser) {
@@ -1141,7 +1142,7 @@ TEST_F(VPRecipeTest, CastVPInterleaveRecipeToVPUser) {
   InterleaveGroup<Instruction> IG(4, false, Align(4));
   VPInterleaveRecipe Recipe(&IG, Addr, {}, Mask, false, {}, DebugLoc());
 
-  checkVPRecipeCastImpl<VPInterleaveRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPInterleaveRecipe, VPUser, VPIRMetadata>(&Recipe);
 }
 
 TEST_F(VPRecipeTest, CastVPReplicateRecipeToVPUser) {
@@ -1157,7 +1158,7 @@ TEST_F(VPRecipeTest, CastVPReplicateRecipeToVPUser) {
   auto *Call = CallInst::Create(FTy, PoisonValue::get(FTy));
   VPReplicateRecipe Recipe(Call, make_range(Args.begin(), Args.end()), true);
 
-  checkVPRecipeCastImpl<VPReplicateRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPReplicateRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   delete Call;
 }
@@ -1181,7 +1182,7 @@ TEST_F(VPRecipeTest, CastVPWidenMemoryRecipeToVPUserAndVPDef) {
   VPValue *Mask = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2));
   VPWidenLoadRecipe Recipe(*Load, Addr, Mask, true, false, {}, {});
 
-  checkVPRecipeCastImpl<VPWidenLoadRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenLoadRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   VPValue *VPV = Recipe.getVPSingleValue();
   EXPECT_TRUE(isa<VPRecipeBase>(VPV->getDefiningRecipe()));
@@ -1200,7 +1201,7 @@ TEST_F(VPRecipeTest, CastVPInterleaveEVLRecipeToVPUser) {
   VPInterleaveRecipe BaseRecipe(&IG, Addr, {}, Mask, false, {}, DebugLoc());
   VPInterleaveEVLRecipe Recipe(BaseRecipe, *EVL, Mask);
 
-  checkVPRecipeCastImpl<VPInterleaveEVLRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPInterleaveEVLRecipe, VPUser, VPIRMetadata>(&Recipe);
 }
 
 TEST_F(VPRecipeTest, CastVPWidenLoadEVLRecipeToVPUser) {
@@ -1215,7 +1216,7 @@ TEST_F(VPRecipeTest, CastVPWidenLoadEVLRecipeToVPUser) {
   VPWidenLoadRecipe BaseLoad(*Load, Addr, Mask, true, false, {}, {});
   VPWidenLoadEVLRecipe Recipe(BaseLoad, Addr, *EVL, Mask);
 
-  checkVPRecipeCastImpl<VPWidenLoadEVLRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenLoadEVLRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   delete Load;
 }
@@ -1231,7 +1232,7 @@ TEST_F(VPRecipeTest, CastVPWidenStoreRecipeToVPUser) {
   VPValue *Mask = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 2));
   VPWidenStoreRecipe Recipe(*Store, Addr, StoredVal, Mask, true, false, {}, {});
 
-  checkVPRecipeCastImpl<VPWidenStoreRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenStoreRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   delete Store;
 }
@@ -1250,7 +1251,7 @@ TEST_F(VPRecipeTest, CastVPWidenStoreEVLRecipeToVPUser) {
                                {});
   VPWidenStoreEVLRecipe Recipe(BaseStore, Addr, *EVL, Mask);
 
-  checkVPRecipeCastImpl<VPWidenStoreEVLRecipe, VPUser>(&Recipe);
+  checkVPRecipeCastImpl<VPWidenStoreEVLRecipe, VPUser, VPIRMetadata>(&Recipe);
 
   delete Store;
 }



More information about the llvm-commits mailing list