[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