[llvm] [VPlan] Support struct return types for widen intrinsics (NFC). (PR #165218)

Shih-Po Hung via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 9 19:16:38 PST 2025


https://github.com/arcbbb updated https://github.com/llvm/llvm-project/pull/165218

>From 6a70859129f3f048b413f23e68a810f26429e26d Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Mon, 27 Oct 2025 02:01:42 -0700
Subject: [PATCH 1/6] [VPlan] Support struct return types for widen intrinsics.
 NFC.

---
 llvm/include/llvm/Analysis/VectorUtils.h      |  5 ++++
 llvm/lib/Analysis/VectorUtils.cpp             | 14 ++++++++++
 .../Transforms/Vectorize/LoopVectorize.cpp    | 17 +++++++++++-
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 27 ++++++++++++++++---
 4 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h
index ce94906ee7c00..f0c064f5490a7 100644
--- a/llvm/include/llvm/Analysis/VectorUtils.h
+++ b/llvm/include/llvm/Analysis/VectorUtils.h
@@ -164,6 +164,11 @@ LLVM_ABI bool
 isVectorIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID, int OpdIdx,
                                        const TargetTransformInfo *TTI);
 
+/// Identifies if the vector form of the intrinsic that returns a struct has
+/// a scalar element at the struct element index \p RetIdx.
+LLVM_ABI bool isVectorIntrinsicWithStructReturnScalarAtField(Intrinsic::ID ID,
+                                                             int RetIdx);
+
 /// Identifies if the vector form of the intrinsic that returns a struct is
 /// overloaded at the struct element index \p RetIdx. /// \p TTI is used to
 /// consider target specific intrinsics, if no target specific intrinsics
diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index 091d94843698c..1f2fc80501cba 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -217,6 +217,16 @@ bool llvm::isVectorIntrinsicWithOverloadTypeAtArg(
   }
 }
 
+bool llvm::isVectorIntrinsicWithStructReturnScalarAtField(Intrinsic::ID ID,
+                                                          int RetIdx) {
+  switch (ID) {
+  case Intrinsic::vp_load_ff:
+    return RetIdx == 1;
+  default:
+    return false;
+  }
+}
+
 bool llvm::isVectorIntrinsicWithStructReturnOverloadAtField(
     Intrinsic::ID ID, int RetIdx, const TargetTransformInfo *TTI) {
 
@@ -224,6 +234,10 @@ bool llvm::isVectorIntrinsicWithStructReturnOverloadAtField(
     return TTI->isTargetIntrinsicWithStructReturnOverloadAtField(ID, RetIdx);
 
   switch (ID) {
+  case Intrinsic::modf:
+  case Intrinsic::sincos:
+  case Intrinsic::sincospi:
+    return false;
   case Intrinsic::frexp:
     return RetIdx == 0 || RetIdx == 1;
   default:
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index facb0fabdf57e..93419100cea35 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4100,7 +4100,22 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       Type *ScalarTy = TypeInfo.inferScalarType(ToCheck);
       if (!Visited.insert({ScalarTy}).second)
         continue;
-      Type *WideTy = toVectorizedTy(ScalarTy, VF);
+      Type *WideTy;
+      if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R);
+          WI && ScalarTy->isStructTy()) {
+        auto *StructTy = cast<StructType>(ScalarTy);
+        SmallVector<Type *, 2> Tys;
+        for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
+          Type *ElementTy = StructTy->getStructElementType(I);
+          if (!isVectorIntrinsicWithStructReturnScalarAtField(
+                  WI->getVectorIntrinsicID(), I))
+            ElementTy = toVectorizedTy(ElementTy, VF);
+          Tys.push_back(ElementTy);
+        }
+        WideTy = StructType::create(Tys);
+      } else
+        WideTy = toVectorizedTy(ScalarTy, VF);
+
       if (any_of(getContainedTypes(WideTy), WillGenerateTargetVectors))
         return true;
     }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 931a5b7582c4e..7cb15914fb2ce 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1738,7 +1738,16 @@ void VPWidenIntrinsicRecipe::execute(VPTransformState &State) {
 
   SmallVector<Type *, 2> TysForDecl;
   // Add return type if intrinsic is overloaded on it.
-  if (isVectorIntrinsicWithOverloadTypeAtArg(VectorIntrinsicID, -1, State.TTI))
+  if (ResultTy->isStructTy()) {
+    auto *StructTy = cast<StructType>(ResultTy);
+    for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
+      if (isVectorIntrinsicWithStructReturnOverloadAtField(VectorIntrinsicID, I,
+                                                           State.TTI))
+        TysForDecl.push_back(
+            toVectorizedTy(StructTy->getStructElementType(I), State.VF));
+    }
+  } else if (isVectorIntrinsicWithOverloadTypeAtArg(VectorIntrinsicID, -1,
+                                                    State.TTI))
     TysForDecl.push_back(VectorType::get(getResultType(), State.VF));
   SmallVector<Value *, 4> Args;
   for (const auto &I : enumerate(operands())) {
@@ -1802,8 +1811,20 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
     Arguments.push_back(V);
   }
 
