[llvm] [VFABI] Create FunctionType for vector functions (PR #75058)

Paschalis Mpeis via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 13 01:36:36 PST 2023


https://github.com/paschalis-mpeis updated https://github.com/llvm/llvm-project/pull/75058

>From 73d59dd54cc972bab2fac190973da010289c25ea Mon Sep 17 00:00:00 2001
From: Paschalis Mpeis <Paschalis.Mpeis at arm.com>
Date: Fri, 1 Dec 2023 17:56:59 +0000
Subject: [PATCH 1/3] [VFABI] Create FunctionType for vector functions

`createFunctionType` optionally returns a FunctionType and the mask's
position when there's one. It requires VFInfo and an Instruction.

Add `checkFunctionType` in 'VectorFunctionABITest.cpp' tests to check
that both the number and the type of vectorized parameters matches the
created `FunctionType`.
---
 llvm/include/llvm/Analysis/VectorUtils.h      |   7 +
 llvm/lib/Analysis/VFABIDemangling.cpp         |   2 +-
 llvm/lib/Analysis/VectorUtils.cpp             |  45 +++++
 .../Analysis/VectorFunctionABITest.cpp        | 157 ++++++++++++++----
 4 files changed, 181 insertions(+), 30 deletions(-)

diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h
index 55a6aa645a86e2..734c440283b4a0 100644
--- a/llvm/include/llvm/Analysis/VectorUtils.h
+++ b/llvm/include/llvm/Analysis/VectorUtils.h
@@ -195,6 +195,13 @@ static constexpr char const *MappingsAttrName = "vector-function-abi-variant";
 /// the presence of the attribute (see InjectTLIMappings).
 void getVectorVariantNames(const CallInst &CI,
                            SmallVectorImpl<std::string> &VariantMappings);
+
+/// Returns a pair of the vectorized FunctionType and the mask's position when
+/// there's one, otherwise -1. It rejects any non vectorized calls as this
+/// method should be called at a point where the Instruction \p I is already
+/// vectorized.
+std::optional<std::pair<FunctionType *, int>>
+createFunctionType(const VFInfo &Info, const Instruction *I, const Module *M);
 } // end namespace VFABI
 
 /// The Vector Function Database.
diff --git a/llvm/lib/Analysis/VFABIDemangling.cpp b/llvm/lib/Analysis/VFABIDemangling.cpp
index 92af314a41caad..fc94a33851963c 100644
--- a/llvm/lib/Analysis/VFABIDemangling.cpp
+++ b/llvm/lib/Analysis/VFABIDemangling.cpp
@@ -376,7 +376,7 @@ std::optional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName,
   // _ZGV<isa><mask><vlen><parameters>_<scalarname>.
   StringRef VectorName = MangledName;
 
-  // Parse the fixed size part of the manled name
+  // Parse the fixed size part of the mangled name
   if (!MangledName.consume_front("_ZGV"))
     return std::nullopt;
 
diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index 91d8c31fa062de..c31f0f3bd2fd58 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -12,6 +12,7 @@
 
 #include "llvm/Analysis/VectorUtils.h"
 #include "llvm/ADT/EquivalenceClasses.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Analysis/DemandedBits.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/LoopIterator.h"
@@ -24,6 +25,7 @@
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/IR/Value.h"
 #include "llvm/Support/CommandLine.h"
+#include <optional>
 
 #define DEBUG_TYPE "vectorutils"
 
@@ -1477,6 +1479,49 @@ void VFABI::getVectorVariantNames(
   }
 }
 
