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

Shih-Po Hung via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 13 17:15:04 PST 2025


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

>From 4329bf578d784840dbc7e5ab12d822f9bb9522e9 Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Wed, 3 Dec 2025 03:51:02 -0800
Subject: [PATCH 1/2] [VectorTypeUtil] Support struct return types for widen
 intrinsics (NFC). #165218

Splitting from #151300, vp_load_ff returns a struct type that cannot be
widened by toVectorizedTy.
This patch adds isVectorIntrinsicWithStructReturnScalarAtField and widen
each struct element type independently.
---
 llvm/include/llvm/IR/VectorTypeUtils.h          | 15 ++++++++++++---
 llvm/lib/Analysis/VectorUtils.cpp               |  4 ++++
 llvm/lib/IR/VectorTypeUtils.cpp                 | 16 ++++++++++++++--
 llvm/lib/Transforms/Vectorize/LoopVectorize.cpp |  6 +++++-
 llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp  | 14 ++++++++++++--
 llvm/unittests/IR/VectorTypeUtilsTest.cpp       |  7 +++++++
 6 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/IR/VectorTypeUtils.h b/llvm/include/llvm/IR/VectorTypeUtils.h
index e3d7fadad6089..3db5c4a2b6576 100644
--- a/llvm/include/llvm/IR/VectorTypeUtils.h
+++ b/llvm/include/llvm/IR/VectorTypeUtils.h
@@ -14,6 +14,11 @@
 
 namespace llvm {
 
+/// Returns true if \p IID is a vector intrinsic that returns a struct with a
+/// scalar element at index \p EleIdx.
+LLVM_ABI bool isVectorIntrinsicWithStructReturnScalarAtField(unsigned IID,
+                                                             unsigned 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.
@@ -31,7 +36,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 +61,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/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index a3e9b039f9225..85d1516f40c4c 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -224,6 +224,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/IR/VectorTypeUtils.cpp b/llvm/lib/IR/VectorTypeUtils.cpp
index 62e39aab90079..5b0b62dac1473 100644
--- a/llvm/lib/IR/VectorTypeUtils.cpp
+++ b/llvm/lib/IR/VectorTypeUtils.cpp
@@ -8,12 +8,21 @@
 
 #include "llvm/IR/VectorTypeUtils.h"
 #include "llvm/ADT/SmallVectorExtras.h"
+#include "llvm/IR/Intrinsics.h"
 
 using namespace llvm;
 
+bool llvm::isVectorIntrinsicWithStructReturnScalarAtField(unsigned IID,
+                                                          unsigned EleIdx) {
+  if (IID == Intrinsic::vp_load_ff)
+    return EleIdx == 1;
+  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) {
+Type *llvm::toVectorizedStructTy(StructType *StructTy, ElementCount EC,
+                                 unsigned IID) {
   if (EC.isScalar())
     return StructTy;
   assert(isUnpackedStructLiteral(StructTy) &&
@@ -22,7 +31,10 @@ 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 (isVectorIntrinsicWithStructReturnScalarAtField(IID, It.index()))
+          return ElTy;
         return VectorType::get(ElTy, EC);
       }));
 }
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 62b68232925d9..b954d3f5ebe44 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4139,7 +4139,11 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       Type *ScalarTy = TypeInfo.inferScalarType(ToCheck);
       if (!Visited.insert({ScalarTy}).second)
         continue;
-      Type *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 0baf7172e4443..a40df5084649f 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1679,7 +1679,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())) {
@@ -1744,7 +1753,8 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
   }
 
   Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
-  Type *RetTy = VF.isVector() ? toVectorizedTy(ScalarRetTy, VF) : ScalarRetTy;
+  Type *RetTy =
+      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 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 d3a43e5429839964f67d094ee62fa7fb0455293e Mon Sep 17 00:00:00 2001
From: ShihPo Hung <shihpo.hung at sifive.com>
Date: Wed, 3 Dec 2025 04:01:20 -0800
Subject: [PATCH 2/2] Split LV changes

---
 llvm/lib/Analysis/VectorUtils.cpp               |  4 ----
 llvm/lib/Transforms/Vectorize/LoopVectorize.cpp |  6 +-----
 llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp  | 14 ++------------
 3 files changed, 3 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index 85d1516f40c4c..a3e9b039f9225 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -224,10 +224,6 @@ 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 b954d3f5ebe44..62b68232925d9 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4139,11 +4139,7 @@ static bool willGenerateVectors(VPlan &Plan, ElementCount VF,
       Type *ScalarTy = TypeInfo.inferScalarType(ToCheck);
       if (!Visited.insert({ScalarTy}).second)
         continue;
-      unsigned IID = 0;
-      if (auto *WI = dyn_cast<VPWidenIntrinsicRecipe>(&R))
-        WI->getVectorIntrinsicID();
-      Type *WideTy = toVectorizedTy(ScalarTy, VF, IID);
-
+      Type *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 a40df5084649f..0baf7172e4443 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1679,16 +1679,7 @@ void VPWidenIntrinsicRecipe::execute(VPTransformState &State) {
 
   SmallVector<Type *, 2> TysForDecl;
   // Add return type if intrinsic is overloaded on it.
-  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))
+  if (isVectorIntrinsicWithOverloadTypeAtArg(VectorIntrinsicID, -1, State.TTI))
     TysForDecl.push_back(VectorType::get(getResultType(), State.VF));
   SmallVector<Value *, 4> Args;
   for (const auto &I : enumerate(operands())) {
@@ -1753,8 +1744,7 @@ static InstructionCost getCostForIntrinsics(Intrinsic::ID ID,
   }
 
   Type *ScalarRetTy = Ctx.Types.inferScalarType(&R);
-  Type *RetTy =
-      VF.isVector() ? toVectorizedTy(ScalarRetTy, VF, ID) : ScalarRetTy;
+  Type *RetTy = VF.isVector() ? toVectorizedTy(ScalarRetTy, VF) : ScalarRetTy;
   SmallVector<Type *> ParamTys;
   for (const VPValue *Op : Operands) {
     ParamTys.push_back(VF.isVector()



More information about the llvm-commits mailing list