-  Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
-  Type *RetTy = VF.isVector() ? toVectorizedTy(ScalarRetTy, VF) : ScalarRetTy;
+  Type *RetTy = Ctx.Types.inferScalarType(&R);
+
+  if (VF.isVector() && RetTy->isStructTy()) {
+    auto *StructTy = cast<StructType>(RetTy);
+    SmallVector<Type *> Tys;
+    for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
+      Type *ElementTy = StructTy->getStructElementType(I);
+      if (!isVectorIntrinsicWithStructReturnScalarAtField(ID, I))
+        ElementTy = toVectorizedTy(ElementTy, VF);
+      Tys.push_back(ElementTy);
+    }
+    RetTy = StructType::get(StructTy->getContext(), Tys);
+  } else if (VF.isVector())
+    RetTy = toVectorizedTy(RetTy, VF);
   SmallVector<Type *> ParamTys;
   for (const VPValue *Op : Operands) {
     ParamTys.push_back(VF.isVector()

>From e2415bbd3510bf1f037d8a42b6ea9b3ce33c8e08 Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Thu, 6 Nov 2025 05:56:00 -0800
Subject: [PATCH 2/6] Extend support in toVectorizedStructTy

---
 llvm/include/llvm/IR/VectorTypeUtils.h        | 10 +++++---
 llvm/lib/IR/VectorTypeUtils.cpp               | 24 ++++++++++++++-----
 .../Transforms/Vectorize/LoopVectorize.cpp    | 19 ++++-----------
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 17 +++----------
 4 files changed, 32 insertions(+), 38 deletions(-)

diff --git a/llvm/include/llvm/IR/VectorTypeUtils.h b/llvm/include/llvm/IR/VectorTypeUtils.h
index e3d7fadad6089..53fba9db09b1d 100644
--- a/llvm/include/llvm/IR/VectorTypeUtils.h
+++ b/llvm/include/llvm/IR/VectorTypeUtils.h
@@ -31,7 +31,11 @@ inline Type *toVectorTy(Type *Scalar, unsigned VF) {
 /// Note:
 ///   - If \p EC is scalar, \p StructTy is returned unchanged
 ///   - Only unpacked literal struct types are supported
-LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC);
+///   vector types.
+///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
+///   are widened.
+LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC,
+                                    unsigned IID = 0);
 
 /// A helper for converting structs of vector types to structs of scalar types.
 /// Note: Only unpacked literal struct types are supported.
@@ -52,9 +56,9 @@ LLVM_ABI bool canVectorizeStructTy(StructType *StructTy);
 ///   - If the incoming type is void, we return void
 ///   - If \p EC is scalar, \p Ty is returned unchanged
 ///   - Only unpacked literal struct types are supported
-inline Type *toVectorizedTy(Type *Ty, ElementCount EC) {
+inline Type *toVectorizedTy(Type *Ty, ElementCount EC, unsigned IID = 0) {
   if (StructType *StructTy = dyn_cast<StructType>(Ty))
-    return toVectorizedStructTy(StructTy, EC);
+    return toVectorizedStructTy(StructTy, EC, IID);
   return toVectorTy(Ty, EC);
 }
 
diff --git a/llvm/lib/IR/VectorTypeUtils.cpp b/llvm/lib/IR/VectorTypeUtils.cpp
index 62e39aab90079..744fe2a704a10 100644
--- a/llvm/lib/IR/VectorTypeUtils.cpp
+++ b/llvm/lib/IR/VectorTypeUtils.cpp
@@ -8,23 +8,35 @@
 
 #include "llvm/IR/VectorTypeUtils.h"
 #include "llvm/ADT/SmallVectorExtras.h"
+#include "llvm/Analysis/VectorUtils.h"
 
 using namespace llvm;
 
 /// A helper for converting structs of scalar types to structs of vector types.
 /// Note: Only unpacked literal struct types are supported.
-Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC) {
+Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC,
+                                 unsigned IID) {
   if (EC.isScalar())
     return StructTy;
   assert(isUnpackedStructLiteral(StructTy) &&
          "expected unpacked struct literal");
   assert(all_of(StructTy->elements(), VectorType::isValidElementType) &&
          "expected all element types to be valid vector element types");
-  return StructType::get(
-      StructTy->getContext(),
-      map_to_vector(StructTy->elements(), [&](Type *ElTy) -> Type * {
-        return VectorType::get(ElTy, EC);
-      }));
+  if (IID != 0) {
+    SmallVector<Type *, 2> Tys;
+    for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
+      Type *ElTy = StructTy->getStructElementType(I);
+      if (!isVectorIntrinsicWithStructReturnScalarAtField(IID, I))
+        ElTy = toVectorizedTy(ElTy, EC);
+      Tys.push_back(ElTy);
+    }
+    return StructType::create(Tys);
+  } else
+    return StructType::get(
+        StructTy->getContext(),
+        map_to_vector(StructTy->elements(), [&](Type *ElTy) -> Type * {
+          return VectorType::get(ElTy, EC);
+        }));
 }
 
 /// A helper for converting structs of vector types to structs of scalar types.
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 93419100cea35..1f1ed8eb3d60d 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4100,21 +4100,10 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       Type *ScalarTy = TypeInfo.inferScalarType(ToCheck);
       if (!Visited.insert({ScalarTy}).second)
         continue;