+// Returns whether any of the operands or return type of \p I are vectors.
+static bool isVectorized(const Instruction *I) {
+  if (I->getType()->isVectorTy())
+    return true;
+  for (auto &U : I->operands())
+    if (U->getType()->isVectorTy())
+      return true;
+  return false;
+}
+
+std::optional<std::pair<FunctionType *, int>>
+VFABI::createFunctionType(const VFInfo &Info, const Instruction *I,
+                          const Module *M) {
+  // only vectorized calls should reach this method
+  if (!isVectorized(I))
+    return std::nullopt;
+
+  ElementCount VF = Info.Shape.VF;
+  // get vectorized operands
+  const bool IsCall = isa<CallBase>(I);
+  SmallVector<Type *, 8> VecParams;
+  for (auto [i, U] : enumerate(I->operands())) {
+    // ignore the function pointer when the Instruction is a call
+    if (IsCall && i == I->getNumOperands() - 1)
+      break;
+    VecParams.push_back(U->getType());
+  }
+
+  // Append a mask and get its position.
+  int MaskPos = -1;
+  if (Info.isMasked()) {
+    auto OptMaskPos = Info.getParamIndexForOptionalMask();
+    if (!OptMaskPos)
+      return std::nullopt;
+
+    MaskPos = OptMaskPos.value();
+    VectorType *MaskTy = VectorType::get(Type::getInt1Ty(M->getContext()), VF);
+    VecParams.insert(VecParams.begin() + MaskPos, MaskTy);
+  }
+  FunctionType *VecFTy = FunctionType::get(I->getType(), VecParams, false);
+  return std::make_pair(VecFTy, MaskPos);
+}
+
 bool VFShape::hasValidParameterList() const {
   for (unsigned Pos = 0, NumParams = Parameters.size(); Pos < NumParams;
        ++Pos) {
diff --git a/llvm/unittests/Analysis/VectorFunctionABITest.cpp b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
index 201dd1127ef234..85177db74ef585 100644
--- a/llvm/unittests/Analysis/VectorFunctionABITest.cpp
+++ b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
@@ -11,6 +11,7 @@
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/IR/InstIterator.h"
 #include "gtest/gtest.h"
+#include <optional>
 
 using namespace llvm;
 
@@ -91,6 +92,77 @@ class VFABIParserTest : public ::testing::Test {
   bool matchParametersNum() {
     return (Parameters.size() - isMasked()) == ScalarFTy->getNumParams();
   }
+
+  /// Creates a mock CallInst and uses it along with VFInfo to create a
+  /// FunctionType. Then it checks that the created FunctionType matches the
+  /// number and type of arguments with both the ScalarFTy and the operands of
+  /// the call.
+  bool checkFunctionType() {
+    // Create a mock vectorized CallInst using dummy values and then use it to
+    // create a vector FunctionType. In the case of scalable ISAs, the created
+    // vector FunctionType might have a mask parameter Type, however, this input
+    // CallInst will not have a mask operand.
+    SmallVector<Value *, 8> Args;
+    SmallVector<Type *, 8> CallTypes;
+    for (auto [VFParam, STy] :
+         zip(Info.Shape.Parameters, ScalarFTy->params())) {
+      // use VectorType where relevant, according to VShape
+      Type *UseTy = STy;
+      if (VFParam.ParamKind == VFParamKind::Vector)
+        UseTy = VectorType::get(STy, Info.Shape.VF);
+
+      CallTypes.push_back(UseTy);
+      Args.push_back(Constant::getNullValue(UseTy));
+    }
+
+    // Mangled names do not currently encode return Type information. Generally,
+    // return types are vectors, so use one.
+    Type *RetTy = ScalarFTy->getReturnType();
+    if (!RetTy->isVoidTy())
+      RetTy = VectorType::get(RetTy, Info.Shape.VF);
+
+    FunctionCallee F = M->getOrInsertFunction(
+        VectorName, FunctionType::get(RetTy, CallTypes, false));
+    std::unique_ptr<CallInst> CI(CallInst::Create(F, Args));
+
+    // Use VFInfo and the mock CallInst to create a FunctionType that will
+    // include a mask where relevant.
+    auto OptVecFTyPos = VFABI::createFunctionType(Info, CI.get(), M.get());
+    if (!OptVecFTyPos)
+      return false;
+
+    FunctionType *VecFTy = OptVecFTyPos->first;
+    // Check that vectorized parameters' size match with VFInfo.
+    // Both may include a mask.
+    if ((VecFTy->getNumParams() != Info.Shape.Parameters.size()))
+      return false;
+
+    // Check if the types of the vectorized parameters from the created
+    // FunctionType match with the arguments passed to the CallInst. Any masks
+    // are ignored, as the original, mock CallInst does not have one.
+    auto VecParams = VecFTy->params();
+    for (auto [VecTy, VFTyParam] : zip(CallTypes, VecParams))
+      if (VecTy != VFTyParam)
+        return false;
+
+    // Check if the types of the scalar and vector FunctionTypes match.
+    // In the case of a mask, the vector FunctionType should have an additional
+    // i1 vector parameter.
+    if (ScalarFTy->getReturnType() != VecFTy->getReturnType()->getScalarType())
+      return false;
+    auto ScalarParams = ScalarFTy->params();
+    for (auto [OptSTy, OptVTy] : zip_longest(ScalarParams, VecParams)) {
+      Type *VTy = OptVTy.value();
+      // ensure the extra vector Type is a mask
+      if (!OptSTy && VTy->isVectorTy() &&
+          VTy->getScalarType() != Type::getInt1Ty(M->getContext()))
+        return false;
+      if (OptSTy && OptSTy.value() != VTy->getScalarType())
+        return false;
+    }
+
+    return true;
+  }
 };
 } // unnamed namespace
 
@@ -130,7 +202,8 @@ TEST_F(VFABIParserTest, ParamListParsing) {
       invokeParser("_ZGVnN2vl16Ls32R3l_foo", "void(i32, i32, i32, ptr, i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_EQ(false, isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)5);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 16}));
@@ -145,7 +218,8 @@ TEST_F(VFABIParserTest, ScalarNameAndVectorName_01) {
   EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_EQ(true, isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(ScalarName, "foo");
   EXPECT_EQ(VectorName, "vector_foo");
 }
@@ -154,7 +228,8 @@ TEST_F(VFABIParserTest, ScalarNameAndVectorName_02) {
   EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_EQ(true, isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(ScalarName, "foo");
   EXPECT_EQ(VectorName, "vector_foo");
 }
@@ -164,7 +239,8 @@ TEST_F(VFABIParserTest, ScalarNameAndVectorName_03) {
       invokeParser("_ZGVnM2v___foo_bar_abc(fooBarAbcVec)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_EQ(true, isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(ScalarName, "__foo_bar_abc");
   EXPECT_EQ(VectorName, "fooBarAbcVec");
 }
@@ -185,7 +261,8 @@ TEST_F(VFABIParserTest, Parse) {
                    "void(i32, i32, i32, i32, ptr, i32, i32, i32, ptr)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)9);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));
@@ -205,7 +282,8 @@ TEST_F(VFABIParserTest, ParseVectorName) {
   EXPECT_TRUE(invokeParser("_ZGVnN2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));
@@ -218,7 +296,8 @@ TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) {
                            "void(i32, i32, i32, ptr)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)4);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, -1}));
@@ -233,7 +312,8 @@ TEST_F(VFABIParserTest, ParseScalableSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getScalable(4));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -246,7 +326,8 @@ TEST_F(VFABIParserTest, ParseFixedWidthSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -280,7 +361,8 @@ TEST_F(VFABIParserTest, LinearWithoutCompileTime) {
                            "void(i32, i32, ptr, i32, i32, i32, ptr, i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
   EXPECT_EQ(Parameters.size(), (unsigned)8);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, 1}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, 1}));
@@ -299,7 +381,8 @@ TEST_F(VFABIParserTest, LLVM_ISA) {
   EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::LLVM);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
   EXPECT_EQ(ScalarName, "foo");
@@ -318,7 +401,7 @@ TEST_F(VFABIParserTest, Align) {
   EXPECT_TRUE(invokeParser("_ZGVsN2l2a2_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0].Alignment, Align(2));
   EXPECT_EQ(ScalarName, "foo");
@@ -341,7 +424,8 @@ TEST_F(VFABIParserTest, ParseUniform) {
   EXPECT_TRUE(invokeParser("_ZGVnN2u_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0}));
@@ -374,8 +458,8 @@ TEST_F(VFABIParserTest, ISAIndependentMangling) {
   do {                                                                         \
     EXPECT_EQ(VF, ElementCount::getFixed(2));                                  \
     EXPECT_FALSE(isMasked());                                                  \
-    EXPECT_TRUE(matchParametersNum())                                          \
-        << "Different number of scalar parameters";                            \
+    EXPECT_TRUE(matchParametersNum());                                         \
+    EXPECT_TRUE(checkFunctionType());                                          \
     EXPECT_EQ(Parameters.size(), (unsigned)10);                                \
     EXPECT_EQ(Parameters, ExpectedParams);                                     \
     EXPECT_EQ(ScalarName, "foo");                                              \
@@ -450,7 +534,8 @@ TEST_F(VFABIParserTest, ParseMaskingNEON) {
   EXPECT_TRUE(invokeParser("_ZGVnM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -463,7 +548,8 @@ TEST_F(VFABIParserTest, ParseMaskingSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -476,7 +562,8 @@ TEST_F(VFABIParserTest, ParseMaskingSSE) {
   EXPECT_TRUE(invokeParser("_ZGVbM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SSE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -489,7 +576,8 @@ TEST_F(VFABIParserTest, ParseMaskingAVX) {
   EXPECT_TRUE(invokeParser("_ZGVcM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AVX);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -502,7 +590,8 @@ TEST_F(VFABIParserTest, ParseMaskingAVX2) {
   EXPECT_TRUE(invokeParser("_ZGVdM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AVX2);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -515,7 +604,8 @@ TEST_F(VFABIParserTest, ParseMaskingAVX512) {
   EXPECT_TRUE(invokeParser("_ZGVeM2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::AVX512);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -528,7 +618,8 @@ TEST_F(VFABIParserTest, ParseMaskingLLVM) {
   EXPECT_TRUE(invokeParser("_ZGV_LLVM_M2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::LLVM);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -546,7 +637,8 @@ TEST_F(VFABIParserTest, LLVM_InternalISA) {
   EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)", "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::LLVM);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
   EXPECT_EQ(ScalarName, "foo");
@@ -558,7 +650,8 @@ TEST_F(VFABIParserTest, IntrinsicsInLLVMIsa) {
                            "void(float, float)"));
   EXPECT_EQ(ISA, VFISAKind::LLVM);
   EXPECT_FALSE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(4));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -573,7 +666,8 @@ TEST_F(VFABIParserTest, ParseScalableRequiresDeclaration) {
   EXPECT_TRUE(invokeParser(MangledName, "void(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));
@@ -592,7 +686,8 @@ TEST_F(VFABIParserTest, ParseScalableMaskingSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "i32(i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getScalable(4));
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -606,7 +701,8 @@ TEST_F(VFABIParserTest, ParseScalableMaskingSVESincos) {
                            "void(double, ptr, ptr)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getScalable(2));
   EXPECT_EQ(Parameters.size(), (unsigned)4);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
@@ -623,7 +719,8 @@ TEST_F(VFABIParserTest, ParseWiderReturnTypeSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsMxvv_foo(vector_foo)", "i64(i32, i32)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)3);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector}));
@@ -638,7 +735,8 @@ TEST_F(VFABIParserTest, ParseVoidReturnTypeSVE) {
   EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "void(i16)"));
   EXPECT_EQ(ISA, VFISAKind::SVE);
   EXPECT_TRUE(isMasked());
-  EXPECT_TRUE(matchParametersNum()) << "Different number of scalar parameters";
+  EXPECT_TRUE(matchParametersNum());
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)2);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));
@@ -656,6 +754,7 @@ TEST_F(VFABIParserTest, ParseUnsupportedElementTypeSVE) {
 TEST_F(VFABIParserTest, ParseUnsupportedReturnTypeSVE) {
   EXPECT_FALSE(invokeParser("_ZGVsMxv_foo(vector_foo)", "fp128(float)"));
 }
+
 class VFABIAttrTest : public testing::Test {
 protected:
   void SetUp() override {

>From c95fe95111af988881865a327fd5641d9ca6239d Mon Sep 17 00:00:00 2001
From: Paschalis Mpeis <Paschalis.Mpeis at arm.com>
Date: Mon, 11 Dec 2023 18:02:37 +0000
Subject: [PATCH 2/3] Handle already masked Instructions.

'createFunctionType' should be able to create the correct FunctionType,
regardless of whether the input Instruction was masked or not.
It uses VFInfo to figure out if the input Instruction was already
masked, and if so it does not append another mask Type.

In checks, create two mock CallInsts, one with a mask and one without
and verify that they have the same number of parameters.
---
 llvm/lib/Analysis/VectorUtils.cpp             | 10 ++++---
 .../Analysis/VectorFunctionABITest.cpp        | 27 +++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index c31f0f3bd2fd58..f4dc26da812250 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -1507,7 +1507,7 @@ VFABI::createFunctionType(const VFInfo &Info, const Instruction *I,
     VecParams.push_back(U->getType());
   }
 
-  // Append a mask and get its position.
+  // Get mask's position mask and append one if not present in the Instruction.
   int MaskPos = -1;
   if (Info.isMasked()) {
     auto OptMaskPos = Info.getParamIndexForOptionalMask();
@@ -1515,8 +1515,12 @@ VFABI::createFunctionType(const VFInfo &Info, const Instruction *I,
       return std::nullopt;
 
     MaskPos = OptMaskPos.value();
-    VectorType *MaskTy = VectorType::get(Type::getInt1Ty(M->getContext()), VF);
-    VecParams.insert(VecParams.begin() + MaskPos, MaskTy);
+    // append a mask only when it's missing
+    if (VecParams.size() == Info.Shape.Parameters.size() - 1) {
+      VectorType *MaskTy =
+          VectorType::get(Type::getInt1Ty(M->getContext()), VF);
+      VecParams.insert(VecParams.begin() + MaskPos, MaskTy);
+    }
   }
   FunctionType *VecFTy = FunctionType::get(I->getType(), VecParams, false);
   return std::make_pair(VecFTy, MaskPos);
diff --git a/llvm/unittests/Analysis/VectorFunctionABITest.cpp b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
index 85177db74ef585..8daebb6042156d 100644
--- a/llvm/unittests/Analysis/VectorFunctionABITest.cpp
+++ b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
@@ -131,6 +131,33 @@ class VFABIParserTest : public ::testing::Test {
     if (!OptVecFTyPos)
       return false;
 
+    // Ensure that masked Instructions are handled
+    if (isMasked()) {
+      // In case of a masked call, try creating another mock CallInst that is
+      // masked. createFunctionType should be able to handle this.
+      SmallVector<Type *, 8> CallTypesInclMask(CallTypes);
+      SmallVector<Value *, 8> ArgsInclMask(Args);
+      Type *MaskTy = VectorType::get(Type::getInt1Ty(M->getContext()), VF);
+      CallTypesInclMask.push_back(MaskTy);
+      ArgsInclMask.push_back(Constant::getNullValue(MaskTy));
+
+      FunctionCallee FMasked = M->getOrInsertFunction(
+          VectorName + "_Masked",
+          FunctionType::get(RetTy, CallTypesInclMask, false));
+      std::unique_ptr<CallInst> CIMasked(
+          CallInst::Create(FMasked, ArgsInclMask));
+      auto OptVecFTyMaskedPos =
+          VFABI::createFunctionType(Info, CIMasked.get(), M.get());
+      if (!OptVecFTyMaskedPos)
+        return false;
+
+      // Both FunctionTypes should have the same number of parameters.
+      assert(
+          (OptVecFTyPos->first->getNumParams() ==
+           OptVecFTyMaskedPos->first->getNumParams()) &&
+          "createFunctionType should accept masked or non masked Instructions");
+    }
+
     FunctionType *VecFTy = OptVecFTyPos->first;
     // Check that vectorized parameters' size match with VFInfo.
     // Both may include a mask.

>From c07a3dabe1e63b53498b84da6d0f04a522d79d86 Mon Sep 17 00:00:00 2001
From: Paschalis Mpeis <Paschalis.Mpeis at arm.com>
Date: Tue, 12 Dec 2023 17:33:18 +0000
Subject: [PATCH 3/3] Simplified createFunctionType method

It accepts ScalarFTy and VecRetTy. As the latter is not kept in VFABI,
it should instead come from the original Instruction/Callinst.

This change allows further simplification in tests. Also, methods like
the test `_ZGVnN3lLRUlnLnRnUn_foo`, which contains only scalar parameters,
would still return a valid FunctionType. This is an edge case that will
only be encountered in tests though.
---
 llvm/include/llvm/Analysis/VectorUtils.h      |  7 ++-
 llvm/lib/Analysis/VectorUtils.cpp             | 43 +++++----------
 .../Analysis/VectorFunctionABITest.cpp        | 53 +++++--------------
 3 files changed, 28 insertions(+), 75 deletions(-)

diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h
index 734c440283b4a0..6b3a001f7c6070 100644
--- a/llvm/include/llvm/Analysis/VectorUtils.h
+++ b/llvm/include/llvm/Analysis/VectorUtils.h
@@ -197,11 +197,10 @@ void getVectorVariantNames(const CallInst &CI,
                            SmallVectorImpl<std::string> &VariantMappings);
 
 /// Returns a pair of the vectorized FunctionType and the mask's position when
-/// there's one, otherwise -1. It rejects any non vectorized calls as this
-/// method should be called at a point where the Instruction \p I is already
-/// vectorized.
+/// there's one, otherwise -1.
 std::optional<std::pair<FunctionType *, int>>
-createFunctionType(const VFInfo &Info, const Instruction *I, const Module *M);
+createFunctionType(const VFInfo &Info, const FunctionType *ScalarFTy,
+                   Type *VecRetTy, const Module *M);
 } // end namespace VFABI
 
 /// The Vector Function Database.
diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp
index f4dc26da812250..19529e0f012a30 100644
--- a/llvm/lib/Analysis/VectorUtils.cpp
+++ b/llvm/lib/Analysis/VectorUtils.cpp
@@ -1479,32 +1479,17 @@ void VFABI::getVectorVariantNames(
   }
 }
 
-// Returns whether any of the operands or return type of \p I are vectors.
-static bool isVectorized(const Instruction *I) {
-  if (I->getType()->isVectorTy())
-    return true;
-  for (auto &U : I->operands())
-    if (U->getType()->isVectorTy())
-      return true;
-  return false;
-}
-
 std::optional<std::pair<FunctionType *, int>>
-VFABI::createFunctionType(const VFInfo &Info, const Instruction *I,
-                          const Module *M) {
-  // only vectorized calls should reach this method
-  if (!isVectorized(I))
-    return std::nullopt;
-
+VFABI::createFunctionType(const VFInfo &Info, const FunctionType *ScalarFTy,
+                          Type *VecRetTy, const Module *M) {
   ElementCount VF = Info.Shape.VF;
-  // get vectorized operands
-  const bool IsCall = isa<CallBase>(I);
-  SmallVector<Type *, 8> VecParams;
-  for (auto [i, U] : enumerate(I->operands())) {
-    // ignore the function pointer when the Instruction is a call
-    if (IsCall && i == I->getNumOperands() - 1)
-      break;
-    VecParams.push_back(U->getType());
+  // Create vector parameter types
+  SmallVector<Type *, 8> VecTypes;
+  for (auto [STy, VFParam] : zip(ScalarFTy->params(), Info.Shape.Parameters)) {
+    if (VFParam.ParamKind == VFParamKind::Vector)
+      VecTypes.push_back(VectorType::get(STy, VF));
+    else
+      VecTypes.push_back(STy);
   }
 
   // Get mask's position mask and append one if not present in the Instruction.
@@ -1515,14 +1500,10 @@ VFABI::createFunctionType(const VFInfo &Info, const Instruction *I,
       return std::nullopt;
 
     MaskPos = OptMaskPos.value();
-    // append a mask only when it's missing
-    if (VecParams.size() == Info.Shape.Parameters.size() - 1) {
-      VectorType *MaskTy =
-          VectorType::get(Type::getInt1Ty(M->getContext()), VF);
-      VecParams.insert(VecParams.begin() + MaskPos, MaskTy);
-    }
+    VectorType *MaskTy = VectorType::get(Type::getInt1Ty(M->getContext()), VF);
+    VecTypes.insert(VecTypes.begin() + MaskPos, MaskTy);
   }
-  FunctionType *VecFTy = FunctionType::get(I->getType(), VecParams, false);
+  FunctionType *VecFTy = FunctionType::get(VecRetTy, VecTypes, false);
   return std::make_pair(VecFTy, MaskPos);
 }
 
diff --git a/llvm/unittests/Analysis/VectorFunctionABITest.cpp b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
index 8daebb6042156d..e02a3b1c03c1f5 100644
--- a/llvm/unittests/Analysis/VectorFunctionABITest.cpp
+++ b/llvm/unittests/Analysis/VectorFunctionABITest.cpp
@@ -98,10 +98,9 @@ class VFABIParserTest : public ::testing::Test {
   /// number and type of arguments with both the ScalarFTy and the operands of
   /// the call.
   bool checkFunctionType() {
-    // Create a mock vectorized CallInst using dummy values and then use it to
-    // create a vector FunctionType. In the case of scalable ISAs, the created
-    // vector FunctionType might have a mask parameter Type, however, this input
-    // CallInst will not have a mask operand.
+    // For scalable ISAs, the created vector FunctionType might have a mask
+    // parameter Type, according to VFABI. Regardless, this input CallInst,
+    // despite being a vectorized call, it will not have a masked operand.
     SmallVector<Value *, 8> Args;
     SmallVector<Type *, 8> CallTypes;
     for (auto [VFParam, STy] :
@@ -117,47 +116,21 @@ class VFABIParserTest : public ::testing::Test {
 
     // Mangled names do not currently encode return Type information. Generally,
     // return types are vectors, so use one.
-    Type *RetTy = ScalarFTy->getReturnType();
-    if (!RetTy->isVoidTy())
-      RetTy = VectorType::get(RetTy, Info.Shape.VF);
+    Type *VecRetTy = ScalarFTy->getReturnType();
+    if (!VecRetTy->isVoidTy())
+      VecRetTy = VectorType::get(VecRetTy, Info.Shape.VF);
 
     FunctionCallee F = M->getOrInsertFunction(
-        VectorName, FunctionType::get(RetTy, CallTypes, false));
+        VectorName, FunctionType::get(VecRetTy, CallTypes, false));
     std::unique_ptr<CallInst> CI(CallInst::Create(F, Args));
 
     // Use VFInfo and the mock CallInst to create a FunctionType that will
-    // include a mask where relevant.
-    auto OptVecFTyPos = VFABI::createFunctionType(Info, CI.get(), M.get());
+    // include a mask when relevant.
+    auto OptVecFTyPos =
+        VFABI::createFunctionType(Info, ScalarFTy, VecRetTy, M.get());
     if (!OptVecFTyPos)
       return false;
 
-    // Ensure that masked Instructions are handled
-    if (isMasked()) {
-      // In case of a masked call, try creating another mock CallInst that is
-      // masked. createFunctionType should be able to handle this.
-      SmallVector<Type *, 8> CallTypesInclMask(CallTypes);
-      SmallVector<Value *, 8> ArgsInclMask(Args);
-      Type *MaskTy = VectorType::get(Type::getInt1Ty(M->getContext()), VF);
-      CallTypesInclMask.push_back(MaskTy);
-      ArgsInclMask.push_back(Constant::getNullValue(MaskTy));
-
-      FunctionCallee FMasked = M->getOrInsertFunction(
-          VectorName + "_Masked",
-          FunctionType::get(RetTy, CallTypesInclMask, false));
-      std::unique_ptr<CallInst> CIMasked(
-          CallInst::Create(FMasked, ArgsInclMask));
-      auto OptVecFTyMaskedPos =
-          VFABI::createFunctionType(Info, CIMasked.get(), M.get());
-      if (!OptVecFTyMaskedPos)
-        return false;
-
-      // Both FunctionTypes should have the same number of parameters.
-      assert(
-          (OptVecFTyPos->first->getNumParams() ==
-           OptVecFTyMaskedPos->first->getNumParams()) &&
-          "createFunctionType should accept masked or non masked Instructions");
-    }
-
     FunctionType *VecFTy = OptVecFTyPos->first;
     // Check that vectorized parameters' size match with VFInfo.
     // Both may include a mask.
@@ -324,7 +297,7 @@ TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) {
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
   EXPECT_TRUE(matchParametersNum());
-  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)4);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, -1}));
@@ -389,7 +362,7 @@ TEST_F(VFABIParserTest, LinearWithoutCompileTime) {
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
   EXPECT_TRUE(matchParametersNum());
-  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(Parameters.size(), (unsigned)8);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, 1}));
   EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, 1}));
@@ -452,7 +425,7 @@ TEST_F(VFABIParserTest, ParseUniform) {
   EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
   EXPECT_FALSE(isMasked());
   EXPECT_TRUE(matchParametersNum());
-  EXPECT_FALSE(checkFunctionType()); // invalid: all operands are scalar
+  EXPECT_TRUE(checkFunctionType());
   EXPECT_EQ(VF, ElementCount::getFixed(2));
   EXPECT_EQ(Parameters.size(), (unsigned)1);
   EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0}));



More information about the llvm-commits mailing list