diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h --- a/llvm/include/llvm/Analysis/VectorUtils.h +++ b/llvm/include/llvm/Analysis/VectorUtils.h @@ -161,7 +161,12 @@ /// /// \param MangledName -> input string in the format /// _ZGV_[()]. -Optional tryDemangleForVFABI(StringRef MangledName); +/// \param M -> Module used to retrive informations about the vector +/// function that are not possible to retrieve from the mangled +/// name. At the moment, this parameter is needed only to retrive the +/// Vectorization Factor of scalable vector functions from their +/// respective IR declarations. +Optional tryDemangleForVFABI(StringRef MangledName, const Module &M); /// Retrieve the `VFParamKind` from a string token. VFParamKind getVFParamKindFromString(const StringRef Token); @@ -200,7 +205,8 @@ SmallVector ListOfStrings; VFABI::getVectorVariantNames(CI, ListOfStrings); for (const auto &MangledName : ListOfStrings) { - const Optional Shape = VFABI::tryDemangleForVFABI(MangledName); + const Optional Shape = + VFABI::tryDemangleForVFABI(MangledName, *(CI.getModule())); // A match is found via scalar and vector names, and also by // ensuring that the variant described in the attribute has a // corresponding definition or declaration of the vector diff --git a/llvm/lib/Analysis/VFABIDemangling.cpp b/llvm/lib/Analysis/VFABIDemangling.cpp --- a/llvm/lib/Analysis/VFABIDemangling.cpp +++ b/llvm/lib/Analysis/VFABIDemangling.cpp @@ -70,6 +70,9 @@ /// ParseRet tryParseVLEN(StringRef &ParseString, unsigned &VF, bool &IsScalable) { if (ParseString.consume_front("x")) { + // Set VF to 0, to be later adjusted to a value grater than zero + // by looking at the signature of the vector function with + // `getECFromSignature`. VF = 0; IsScalable = true; return ParseRet::OK; @@ -78,6 +81,10 @@ if (ParseString.consumeInteger(10, VF)) return ParseRet::Error; + // The token `0` is invalid for VLEN. + if (VF == 0) + return ParseRet::Error; + IsScalable = false; return ParseRet::OK; } @@ -287,11 +294,50 @@ return ParseRet::None; } +#ifndef NDEBUG +// Verify the assumtion that all vectors in the signature of a vector +// function have the same number of elements. +bool verifyAllVectorsHaveSameWidth(FunctionType *Signature) { + SmallVector VecTys; + if (auto *RetTy = dyn_cast(Signature->getReturnType())) + VecTys.push_back(RetTy); + for (auto *Ty : Signature->params()) + if (auto *VTy = dyn_cast(Ty)) + VecTys.push_back(VTy); + + if (VecTys.size() <= 1) + return true; + + assert(VecTys.size() > 1 && "Invalid number of elements."); + const ElementCount EC = VecTys[0]->getElementCount(); + return llvm::all_of( + llvm::make_range(VecTys.begin() + 1, VecTys.end()), + [&EC](VectorType *VTy) { return (EC == VTy->getElementCount()); }); +} + +#endif // NDEBUG + +// Extract the VectorizationFactor from a given function signature, +// under the assumtion that all vectors have the same number of +// elements, i.e. same ElementCount.Min. +ElementCount getECFromSignature(FunctionType *Signature) { + assert(verifyAllVectorsHaveSameWidth(Signature) && + "Invalid vector signature."); + + if (auto *RetTy = dyn_cast(Signature->getReturnType())) + return RetTy->getElementCount(); + for (auto *Ty : Signature->params()) + if (auto *VTy = dyn_cast(Ty)) + return VTy->getElementCount(); + + return ElementCount(/*Min=*/1, /*Scalable=*/false); +} } // namespace // Format of the ABI name: // _ZGV_[()] -Optional VFABI::tryDemangleForVFABI(StringRef MangledName) { +Optional VFABI::tryDemangleForVFABI(StringRef MangledName, + const Module &M) { const StringRef OriginalName = MangledName; // Assume there is no custom name , and therefore the // vector name consists of @@ -402,6 +448,32 @@ assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate && "The global predicate must be the last parameter"); + // Adjust the VF for scalable signatures. The EC.Min is not encoded + // in the name of the function, but it is encoded in the IR + // signature of the function. We need to extract this information + // because it is needed by the loop vectorizer, which reasons in + // terms of VectorizationFactor or ElementCount. In particular, we + // need to make sure that the VF field of the VFShape class is never + // set to 0. + if (IsScalable) { + const Function *F = M.getFunction(VectorName); + // The declaration of the function must be present in the module + // to be able to retrieve its signature. + if (!F) + return None; + const ElementCount EC = getECFromSignature(F->getFunctionType()); + VF = EC.Min; + } + + // Sanity checks. + // 1. We don't accept a zero lanes vectorization factor. + // 2. We don't accept the demangling if the vector function is not + // present in the module. + if (VF == 0) + return None; + if (!M.getFunction(VectorName)) + return None; + const VFShape Shape({VF, IsScalable, Parameters}); return VFInfo({Shape, std::string(ScalarName), std::string(VectorName), ISA}); } diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp --- a/llvm/lib/Analysis/VectorUtils.cpp +++ b/llvm/lib/Analysis/VectorUtils.cpp @@ -1175,7 +1175,7 @@ for (auto &S : SetVector(ListAttr.begin(), ListAttr.end())) { #ifndef NDEBUG LLVM_DEBUG(dbgs() << "VFABI: adding mapping '" << S << "'\n"); - Optional Info = VFABI::tryDemangleForVFABI(S); + Optional Info = VFABI::tryDemangleForVFABI(S, *(CI.getModule())); assert(Info.hasValue() && "Invalid name for a VFABI variant."); assert(CI.getModule()->getFunction(Info.getValue().VectorName) && "Vector function is missing."); diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp --- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -301,7 +301,7 @@ #ifndef NDEBUG for (const std::string &VariantMapping : VariantMappings) { LLVM_DEBUG(dbgs() << "VFABI: adding mapping '" << VariantMapping << "'\n"); - Optional VI = VFABI::tryDemangleForVFABI(VariantMapping); + Optional VI = VFABI::tryDemangleForVFABI(VariantMapping, *M); assert(VI.hasValue() && "Cannot add an invalid VFABI name."); assert(M->getNamedValue(VI.getValue().VectorName) && "Cannot add variant to attribute: " diff --git a/llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt b/llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt --- a/llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt +++ b/llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt @@ -1,5 +1,7 @@ set(LLVM_LINK_COMPONENTS Analysis + AsmParser + Core Support ) add_llvm_fuzzer(vfabi-demangler-fuzzer diff --git a/llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp b/llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp --- a/llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp +++ b/llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp @@ -11,12 +11,27 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/VectorUtils.h" +#include "llvm/AsmParser/Parser.h" using namespace llvm; extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + LLVMContext Ctx; + SMDiagnostic Err; + const std::unique_ptr M = + parseAssemblyString("declare i32 @foo(i32 )\n", Err, Ctx); const StringRef MangledName((const char *)Data, Size); - const auto Info = VFABI::tryDemangleForVFABI(MangledName); + // Make sure that whatever symbol the demangler is operating on is + // present in the module (the signature is not important). This is + // because `tryDemangleForVFABI` fails if the function is not + // present. We need to make sure we can even invoke + // `getOrInsertFunction` because such method asserts on strings with + // zeroes. + if (!MangledName.empty() && MangledName.find_first_of(0) == StringRef::npos) + M->getOrInsertFunction( + MangledName, + FunctionType::get(Type::getVoidTy(M->getContext()), false)); + const auto Info = VFABI::tryDemangleForVFABI(MangledName, *M); // Do not optimize away the return value. Inspired by // https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307-L345 diff --git a/llvm/unittests/Analysis/VectorFunctionABITest.cpp b/llvm/unittests/Analysis/VectorFunctionABITest.cpp --- a/llvm/unittests/Analysis/VectorFunctionABITest.cpp +++ b/llvm/unittests/Analysis/VectorFunctionABITest.cpp @@ -13,79 +13,34 @@ using namespace llvm; -// This test makes sure that the demangling method succeeds only on -// valid values of the string. -TEST(VectorFunctionABITests, OnlyValidNames) { - // Incomplete string. - EXPECT_FALSE(VFABI::tryDemangleForVFABI("").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGV").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVn").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2v").hasValue()); - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2v_").hasValue()); - // Missing parameters. - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2_foo").hasValue()); - // Missing _ZGV prefix. - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZVnN2v_foo").hasValue()); - // Missing . - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVN2v_foo").hasValue()); - // Missing . - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVn2v_foo").hasValue()); - // Missing . - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnNv_foo").hasValue()); - // Missing . - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2v_").hasValue()); - // Missing _ separator. - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2vfoo").hasValue()); - // Missing . - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2v_foo()").hasValue()); - // Unterminated name. - EXPECT_FALSE(VFABI::tryDemangleForVFABI("_ZGVnN2v_foo(bar").hasValue()); -} - -TEST(VectorFunctionABITests, ParamListParsing) { - // Testing "vl16Ls32R3l" - const auto OptVFS = VFABI::tryDemangleForVFABI("_ZGVnN2vl16Ls32R3l_foo"); - EXPECT_TRUE(OptVFS.hasValue()); - const VFInfo VFS = OptVFS.getValue(); - EXPECT_EQ(VFS.Shape.Parameters.size(), (unsigned)5); - EXPECT_EQ(VFS.Shape.Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); - EXPECT_EQ(VFS.Shape.Parameters[1], - VFParameter({1, VFParamKind::OMP_Linear, 16})); - EXPECT_EQ(VFS.Shape.Parameters[2], - VFParameter({2, VFParamKind::OMP_LinearValPos, 32})); - EXPECT_EQ(VFS.Shape.Parameters[3], - VFParameter({3, VFParamKind::OMP_LinearRef, 3})); - EXPECT_EQ(VFS.Shape.Parameters[4], - VFParameter({4, VFParamKind::OMP_Linear, 1})); -} - -TEST(VectorFunctionABITests, ScalarNameAndVectorName) { - // Parse Scalar Name - const auto A = VFABI::tryDemangleForVFABI("_ZGVnM2v_sin"); - const auto B = VFABI::tryDemangleForVFABI("_ZGVnM2v_sin(UserFunc)"); - const auto C = VFABI::tryDemangleForVFABI("_ZGVnM2v___sin_sin_sin"); - EXPECT_TRUE(A.hasValue()); - EXPECT_TRUE(B.hasValue()); - EXPECT_TRUE(C.hasValue()); - EXPECT_EQ(A.getValue().ScalarName, "sin"); - EXPECT_EQ(B.getValue().ScalarName, "sin"); - EXPECT_EQ(C.getValue().ScalarName, "__sin_sin_sin"); - EXPECT_EQ(A.getValue().VectorName, "_ZGVnM2v_sin"); - EXPECT_EQ(B.getValue().VectorName, "UserFunc"); - EXPECT_EQ(C.getValue().VectorName, "_ZGVnM2v___sin_sin_sin"); -} - namespace { // Test fixture needed that holds the veariables needed by the parser. class VFABIParserTest : public ::testing::Test { private: // Parser output. VFInfo Info; - // Reset the parser output references. - void reset() { Info = VFInfo(); } + // Reset the data needed for the test. + void reset(const StringRef Name, const StringRef IRType) { + M = parseAssemblyString("declare void @dummy()", Err, Ctx); + EXPECT_NE(M.get(), nullptr) << "Loading an invalid module.\n " + << Err.getMessage() << "\n"; + Type *Ty = parseType(IRType, Err, *(M.get())); + FunctionType *FTy = dyn_cast(Ty); + EXPECT_NE(FTy, nullptr) << "Invalid function type string: " << IRType + << "\n" + << Err.getMessage() << "\n"; + FunctionCallee F = M->getOrInsertFunction(Name, FTy); + EXPECT_NE(F.getCallee(), nullptr) + << "The function must be present in the module\n"; + // Reset the VFInfo + Info = VFInfo(); + } + // Data needed to load the optional IR passed to invokeParser + LLVMContext Ctx; + SMDiagnostic Err; + std::unique_ptr M; + // CallInst *CI; protected: // Referencies to the parser output field. unsigned &VF = Info.Shape.VF; @@ -94,10 +49,33 @@ std::string &ScalarName = Info.ScalarName; std::string &VectorName = Info.VectorName; bool &IsScalable = Info.Shape.IsScalable; - // Invoke the parser. - bool invokeParser(const StringRef MangledName) { - reset(); - const auto OptInfo = VFABI::tryDemangleForVFABI(MangledName); + // Invoke the parser. We need to make sure that a function exist in + // the module because the parser fails if such function don't + // exists. Every time this method is invoked the state of the test + // is reset. + // + // \p MangledName -> the string the parser has to demangle. + // + // \p VectorName -> optional vector name that the method needs to + // use to create the function in the module if it differs from the + // standard mangled name. + // + // \p IRType -> FunctionType string to be used for the signature of + // the vector function. The correct signature is needed by the + // parser only for scalable functions. For the sake of testing, the + // generic fixed-length case can use as signature `void()`. + // + bool invokeParser(const StringRef MangledName, + const StringRef VectorName = "", + const StringRef IRType = "void()") { + StringRef Name = MangledName; + if (!VectorName.empty()) + Name = VectorName; + // Reset the VFInfo and the Module to be able to invoke + // `invokeParser` multiple times in the same test. + reset(Name, IRType); + + const auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, *(M.get())); if (OptInfo.hasValue()) { Info = OptInfo.getValue(); return true; @@ -120,6 +98,67 @@ }; } // unnamed namespace +// This test makes sure that the demangling method succeeds only on +// valid values of the string. +TEST_F(VFABIParserTest, OnlyValidNames) { + // Incomplete string. + EXPECT_FALSE(invokeParser("")); + EXPECT_FALSE(invokeParser("_ZGV")); + EXPECT_FALSE(invokeParser("_ZGVn")); + EXPECT_FALSE(invokeParser("_ZGVnN")); + EXPECT_FALSE(invokeParser("_ZGVnN2")); + EXPECT_FALSE(invokeParser("_ZGVnN2v")); + EXPECT_FALSE(invokeParser("_ZGVnN2v_")); + // Missing parameters. + EXPECT_FALSE(invokeParser("_ZGVnN2_foo")); + // Missing _ZGV prefix. + EXPECT_FALSE(invokeParser("_ZVnN2v_foo")); + // Missing . + EXPECT_FALSE(invokeParser("_ZGVN2v_foo")); + // Missing . + EXPECT_FALSE(invokeParser("_ZGVn2v_foo")); + // Missing . + EXPECT_FALSE(invokeParser("_ZGVnNv_foo")); + // Missing . + EXPECT_FALSE(invokeParser("_ZGVnN2v_")); + // Missing _ separator. + EXPECT_FALSE(invokeParser("_ZGVnN2vfoo")); + // Missing . Using `fakename` because the string being + // parsed is not a valid function name that `invokeParser` can add. + EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()", "fakename")); + // Unterminated name. Using `fakename` because the string being + // parsed is not a valid function name that `invokeParser` can add. + EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar", "fakename")); +} + +TEST_F(VFABIParserTest, ParamListParsing) { + EXPECT_TRUE(invokeParser("_ZGVnN2vl16Ls32R3l_foo")); + 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})); + EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 32})); + EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, 3})); + EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, 1})); +} + +TEST_F(VFABIParserTest, ScalarNameAndVectorName_01) { + EXPECT_TRUE(invokeParser("_ZGVnM2v_sin")); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVnM2v_sin"); +} + +TEST_F(VFABIParserTest, ScalarNameAndVectorName_02) { + EXPECT_TRUE(invokeParser("_ZGVnM2v_sin(UserFunc)", "UserFunc")); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "UserFunc"); +} + +TEST_F(VFABIParserTest, ScalarNameAndVectorName_03) { + EXPECT_TRUE(invokeParser("_ZGVnM2v___sin_sin_sin")); + EXPECT_EQ(ScalarName, "__sin_sin_sin"); + EXPECT_EQ(VectorName, "_ZGVnM2v___sin_sin_sin"); +} + TEST_F(VFABIParserTest, Parse) { EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin")); EXPECT_EQ(VF, (unsigned)2); @@ -141,7 +180,7 @@ } TEST_F(VFABIParserTest, ParseVectorName) { - EXPECT_TRUE(invokeParser("_ZGVnN2v_sin(my_v_sin)")); + EXPECT_TRUE(invokeParser("_ZGVnN2v_sin(my_v_sin)", "my_v_sin")); EXPECT_EQ(VF, (unsigned)2); EXPECT_FALSE(IsMasked()); EXPECT_FALSE(IsScalable); @@ -168,13 +207,15 @@ } TEST_F(VFABIParserTest, ParseScalableSVE) { - EXPECT_TRUE(invokeParser("_ZGVsMxv_sin")); - EXPECT_EQ(VF, (unsigned)0); + EXPECT_TRUE(invokeParser( + "_ZGVsMxv_sin(custom_vg)", "custom_vg", + "(, )")); + EXPECT_EQ(VF, (unsigned)2); EXPECT_TRUE(IsMasked()); EXPECT_TRUE(IsScalable); EXPECT_EQ(ISA, VFISAKind::SVE); EXPECT_EQ(ScalarName, "sin"); - EXPECT_EQ(VectorName, "_ZGVsMxv_sin"); + EXPECT_EQ(VectorName, "custom_vg"); } TEST_F(VFABIParserTest, ParseFixedWidthSVE) { @@ -245,7 +286,7 @@ TEST_F(VFABIParserTest, LLVM_ISA) { EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_sin")); - EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)")); + EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)", "vector_name")); EXPECT_EQ(ISA, VFISAKind::LLVM); } @@ -353,8 +394,8 @@ EXPECT_EQ(VectorName, "_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); // LLVM: = "_LLVM_" internal vector function. - EXPECT_TRUE( - invokeParser("_ZGV_LLVM_N2vls2Ls27Us4Rs5l1L10U100R1000u2_sin(vectorf)")); + EXPECT_TRUE(invokeParser( + "_ZGV_LLVM_N2vls2Ls27Us4Rs5l1L10U100R1000u2_sin(vectorf)", "vectorf")); EXPECT_EQ(ISA, VFISAKind::LLVM); __COMMON_CHECKS; EXPECT_EQ(VectorName, "vectorf"); @@ -454,7 +495,8 @@ } TEST_F(VFABIParserTest, ParseMaskingLLVM) { - EXPECT_TRUE(invokeParser("_ZGV_LLVM_M2v_sin(custom_vector_sin)")); + EXPECT_TRUE(invokeParser("_ZGV_LLVM_M2v_sin(custom_vector_sin)", + "custom_vector_sin")); EXPECT_EQ(VF, (unsigned)2); EXPECT_TRUE(IsMasked()); EXPECT_FALSE(IsScalable); @@ -467,8 +509,11 @@ } TEST_F(VFABIParserTest, ParseScalableMaskingLLVM) { - EXPECT_TRUE(invokeParser("_ZGV_LLVM_Mxv_sin(custom_vector_sin)")); + EXPECT_TRUE(invokeParser( + "_ZGV_LLVM_Mxv_sin(custom_vector_sin)", "custom_vector_sin", + " (, )")); EXPECT_TRUE(IsMasked()); + EXPECT_EQ(VF, (unsigned)2); EXPECT_TRUE(IsScalable); EXPECT_EQ(ISA, VFISAKind::LLVM); EXPECT_EQ(Parameters.size(), (unsigned)2); @@ -479,7 +524,10 @@ } TEST_F(VFABIParserTest, ParseScalableMaskingLLVMSincos) { - EXPECT_TRUE(invokeParser("_ZGV_LLVM_Mxvl8l8_sincos(custom_vector_sincos)")); + EXPECT_TRUE(invokeParser("_ZGV_LLVM_Mxvl8l8_sincos(custom_vector_sincos)", + "custom_vector_sincos", + "void(, double *, double *)")); + EXPECT_EQ(VF, (unsigned)2); EXPECT_TRUE(IsMasked()); EXPECT_TRUE(IsScalable); EXPECT_EQ(ISA, VFISAKind::LLVM); @@ -528,12 +576,13 @@ TEST_F(VFABIParserTest, LLVM_InternalISA) { EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_sin")); - EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)")); + EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_sin_(vector_name)", "vector_name")); EXPECT_EQ(ISA, VFISAKind::LLVM); } TEST_F(VFABIParserTest, IntrinsicsInLLVMIsa) { - EXPECT_TRUE(invokeParser("_ZGV_LLVM_N4vv_llvm.pow.f32(__svml_powf4)")); + EXPECT_TRUE(invokeParser("_ZGV_LLVM_N4vv_llvm.pow.f32(__svml_powf4)", + "__svml_powf4")); EXPECT_EQ(VF, (unsigned)4); EXPECT_FALSE(IsMasked()); EXPECT_FALSE(IsScalable); @@ -543,3 +592,20 @@ EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector})); EXPECT_EQ(ScalarName, "llvm.pow.f32"); } + +TEST_F(VFABIParserTest, ParseScalableRequiresDeclaration) { + const char *MangledName = "_ZGVsMxv_sin(custom_vg)"; + // The parser succeds only when the correct function definition of + // `custom_vg` is added to the module. + EXPECT_FALSE(invokeParser(MangledName)); + EXPECT_TRUE(invokeParser( + MangledName, "custom_vg", + "(, )")); +} + +TEST_F(VFABIParserTest, ZeroIsInvalidVLEN) { + EXPECT_FALSE(invokeParser("_ZGVeM0v_sin")); + EXPECT_FALSE(invokeParser("_ZGVeN0v_sin")); + EXPECT_FALSE(invokeParser("_ZGVsM0v_sin")); + EXPECT_FALSE(invokeParser("_ZGVsN0v_sin")); +}