-      Type *WideTy;
-      if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R);
-          WI && ScalarTy->isStructTy()) {
-        auto *StructTy = cast<StructType>(ScalarTy);
-        SmallVector<Type *, 2> Tys;
-        for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
-          Type *ElementTy = StructTy->getStructElementType(I);
-          if (!isVectorIntrinsicWithStructReturnScalarAtField(
-                  WI->getVectorIntrinsicID(), I))
-            ElementTy = toVectorizedTy(ElementTy, VF);
-          Tys.push_back(ElementTy);
-        }
-        WideTy = StructType::create(Tys);
-      } else
-        WideTy = toVectorizedTy(ScalarTy, VF);
+      unsigned IID = 0;
+      if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R))
+        WI->getVectorIntrinsicID();
+      Type *WideTy = toVectorizedTy(ScalarTy, VF, IID);
 
       if (any_of(getContainedTypes(WideTy), WillGenerateTargetVectors))
         return true;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 7cb15914fb2ce..ba7a832f70874 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1811,20 +1811,9 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
     Arguments.push_back(V);
   }
 
-  Type *RetTy = Ctx.Types.inferScalarType(&R);
-
-  if (VF.isVector() && RetTy->isStructTy()) {
-    auto *StructTy = cast<StructType>(RetTy);
-    SmallVector<Type *> Tys;
-    for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
-      Type *ElementTy = StructTy->getStructElementType(I);
-      if (!isVectorIntrinsicWithStructReturnScalarAtField(ID, I))
-        ElementTy = toVectorizedTy(ElementTy, VF);
-      Tys.push_back(ElementTy);
-    }
-    RetTy = StructType::get(StructTy->getContext(), Tys);
-  } else if (VF.isVector())
-    RetTy = toVectorizedTy(RetTy, VF);
+  Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
+  Type *RetTy =
+      VF.isVector() ? toVectorizedTy(ScalarRetTy, VF, ID) : ScalarRetTy;
   SmallVector<Type *> ParamTys;
   for (const VPValue *Op : Operands) {
     ParamTys.push_back(VF.isVector()

>From 6f7549882477482acd21d1e444e5212c6ac92323 Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Thu, 6 Nov 2025 06:22:49 -0800
Subject: [PATCH 3/6] Add unittest

---
 llvm/unittests/IR/VectorTypeUtilsTest.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/llvm/unittests/IR/VectorTypeUtilsTest.cpp b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
index c77f183e921de..5d4d30afa8fb0 100644
--- a/llvm/unittests/IR/VectorTypeUtilsTest.cpp
+++ b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/IR/VectorTypeUtils.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
 #include "gtest/gtest.h"
 
@@ -24,6 +25,7 @@ TEST(VectorTypeUtilsTest, TestToVectorizedTy) {
   Type *FTy = Type::getFloatTy(C);
   Type *HomogeneousStructTy = StructType::get(FTy, FTy, FTy);
   Type *MixedStructTy = StructType::get(FTy, ITy);
+  Type *FFLoadRetTy = StructType::get(ITy, ITy);
   Type *VoidTy = Type::getVoidTy(C);
 
   for (ElementCount VF :
@@ -54,6 +56,11 @@ TEST(VectorTypeUtilsTest, TestToVectorizedTy) {
                 VectorType::get(ITy, VF));
 
     EXPECT_EQ(toVectorizedTy(VoidTy, VF), VoidTy);
+    Type *WidenFFLoadRetTy =
+        toVectorizedTy(FFLoadRetTy, VF, Intrinsic::vp_load_ff);
+    EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(0),
+              VectorType::get(ITy, VF));
+    EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(1), ITy);
   }
 
   ElementCount ScalarVF = ElementCount::getFixed(1);

>From 593dfb1a32694c7c45d984a7e8a757f7effe6ca2 Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Thu, 6 Nov 2025 07:19:37 -0800
Subject: [PATCH 4/6] Move changes to toVectorizedRetTy

---
 llvm/include/llvm/IR/VectorTypeUtils.h        | 21 ++++++----
 llvm/lib/IR/VectorTypeUtils.cpp               | 39 ++++++++++++-------
 .../Transforms/Vectorize/LoopVectorize.cpp    |  2 +-
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  2 +-
 llvm/unittests/IR/VectorTypeUtilsTest.cpp     |  2 +-
 5 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/llvm/include/llvm/IR/VectorTypeUtils.h b/llvm/include/llvm/IR/VectorTypeUtils.h
index 53fba9db09b1d..db82cb3fd5570 100644
--- a/llvm/include/llvm/IR/VectorTypeUtils.h
+++ b/llvm/include/llvm/IR/VectorTypeUtils.h
@@ -31,11 +31,7 @@ inline Type *toVectorTy(Type *Scalar, unsigned VF) {
 /// Note:
 ///   - If \p EC is scalar, \p StructTy is returned unchanged
 ///   - Only unpacked literal struct types are supported
-///   vector types.
-///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
-///   are widened.
-LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC,
-                                    unsigned IID = 0);
+LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC);
 
 /// A helper for converting structs of vector types to structs of scalar types.
 /// Note: Only unpacked literal struct types are supported.
@@ -49,6 +45,17 @@ LLVM_ABI bool isVectorizedStructTy(StructType *StructTy);
 /// are scalars that can be used as vector element types.
 LLVM_ABI bool canVectorizeStructTy(StructType *StructTy);
 
+/// A helper for converting to vectorized return types. For scalar types, this
+/// is equivalent to calling `toVectorTy`. For struct types, this returns a new
+/// struct where each element type has been widened to a vector type.
+/// Note:
+///   - If the incoming type is void, we return void
+///   - If \p EC is scalar, \p Ty is returned unchanged
+///   - Only unpacked literal struct types are supported
+///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
+///   are widened.
+LLVM_ABI Type *toVectorizedRetTy(Type *Ty, ElementCount EC, unsigned IID = 0);
+
 /// A helper for converting to vectorized types. For scalar types, this is
 /// equivalent to calling `toVectorTy`. For struct types, this returns a new
 /// struct where each element type has been widened to a vector type.
@@ -56,9 +63,9 @@ LLVM_ABI bool canVectorizeStructTy(StructType *StructTy);
 ///   - If the incoming type is void, we return void
 ///   - If \p EC is scalar, \p Ty is returned unchanged
 ///   - Only unpacked literal struct types are supported
-inline Type *toVectorizedTy(Type *Ty, ElementCount EC, unsigned IID = 0) {
+inline Type *toVectorizedTy(Type *Ty, ElementCount EC) {
   if (StructType *StructTy = dyn_cast<StructType>(Ty))
-    return toVectorizedStructTy(StructTy, EC, IID);
+    return toVectorizedStructTy(StructTy, EC);
   return toVectorTy(Ty, EC);
 }
 
diff --git a/llvm/lib/IR/VectorTypeUtils.cpp b/llvm/lib/IR/VectorTypeUtils.cpp
index 744fe2a704a10..c6269f6cd5911 100644
--- a/llvm/lib/IR/VectorTypeUtils.cpp
+++ b/llvm/lib/IR/VectorTypeUtils.cpp
@@ -14,29 +14,42 @@ using namespace llvm;
 
 /// A helper for converting structs of scalar types to structs of vector types.
 /// Note: Only unpacked literal struct types are supported.
-Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC,
-                                 unsigned IID) {
+Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC) {
   if (EC.isScalar())
     return StructTy;
   assert(isUnpackedStructLiteral(StructTy) &&
          "expected unpacked struct literal");
   assert(all_of(StructTy->elements(), VectorType::isValidElementType) &&
          "expected all element types to be valid vector element types");
-  if (IID != 0) {
-    SmallVector<Type *, 2> Tys;
-    for (unsigned I = 0, E = StructTy->getNumElements(); I != E; ++I) {
-      Type *ElTy = StructTy->getStructElementType(I);
-      if (!isVectorIntrinsicWithStructReturnScalarAtField(IID, I))
-        ElTy = toVectorizedTy(ElTy, EC);
-      Tys.push_back(ElTy);
-    }
-    return StructType::create(Tys);
-  } else
+  return StructType::get(
+      StructTy->getContext(),
+      map_to_vector(StructTy->elements(), [&](Type *ElTy) -> Type * {
+        return VectorType::get(ElTy, EC);
+      }));
+}
+
+Type *llvm::toVectorizedRetTy(Type *Ty, ElementCount EC, unsigned IID) {
+  if (EC.isScalar())
+    return Ty;
+
+  if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
+    assert(isUnpackedStructLiteral(StructTy) &&
+           "expected unpacked struct literal");
+    assert(all_of(StructTy->elements(), VectorType::isValidElementType) &&
+           "expected all element types to be valid vector element types");
     return StructType::get(
         StructTy->getContext(),
-        map_to_vector(StructTy->elements(), [&](Type *ElTy) -> Type * {
+        map_to_vector(enumerate(StructTy->elements()), [&](auto It) -> Type * {
+          Type *ElTy = It.value();
+          if (IID != 0 &&
+              isVectorIntrinsicWithStructReturnScalarAtField(IID, It.index()))
+            return ElTy;
           return VectorType::get(ElTy, EC);
         }));
+  }
+  if (IID != 0 && isVectorIntrinsicWithScalarOpAtArg(IID, -1, nullptr))
+    return Ty;
+  return toVectorTy(Ty, EC);
 }
 
 /// A helper for converting structs of vector types to structs of scalar types.
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 1f1ed8eb3d60d..1f605e93df1b7 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4103,7 +4103,7 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       unsigned IID = 0;
       if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R))
         WI->getVectorIntrinsicID();
-      Type *WideTy = toVectorizedTy(ScalarTy, VF, IID);
+      Type *WideTy = toVectorizedRetTy(ScalarTy, VF, IID);
 
       if (any_of(getContainedTypes(WideTy), WillGenerateTargetVectors))
         return true;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index ba7a832f70874..752bbe5da4a50 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1813,7 +1813,7 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
 
   Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
   Type *RetTy =
-      VF.isVector() ? toVectorizedTy(ScalarRetTy, VF, ID) : ScalarRetTy;
+      VF.isVector() ? toVectorizedRetTy(ScalarRetTy, VF, ID) : ScalarRetTy;
   SmallVector<Type *> ParamTys;
   for (const VPValue *Op : Operands) {
     ParamTys.push_back(VF.isVector()
diff --git a/llvm/unittests/IR/VectorTypeUtilsTest.cpp b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
index 5d4d30afa8fb0..e8da8046ab28e 100644
--- a/llvm/unittests/IR/VectorTypeUtilsTest.cpp
+++ b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
@@ -57,7 +57,7 @@ TEST(VectorTypeUtilsTest, TestToVectorizedTy) {
 
     EXPECT_EQ(toVectorizedTy(VoidTy, VF), VoidTy);
     Type *WidenFFLoadRetTy =
-        toVectorizedTy(FFLoadRetTy, VF, Intrinsic::vp_load_ff);
+        toVectorizedRetTy(FFLoadRetTy, VF, Intrinsic::vp_load_ff);
     EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(0),
               VectorType::get(ITy, VF));
     EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(1), ITy);

>From 3d64710e5644061fe7df9ec150a818bf698b339b Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Sun, 9 Nov 2025 18:42:29 -0800
Subject: [PATCH 5/6] Move logic to toVectorizedStructTy

---
 llvm/include/llvm/IR/VectorTypeUtils.h        | 21 ++++--------
 llvm/lib/IR/VectorTypeUtils.cpp               | 33 ++++---------------
 .../Transforms/Vectorize/LoopVectorize.cpp    |  2 +-
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  2 +-
 llvm/unittests/IR/VectorTypeUtilsTest.cpp     |  2 +-
 5 files changed, 17 insertions(+), 43 deletions(-)

diff --git a/llvm/include/llvm/IR/VectorTypeUtils.h b/llvm/include/llvm/IR/VectorTypeUtils.h
index db82cb3fd5570..53fba9db09b1d 100644
--- a/llvm/include/llvm/IR/VectorTypeUtils.h
+++ b/llvm/include/llvm/IR/VectorTypeUtils.h
@@ -31,7 +31,11 @@ inline Type *toVectorTy(Type *Scalar, unsigned VF) {
 /// Note:
 ///   - If \p EC is scalar, \p StructTy is returned unchanged
 ///   - Only unpacked literal struct types are supported
-LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC);
+///   vector types.
+///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
+///   are widened.
+LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC,
+                                    unsigned IID = 0);
 
 /// A helper for converting structs of vector types to structs of scalar types.
 /// Note: Only unpacked literal struct types are supported.
@@ -45,17 +49,6 @@ LLVM_ABI bool isVectorizedStructTy(StructType *StructTy);
 /// are scalars that can be used as vector element types.
 LLVM_ABI bool canVectorizeStructTy(StructType *StructTy);
 
-/// A helper for converting to vectorized return types. For scalar types, this
-/// is equivalent to calling `toVectorTy`. For struct types, this returns a new
-/// struct where each element type has been widened to a vector type.
-/// Note:
-///   - If the incoming type is void, we return void
-///   - If \p EC is scalar, \p Ty is returned unchanged
-///   - Only unpacked literal struct types are supported
-///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
-///   are widened.
-LLVM_ABI Type *toVectorizedRetTy(Type *Ty, ElementCount EC, unsigned IID = 0);
-
 /// A helper for converting to vectorized types. For scalar types, this is
 /// equivalent to calling `toVectorTy`. For struct types, this returns a new
 /// struct where each element type has been widened to a vector type.
@@ -63,9 +56,9 @@ LLVM_ABI Type *toVectorizedRetTy(Type *Ty, ElementCount EC, unsigned IID = 0);
 ///   - If the incoming type is void, we return void
 ///   - If \p EC is scalar, \p Ty is returned unchanged
 ///   - Only unpacked literal struct types are supported
-inline Type *toVectorizedTy(Type *Ty, ElementCount EC) {
+inline Type *toVectorizedTy(Type *Ty, ElementCount EC, unsigned IID = 0) {
   if (StructType *StructTy = dyn_cast<StructType>(Ty))
-    return toVectorizedStructTy(StructTy, EC);
+    return toVectorizedStructTy(StructTy, EC, IID);
   return toVectorTy(Ty, EC);
 }
 
diff --git a/llvm/lib/IR/VectorTypeUtils.cpp b/llvm/lib/IR/VectorTypeUtils.cpp
index c6269f6cd5911..9805af7e8dbe0 100644
--- a/llvm/lib/IR/VectorTypeUtils.cpp
+++ b/llvm/lib/IR/VectorTypeUtils.cpp
@@ -14,7 +14,8 @@ using namespace llvm;
 
 /// A helper for converting structs of scalar types to structs of vector types.
 /// Note: Only unpacked literal struct types are supported.
-Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC) {
+Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC,
+                                 unsigned IID) {
   if (EC.isScalar())
     return StructTy;
   assert(isUnpackedStructLiteral(StructTy) &&
@@ -23,35 +24,15 @@ Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC) {
          "expected all element types to be valid vector element types");
   return StructType::get(
       StructTy->getContext(),
-      map_to_vector(StructTy->elements(), [&](Type *ElTy) -> Type * {
+      map_to_vector(enumerate(StructTy->elements()), [&](auto It) -> Type * {
+        Type *ElTy = It.value();
+        if (IID != 0 &&
+            isVectorIntrinsicWithStructReturnScalarAtField(IID, It.index()))
+          return ElTy;
         return VectorType::get(ElTy, EC);
       }));
 }
 
-Type *llvm::toVectorizedRetTy(Type *Ty, ElementCount EC, unsigned IID) {
-  if (EC.isScalar())
-    return Ty;
-
-  if (StructType *StructTy = dyn_cast<StructType>(Ty)) {
-    assert(isUnpackedStructLiteral(StructTy) &&
-           "expected unpacked struct literal");
-    assert(all_of(StructTy->elements(), VectorType::isValidElementType) &&
-           "expected all element types to be valid vector element types");
-    return StructType::get(
-        StructTy->getContext(),
-        map_to_vector(enumerate(StructTy->elements()), [&](auto It) -> Type * {
-          Type *ElTy = It.value();
-          if (IID != 0 &&
-              isVectorIntrinsicWithStructReturnScalarAtField(IID, It.index()))
-            return ElTy;
-          return VectorType::get(ElTy, EC);
-        }));
-  }
-  if (IID != 0 && isVectorIntrinsicWithScalarOpAtArg(IID, -1, nullptr))
-    return Ty;
-  return toVectorTy(Ty, EC);
-}
-
 /// A helper for converting structs of vector types to structs of scalar types.
 /// Note: Only unpacked literal struct types are supported.
 Type *llvm::toScalarizedStructTy(StructType *StructTy) {
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 1f605e93df1b7..1f1ed8eb3d60d 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4103,7 +4103,7 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       unsigned IID = 0;
       if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R))
         WI->getVectorIntrinsicID();
-      Type *WideTy = toVectorizedRetTy(ScalarTy, VF, IID);
+      Type *WideTy = toVectorizedTy(ScalarTy, VF, IID);
 
       if (any_of(getContainedTypes(WideTy), WillGenerateTargetVectors))
         return true;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 752bbe5da4a50..ba7a832f70874 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1813,7 +1813,7 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
 
   Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
   Type *RetTy =
-      VF.isVector() ? toVectorizedRetTy(ScalarRetTy, VF, ID) : ScalarRetTy;
+      VF.isVector() ? toVectorizedTy(ScalarRetTy, VF, ID) : ScalarRetTy;
   SmallVector<Type *> ParamTys;
   for (const VPValue *Op : Operands) {
     ParamTys.push_back(VF.isVector()
diff --git a/llvm/unittests/IR/VectorTypeUtilsTest.cpp b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
index e8da8046ab28e..5d4d30afa8fb0 100644
--- a/llvm/unittests/IR/VectorTypeUtilsTest.cpp
+++ b/llvm/unittests/IR/VectorTypeUtilsTest.cpp
@@ -57,7 +57,7 @@ TEST(VectorTypeUtilsTest, TestToVectorizedTy) {
 
     EXPECT_EQ(toVectorizedTy(VoidTy, VF), VoidTy);
     Type *WidenFFLoadRetTy =
-        toVectorizedRetTy(FFLoadRetTy, VF, Intrinsic::vp_load_ff);
+        toVectorizedTy(FFLoadRetTy, VF, Intrinsic::vp_load_ff);
     EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(0),
               VectorType::get(ITy, VF));
     EXPECT_EQ(cast<StructType>(WidenFFLoadRetTy)->getElementType(1), ITy);

>From e3fefe0c928b52bf50b3f26b464f9c3099e2febb Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Sun, 9 Nov 2025 19:13:08 -0800
Subject: [PATCH 6/6] Move logic to VectorTypeUtils.h

---
 llvm/include/llvm/Analysis/VectorUtils.h |  5 -----
 llvm/include/llvm/IR/VectorTypeUtils.h   |  7 ++++++-
 llvm/lib/Analysis/VectorUtils.cpp        | 10 ----------
 llvm/lib/IR/VectorTypeUtils.cpp          | 12 +++++++++++-
 4 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h
index f0c064f5490a7..ce94906ee7c00 100644
--- a/llvm/include/llvm/Analysis/VectorUtils.h
+++ b/llvm/include/llvm/Analysis/VectorUtils.h
@@ -164,11 +164,6 @@ LLVM_ABI bool
 isVectorIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID, int OpdIdx,
                                        const TargetTransformInfo *TTI);
 
-/// Identifies if the vector form of the intrinsic that returns a struct has
-/// a scalar element at the struct element index \p RetIdx.
-LLVM_ABI bool isVectorIntrinsicWithStructReturnScalarAtField(Intrinsic::ID ID,
-                                                             int RetIdx);
-
 /// Identifies if the vector form of the intrinsic that returns a struct is
 /// overloaded at the struct element index \p RetIdx. /// \p TTI is used to
 /// consider target specific intrinsics, if no target specific intrinsics
diff --git a/llvm/include/llvm/IR/VectorTypeUtils.h b/llvm/include/llvm/IR/VectorTypeUtils.h
index 53fba9db09b1d..ca1554935975f 100644
--- a/llvm/include/llvm/IR/VectorTypeUtils.h
+++ b/llvm/include/llvm/IR/VectorTypeUtils.h
@@ -14,6 +14,11 @@
 
 namespace llvm {
 
+/// Identifies if the vector form of the intrinsic that returns a struct has
+/// a scalar element at the struct element index \p EleIdx.
+LLVM_ABI bool isVectorIntrinsicWithStructReturnScalarAtField(unsigned ID,
+                                                             int EleIdx);
+
 /// A helper function for converting Scalar types to vector types. If
 /// the incoming type is void, we return void. If the EC represents a
 /// scalar, we return the scalar type.
@@ -32,7 +37,7 @@ inline Type *toVectorTy(Type *Scalar, unsigned VF) {
 ///   - If \p EC is scalar, \p StructTy is returned unchanged
 ///   - Only unpacked literal struct types are supported
 ///   vector types.
-///   - If IID (Intrinsic::ID) is provided, only fields that are vector types
+///   - If IID (Intrinsic ID) is provided, only fields that are vector types
 ///   are widened.
 LLVM_ABI Type *toVectorizedStructTy(StructType *StructTy, ElementCount EC,
                                     unsigned IID = 0);
diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index 1f2fc80501cba..12a1f1da584d4 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -217,16 +217,6 @@ bool llvm::isVectorIntrinsicWithOverloadTypeAtArg(
   }
 }
 
-bool llvm::isVectorIntrinsicWithStructReturnScalarAtField(Intrinsic::ID ID,
-                                                          int RetIdx) {
-  switch (ID) {
-  case Intrinsic::vp_load_ff:
-    return RetIdx == 1;
-  default:
-    return false;
-  }
-}
-
 bool llvm::isVectorIntrinsicWithStructReturnOverloadAtField(
     Intrinsic::ID ID, int RetIdx, const TargetTransformInfo *TTI) {
 
diff --git a/llvm/lib/IR/VectorTypeUtils.cpp b/llvm/lib/IR/VectorTypeUtils.cpp
index 9805af7e8dbe0..80ee1c199e158 100644
--- a/llvm/lib/IR/VectorTypeUtils.cpp
+++ b/llvm/lib/IR/VectorTypeUtils.cpp
@@ -8,10 +8,20 @@
 
 #include "llvm/IR/VectorTypeUtils.h"
 #include "llvm/ADT/SmallVectorExtras.h"
-#include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/Intrinsics.h"
 
 using namespace llvm;
 
+bool llvm::isVectorIntrinsicWithStructReturnScalarAtField(unsigned ID,
+                                                          int EleIdx) {
+  switch (ID) {
+  case Intrinsic::vp_load_ff:
+    return EleIdx == 1;
+  default:
+    return false;
+  }
+}
+
 /// A helper for converting structs of scalar types to structs of vector types.
 /// Note: Only unpacked literal struct types are supported.
 Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC,



More information about the llvm-commits mailing list