Index: llvm/include/llvm/Analysis/VectorUtils.h =================================================================== --- llvm/include/llvm/Analysis/VectorUtils.h +++ llvm/include/llvm/Analysis/VectorUtils.h @@ -20,6 +20,144 @@ namespace llvm { +/// Describes the type of Parameters +enum class VFParamKind { + Vector, // No semantic information. + OMP_Linear, // declare simd linear(i) + OMP_LinearRef, // declare simd linear(ref(i)) + OMP_LinearVal, // declare simd linear(val(i)) + OMP_LinearUVal, // declare simd linear(uval(i)) + OMP_LinearPos, // declare simd linear(i:c) uniform(c) + OMP_LinearValPos, // declare simd linear(val(i:c)) uniform(c) + OMP_LinearRefPos, // declare simd linear(ref(i:c)) uniform(c) + OMP_LinearUValPos, // declare simd linear(uval(i:c)) uniform(c + OMP_Uniform, // declare simd uniform(i) + GlobalPredicate, // Global logical predicate that acts on all lanes + // of the input and output mask concurrently. For + // example, it is implied by the `M` token in the + // Vector Function ABI mangled name. + Unknown +}; + +/// Describes the type of Instruction Set Architecture +enum class VFISAKind { + AdvancedSIMD, // AArch64 Advanced SIMD (NEON) + SVE, // AArch64 Scalable Vector Extension + SSE, // x86 SSE + AVX, // x86 AVX + AVX2, // x86 AVX2 + AVX512, // x86 AVX512 + Unknown // Unknown ISA +}; + +/// Encapsulates information needed to describe a parameter. +/// +/// The description of the parameter is not linked directly to +/// OpenMP or any other vector function description. This structure +/// is extendible to handle other paradigms that describe vector +/// functions and their parameters. +struct VFParameter { + unsigned ParamPos; // Parameter Position in Scalar Function. + VFParamKind ParamKind; // Kind of Parameter. + int LinearStepOrPos = 0; // Step or Position of the Parameter. + Align Alignment = Align(); // Optional aligment in bytes, defaulted to 1. + + // Comparison operator. + bool operator==(const VFParameter &Other) const { + return std::tie(ParamPos, ParamKind, LinearStepOrPos, Alignment) == + std::tie(Other.ParamPos, Other.ParamKind, Other.LinearStepOrPos, + Other.Alignment); + } +}; + +namespace VFABI { +/// Convenience container for the data produced by the parser when +/// parsing +/// _ZGV_[()]. +struct ParsedData { + unsigned VF; // Holds if is a number. + bool IsMasked; // true if == "M", false if == "N". + bool IsScalable; // true if == "x". + VFISAKind ISA; // Holds with the internal enum. + SmallVector Parameters; // Holds the . + StringRef ScalarName; // Holds the . + StringRef VectorName; // Holds the optional if present, + // otherwise holds + // _ZGV_. +}; + +/// Parse a string in the format +/// _ZGV_[()]. +/// +/// \param MangledName -> input string. +/// \param PD -> ParsedData instance to store the results produced by the parse. +Optional tryDemangleForVFABI(StringRef MangledName); + +/// Retrieve the `VFParamKind` from a string token. +VFParamKind getVFParamKindFromString(const StringRef Token); +} // end namespace VFABI + +/// Contains the information about the kind of vectorization +/// available. +/// +/// This object in independent on the paradigm used to +/// represent vector functions. in particular, it is not attached to +/// any target-specific ABI. + +struct VFShape { + unsigned VF; // Vectorization factor. + bool IsScalable; // True if the function is a scalable function. + VFISAKind ISA; // Instruction Set Architecture. + SmallVector Parameters; // List of parameter informations. + // Comparison operator. + bool operator==(const VFShape &Other) const { + return std::tie(VF, IsScalable, ISA, Parameters) == + std::tie(Other.VF, Other.IsScalable, Other.ISA, Other.Parameters); + } +}; + +/// Holds the VFShape for a specific scalar to vector function mapping. +struct VFInfo { + VFShape Shape; // Classification of the vector function. + StringRef ScalarName; // Scalar Function Name. + StringRef VectorName; // Vector Function Name associated to this VFInfo. + + // Comparison operator. + bool operator==(const VFInfo &Other) const { + return std::tie(Shape, ScalarName, VectorName) == + std::tie(Shape, Other.ScalarName, Other.VectorName); + } + + /// Static method to contruct a VFInfo out of a mangled names in the + /// following format: + /// + /// {()} + /// + /// where is the name of the vector function, mangled according + /// to the rules described in the Vector Function ABI of the target vector + /// extentsion (or from now on). The is in the following + /// format: + /// + /// _ZGV_[()] + /// + /// This methods support demangling rules for the following : + /// + /// * AArch64: https://developer.arm.com/docs/101129/latest + /// + /// * x86 (libmvec): https://sourceware.org/glibc/wiki/libmvec and + /// https://sourceware.org/glibc/wiki/libmvec?action=AttachFile&do=view&target=VectorABI.txt + static Optional getFromVFABI(StringRef MangledName) { + const auto OptPD = VFABI::tryDemangleForVFABI(MangledName); + if (OptPD.hasValue()) { + const VFABI::ParsedData PD = OptPD.getValue(); + const VFShape Shape({PD.VF, PD.IsScalable, PD.ISA, PD.Parameters}); + return VFInfo({Shape, PD.ScalarName, PD.VectorName}); + } + + return None; + } +}; + template class ArrayRef; class DemandedBits; class GetElementPtrInst; Index: llvm/lib/Analysis/CMakeLists.txt =================================================================== --- llvm/lib/Analysis/CMakeLists.txt +++ llvm/lib/Analysis/CMakeLists.txt @@ -95,6 +95,7 @@ ValueLatticeUtils.cpp ValueTracking.cpp VectorUtils.cpp + VFABIDemangling.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Analysis Index: llvm/lib/Analysis/VFABIDemangling.cpp =================================================================== --- /dev/null +++ llvm/lib/Analysis/VFABIDemangling.cpp @@ -0,0 +1,413 @@ +//===- VFABIDemangling.cpp - Vector Function ABI demangling utilities. ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VectorUtils.h" + +using namespace llvm; + +namespace { +/// Utilities for the Vector Function ABI name parser. + +/// Return types for the parser functions. +enum class ParseRet { + OK, // Found. + None, // Not found. + Error // Syntax error. +}; + +/// Extracts the `` information from the mangled string, and +/// sets the `ISA` accordingly. +ParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) { + if (MangledName.empty()) + return ParseRet::Error; + + ISA = StringSwitch(MangledName.take_front(1)) + .Case("n", VFISAKind::AdvancedSIMD) + .Case("s", VFISAKind::SVE) + .Case("b", VFISAKind::SSE) + .Case("c", VFISAKind::AVX) + .Case("d", VFISAKind::AVX2) + .Case("e", VFISAKind::AVX512) + .Default(VFISAKind::Unknown); + + MangledName = MangledName.drop_front(1); + + return ParseRet::OK; +} + +/// Extracts the `` information from the mangled string, and +/// sets `IsMasked` accordingly. The input string `MangledName` is +/// left unmodified. +ParseRet tryParseMask(StringRef &MangledName, bool &IsMasked) { + if (MangledName.consume_front("M")) { + IsMasked = true; + return ParseRet::OK; + } + + if (MangledName.consume_front("N")) { + IsMasked = false; + return ParseRet::OK; + } + + return ParseRet::Error; +} + +/// Extract the `` information from the mangled string, and +/// sets `VF` accordingly. A ` == "x"` token is interpreted as a scalable +/// vector length. On success, the `` token is removed from +/// the input string `ParseString`. +/// +ParseRet tryParseVLEN(StringRef &ParseString, unsigned &VF, bool &IsScalable) { + if (ParseString.consume_front("x")) { + VF = 0; + IsScalable = true; + return ParseRet::OK; + } + + if (ParseString.consumeInteger(10, VF)) + return ParseRet::Error; + + IsScalable = false; + return ParseRet::OK; +} + +/// The function looks for the following strings at the beginning of +/// the input string `ParseString`: +/// +/// +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `Pos` to +/// , and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +/// +/// The function expects to be one of "ls", "Rs", "Us" or +/// "Ls". +ParseRet tryParseLinearTokenWithRuntimeStep(StringRef &ParseString, + VFParamKind &PKind, int &Pos, + const StringRef Token) { + if (ParseString.consume_front(Token)) { + PKind = VFABI::getVFParamKindFromString(Token); + if (ParseString.consumeInteger(10, Pos)) + return ParseRet::Error; + return ParseRet::OK; + } + + return ParseRet::None; +} + +/// The function looks for the following stringt at the beginning of +/// the input string `ParseString`: +/// +/// +/// +/// is one of "ls", "Rs", "Us" or "Ls". +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `StepOrPos` to +/// , and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +ParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString, + VFParamKind &PKind, int &StepOrPos) { + ParseRet Ret; + + // "ls" + Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "ls"); + if (Ret != ParseRet::None) + return Ret; + + // "Rs" + Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Rs"); + if (Ret != ParseRet::None) + return Ret; + + // "Ls" + Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Ls"); + if (Ret != ParseRet::None) + return Ret; + + // "Us" + Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Us"); + if (Ret != ParseRet::None) + return Ret; + + return ParseRet::None; +} + +/// The function looks for the following strings at the beginning of +/// the input string `ParseString`: +/// +/// {"n"} +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `LinearStep` to +/// , and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +/// +/// The function expects to be one of "l", "R", "U" or +/// "L". +ParseRet tryParseCompileTimeLinearToken(StringRef &ParseString, + VFParamKind &PKind, int &LinearStep, + const StringRef Token) { + if (ParseString.consume_front(Token)) { + PKind = VFABI::getVFParamKindFromString(Token); + const bool Negate = ParseString.consume_front("n"); + if (ParseString.consumeInteger(10, LinearStep)) + LinearStep = 1; + if (Negate) + LinearStep *= -1; + return ParseRet::OK; + } + + return ParseRet::None; +} + +/// The function looks for the following strings at the beginning of +/// the input string `ParseString`: +/// +/// ["l" | "R" | "U" | "L"] {"n"} +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `LinearStep` to +/// , and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +ParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString, + VFParamKind &PKind, int &StepOrPos) { + // "l" {"n"} + if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l") == + ParseRet::OK) + return ParseRet::OK; + + // "R" {"n"} + if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R") == + ParseRet::OK) + return ParseRet::OK; + + // "L" {"n"} + if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L") == + ParseRet::OK) + return ParseRet::OK; + + // "U" {"n"} + if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U") == + ParseRet::OK) + return ParseRet::OK; + + return ParseRet::None; +} + +/// The function looks for the following strings at the beginning of +/// the input string `ParseString`: +/// +/// "u" +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `Pos` to +/// , and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +ParseRet tryParseUniform(StringRef &ParseString, VFParamKind &PKind, int &Pos) { + // "u" + const char *UniformToken = "u"; + if (ParseString.consume_front(UniformToken)) { + PKind = VFABI::getVFParamKindFromString(UniformToken); + if (ParseString.consumeInteger(10, Pos)) + return ParseRet::Error; + + return ParseRet::OK; + } + return ParseRet::None; +} + +/// Looks into the part of the mangled name in search +/// for valid paramaters at the beginning of the string +/// `ParseString`. +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `StepOrPos` +/// accordingly, and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +ParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind, + int &StepOrPos) { + if (ParseString.consume_front("v")) { + PKind = VFParamKind::Vector; + StepOrPos = 0; + return ParseRet::OK; + } + + const ParseRet HasLinearRuntime = + tryParseLinearWithRuntimeStep(ParseString, PKind, StepOrPos); + if (HasLinearRuntime != ParseRet::None) + return HasLinearRuntime; + + const ParseRet HasLinearCompileTime = + tryParseLinearWithCompileTimeStep(ParseString, PKind, StepOrPos); + if (HasLinearCompileTime != ParseRet::None) + return HasLinearCompileTime; + + const ParseRet HasUniform = tryParseUniform(ParseString, PKind, StepOrPos); + if (HasUniform != ParseRet::None) + return HasUniform; + + return ParseRet::None; +} + +/// Looks into the part of the mangled name in search +/// of a valid 'aligned' clause. The function should be invoked +/// after parsing a parameter via `tryParseParameter`. +/// +/// On success, it removes the parsed parameter from `ParseString`, +/// sets `PKind` to the correspondent enum value, sets `StepOrPos` +/// accordingly, and return success. On a syntax error, it return a +/// parsing error. If nothing is parsed, it returns None. +ParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) { + uint64_t Val; + // "a" + if (ParseString.consume_front("a")) { + if (ParseString.consumeInteger(10, Val)) + return ParseRet::Error; + + if (!isPowerOf2_64(Val)) + return ParseRet::Error; + + Alignment = Align(Val); + + return ParseRet::OK; + } + + return ParseRet::None; +} +} // namespace + +// Format of the ABI name: +// _ZGV_[()] +Optional VFABI::tryDemangleForVFABI(StringRef MangledName) { + ParsedData PD; + + // Assume there is no custom name , and therefore the + // vector name consists of + // _ZGV_. + PD.VectorName = MangledName; + + // Parse the fixed size part of the manled name + if (!MangledName.consume_front("_ZGV")) + return None; + + // Extract ISA. An unknow ISA is also supported, so we accept all + // values. + if (tryParseISA(MangledName, PD.ISA) != ParseRet::OK) + return None; + + // Extract . + if (tryParseMask(MangledName, PD.IsMasked) != ParseRet::OK) + return None; + + // Parse the variable size, starting from . + if (tryParseVLEN(MangledName, PD.VF, PD.IsScalable) != ParseRet::OK) + return None; + + // Parse the . + ParseRet ParamFound; + do { + const unsigned ParameterPos = PD.Parameters.size(); + VFParamKind PKind; + int StepOrPos; + ParamFound = tryParseParameter(MangledName, PKind, StepOrPos); + + // Bail off if there is a parsing error in the parsing of the parameter. + if (ParamFound == ParseRet::Error) + return None; + + if (ParamFound == ParseRet::OK) { + Align Alignment; + // Look for the alignment token "a ". + ParseRet AlignFound = tryParseAlign(MangledName, Alignment); + // Bail off if there is a syntax error in the align token. + if (AlignFound == ParseRet::Error) + return None; + + // Add the parameter. + PD.Parameters.push_back({ParameterPos, PKind, StepOrPos, Alignment}); + } + } while (ParamFound == ParseRet::OK); + + // A valid MangledName mus have at least one valid entry in the + // . + if (PD.Parameters.empty()) + return None; + + // Check for the and the optional , which + // are separated from the prefix with "_" + if (!MangledName.consume_front("_")) + return None; + + // The rest of the string must be in the format: + // [()] + PD.ScalarName = MangledName.take_while([](char In) { return In != '('; }); + + if (PD.ScalarName.empty()) + return None; + + // Reduce MangledName to [()]. + MangledName = MangledName.ltrim(PD.ScalarName); + // Find the optional custom name redirection. + if (MangledName.consume_front("(")) { + if (!MangledName.consume_back(")")) + return None; + // Update the vector variant with the one specified by the user. + PD.VectorName = MangledName; + // If the vector name is missing, bail out. + if (PD.VectorName.empty()) + return None; + } + + // When is "M", we need to add a parameter that is used as + // global predicate for the function. + if (PD.IsMasked) { + unsigned Pos = PD.Parameters.size(); + PD.Parameters.push_back({Pos, VFParamKind::GlobalPredicate}); + } + + // Asserts for parameters of type `VFParamKind::GlobalPredicate`, as + // prescribed by the Vector Function ABI specifications supported by + // this parser: + // 1. Uniqueness. + // 2. Must be the last in the parameter list. + const auto NGlobalPreds = std::count_if( + PD.Parameters.begin(), PD.Parameters.end(), [](const VFParameter PK) { + return PK.ParamKind == VFParamKind::GlobalPredicate; + }); + assert(NGlobalPreds < 2 && "Cannot have more than one global predicate."); + if (NGlobalPreds) + assert(PD.Parameters.back().ParamKind == VFParamKind::GlobalPredicate && + "The global predicate must be the last parameter"); + + return PD; +} + +VFParamKind VFABI::getVFParamKindFromString(const StringRef Token) { + const VFParamKind ParamKind = StringSwitch(Token) + .Case("v", VFParamKind::Vector) + .Case("l", VFParamKind::OMP_Linear) + .Case("R", VFParamKind::OMP_LinearRef) + .Case("L", VFParamKind::OMP_LinearVal) + .Case("U", VFParamKind::OMP_LinearUVal) + .Case("ls", VFParamKind::OMP_LinearPos) + .Case("Ls", VFParamKind::OMP_LinearValPos) + .Case("Rs", VFParamKind::OMP_LinearRefPos) + .Case("Us", VFParamKind::OMP_LinearUValPos) + .Case("u", VFParamKind::OMP_Uniform) + .Default(VFParamKind::Unknown); + + if (ParamKind != VFParamKind::Unknown) + return ParamKind; + + // This function should never be invoked with an invalid input. + llvm_unreachable("This fuction should be invoken only on parameters" + " that have a textual representation in the mangled name" + " of the Vector Function ABI"); +} Index: llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/vfabi-demangle-fuzzer/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Analysis + Support +) +add_llvm_fuzzer(vfabi-demangler-fuzzer + vfabi-demangler-fuzzer.cpp +) Index: llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp =================================================================== --- /dev/null +++ llvm/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp @@ -0,0 +1,26 @@ +//===-- vfabi-demangler-fuzzer.cpp - Fuzzer VFABI using lib/Fuzzer ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Build tool to fuzz the demangler for the vector function ABI names. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VectorUtils.h" + +using namespace llvm; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const StringRef MangledName((const char *)Data, Size); + const auto PD = VFABI::tryDemangleForVFABI(MangledName); + + // Do not optimize away the return value. Inspired by + // https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307-L345 + asm volatile("" : : "r,m"(PD) : "memory"); + + return 0; +} Index: llvm/unittests/Analysis/CMakeLists.txt =================================================================== --- llvm/unittests/Analysis/CMakeLists.txt +++ llvm/unittests/Analysis/CMakeLists.txt @@ -28,6 +28,7 @@ PhiValuesTest.cpp ProfileSummaryInfoTest.cpp ScalarEvolutionTest.cpp + VectorFunctionABITest.cpp SparsePropagation.cpp TargetLibraryInfoTest.cpp TBAATest.cpp Index: llvm/unittests/Analysis/VectorFunctionABITest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Analysis/VectorFunctionABITest.cpp @@ -0,0 +1,436 @@ +//===------- VectorFunctionABITest.cpp - VFABI Unittests ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VectorUtils.h" +#include "gtest/gtest.h" + +using namespace llvm; + +// This test makes sure that the getFromVFABI method succeeds only on +// valid values of the string. +TEST(VectorFunctionABITests, OnlyValidNames) { + // Incomplete string. + EXPECT_FALSE(VFInfo::getFromVFABI("").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGV").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVn").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2v").hasValue()); + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2v_").hasValue()); + // Missing parameters. + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2_foo").hasValue()); + // Missing _ZGV prefix. + EXPECT_FALSE(VFInfo::getFromVFABI("_ZVnN2v_foo").hasValue()); + // Missing . + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVN2v_foo").hasValue()); + // Missing . + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVn2v_foo").hasValue()); + // Missing . + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnNv_foo").hasValue()); + // Missing . + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2v_").hasValue()); + // Missing _ separator. + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2vfoo").hasValue()); + // Missing . + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2v_foo()").hasValue()); + // Unterminated name. + EXPECT_FALSE(VFInfo::getFromVFABI("_ZGVnN2v_foo(bar").hasValue()); +} + +TEST(VectorFunctionABITests, ParamListParsing) { + // Testing "vl16Ls32R3l" + const auto OptVFS = VFInfo::getFromVFABI("_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 = VFInfo::getFromVFABI("_ZGVnM2v_sin"); + const auto B = VFInfo::getFromVFABI("_ZGVnM2v_sin(UserFunc)"); + const auto C = VFInfo::getFromVFABI("_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: + // Field holding the parser output. + VFABI::ParsedData PD; + // Reset the parser output references. + void reset() { + VF = 0; + IsMasked = false; + ISA = VFISAKind::Unknown; + Parameters.clear(); + ScalarName = ""; + VectorName = ""; + IsScalable = false; + } + +protected: + // Referencies to the parser output field. + unsigned &VF = PD.VF; + bool &IsMasked = PD.IsMasked; + VFISAKind &ISA = PD.ISA; + SmallVector &Parameters = PD.Parameters; + StringRef &ScalarName = PD.ScalarName; + StringRef &VectorName = PD.VectorName; + bool &IsScalable = PD.IsScalable; + // Invoke the parser. + bool invokeParser(const StringRef MangledName) { + reset(); + const auto OptPD = VFABI::tryDemangleForVFABI(MangledName); + if (OptPD.hasValue()) { + PD = OptPD.getValue(); + return true; + } + + return false; + } +}; +} // unnamed namespace + +TEST_F(VFABIParserTest, Parse) { + EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(Parameters.size(), (unsigned)9); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearPos, 2})); + EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearValPos, 27})); + EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUValPos, 4})); + EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_LinearRefPos, 5})); + EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_Linear, 1})); + EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearVal, 10})); + EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, 100})); + EXPECT_EQ(Parameters[8], VFParameter({8, VFParamKind::OMP_LinearRef, 1000})); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin"); +} + +TEST_F(VFABIParserTest, ParseVectorName) { + EXPECT_TRUE(invokeParser("_ZGVnN2v_sin(my_v_sin)")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + EXPECT_EQ(Parameters.size(), (unsigned)1); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "my_v_sin"); +} + +TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) { + EXPECT_TRUE(invokeParser("_ZGVnN2ln1Ln10Un100Rn1000_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(Parameters.size(), (unsigned)4); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Linear, -1})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_LinearVal, -10})); + EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearUVal, -100})); + EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, -1000})); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVnN2ln1Ln10Un100Rn1000_sin"); +} + +TEST_F(VFABIParserTest, ParseScalableSVE) { + EXPECT_TRUE(invokeParser("_ZGVsMxv_sin")); + EXPECT_EQ(VF, (unsigned)0); + EXPECT_TRUE(IsMasked); + EXPECT_TRUE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::SVE); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVsMxv_sin"); +} + +TEST_F(VFABIParserTest, ParseFixedWidthSVE) { + EXPECT_TRUE(invokeParser("_ZGVsM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::SVE); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVsM2v_sin"); +} + +TEST_F(VFABIParserTest, NotAVectorFunctionABIName) { + // Vector names should start with `_ZGV`. + EXPECT_FALSE(invokeParser("ZGVnN2v_sin")); +} + +TEST_F(VFABIParserTest, LinearWithRuntimeStep) { + EXPECT_FALSE(invokeParser("_ZGVnN2ls_sin")) + << "A number should be present after \"ls\"."; + EXPECT_TRUE(invokeParser("_ZGVnN2ls2_sin")); + EXPECT_FALSE(invokeParser("_ZGVnN2Rs_sin")) + << "A number should be present after \"Rs\"."; + EXPECT_TRUE(invokeParser("_ZGVnN2Rs4_sin")); + EXPECT_FALSE(invokeParser("_ZGVnN2Ls_sin")) + << "A number should be present after \"Ls\"."; + EXPECT_TRUE(invokeParser("_ZGVnN2Ls6_sin")); + EXPECT_FALSE(invokeParser("_ZGVnN2Us_sin")) + << "A number should be present after \"Us\"."; + EXPECT_TRUE(invokeParser("_ZGVnN2Us8_sin")); +} + +TEST_F(VFABIParserTest, LinearWithoutCompileTime) { + EXPECT_TRUE(invokeParser("_ZGVnN3lLRUlnLnRnUn_sin")); + 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})); + EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_LinearRef, 1})); + EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::OMP_LinearUVal, 1})); + EXPECT_EQ(Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, -1})); + EXPECT_EQ(Parameters[5], VFParameter({5, VFParamKind::OMP_LinearVal, -1})); + EXPECT_EQ(Parameters[6], VFParameter({6, VFParamKind::OMP_LinearRef, -1})); + EXPECT_EQ(Parameters[7], VFParameter({7, VFParamKind::OMP_LinearUVal, -1})); +} + +TEST_F(VFABIParserTest, ISA) { + EXPECT_TRUE(invokeParser("_ZGVqN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::Unknown); + + EXPECT_TRUE(invokeParser("_ZGVnN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + + EXPECT_TRUE(invokeParser("_ZGVsN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::SVE); + + EXPECT_TRUE(invokeParser("_ZGVbN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::SSE); + + EXPECT_TRUE(invokeParser("_ZGVcN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX); + + EXPECT_TRUE(invokeParser("_ZGVdN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX2); + + EXPECT_TRUE(invokeParser("_ZGVeN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX512); +} + +TEST_F(VFABIParserTest, InvalidMask) { + EXPECT_FALSE(invokeParser("_ZGVsK2v_sin")); +} + +TEST_F(VFABIParserTest, InvalidParameter) { + EXPECT_FALSE(invokeParser("_ZGVsM2vX_sin")); +} + +TEST_F(VFABIParserTest, Align) { + EXPECT_TRUE(invokeParser("_ZGVsN2l2a2_sin")); + EXPECT_EQ(Parameters.size(), (unsigned)1); + EXPECT_EQ(Parameters[0].Alignment, Align(2)); + + // Missing alignement value. + EXPECT_FALSE(invokeParser("_ZGVsM2l2a_sin")); + // Invalid alignment token "x". + EXPECT_FALSE(invokeParser("_ZGVsM2l2ax_sin")); + // Alignment MUST be associated to a paramater. + EXPECT_FALSE(invokeParser("_ZGVsM2a2_sin")); + // Alignment must be a power of 2. + EXPECT_FALSE(invokeParser("_ZGVsN2l2a0_sin")); + EXPECT_TRUE(invokeParser("_ZGVsN2l2a1_sin")); + EXPECT_FALSE(invokeParser("_ZGVsN2l2a3_sin")); + EXPECT_FALSE(invokeParser("_ZGVsN2l2a6_sin")); +} + +TEST_F(VFABIParserTest, ParseUniform) { + EXPECT_TRUE(invokeParser("_ZGVnN2u0_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(Parameters.size(), (unsigned)1); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0})); + EXPECT_EQ(ScalarName, "sin"); + EXPECT_EQ(VectorName, "_ZGVnN2u0_sin"); + + EXPECT_FALSE(invokeParser("_ZGVnN2u_sin")); + EXPECT_FALSE(invokeParser("_ZGVnN2ul_sin")); +} + +TEST_F(VFABIParserTest, ISAIndependentMangling) { + // This test makes sure that the mangling of the parameters in + // independent on the token. + const SmallVector ExpectedParams = { + VFParameter({0, VFParamKind::Vector, 0}), + VFParameter({1, VFParamKind::OMP_LinearPos, 2}), + VFParameter({2, VFParamKind::OMP_LinearValPos, 27}), + VFParameter({3, VFParamKind::OMP_LinearUValPos, 4}), + VFParameter({4, VFParamKind::OMP_LinearRefPos, 5}), + VFParameter({5, VFParamKind::OMP_Linear, 1}), + VFParameter({6, VFParamKind::OMP_LinearVal, 10}), + VFParameter({7, VFParamKind::OMP_LinearUVal, 100}), + VFParameter({8, VFParamKind::OMP_LinearRef, 1000}), + VFParameter({9, VFParamKind::OMP_Uniform, 2}), + }; + +#define __COMMON_CHECKS \ + do { \ + EXPECT_EQ(VF, (unsigned)2); \ + EXPECT_FALSE(IsMasked); \ + EXPECT_FALSE(IsScalable); \ + EXPECT_EQ(Parameters.size(), (unsigned)10); \ + EXPECT_EQ(Parameters, ExpectedParams); \ + EXPECT_EQ(ScalarName, "sin"); \ + } while (0) + + // Advanced SIMD: = "n" + EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // SVE: = "s" + EXPECT_TRUE(invokeParser("_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::SVE); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // SSE: = "b" + EXPECT_TRUE(invokeParser("_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::SSE); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX: = "c" + EXPECT_TRUE(invokeParser("_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX2: = "d" + EXPECT_TRUE(invokeParser("_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX2); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX512: = "e" + EXPECT_TRUE(invokeParser("_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::AVX512); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // Unknown ISA (randomly using "q"). This test will need update if + // some targets decide to use "q" as their ISA token. + EXPECT_TRUE(invokeParser("_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::Unknown); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + +#undef __COMMON_CHECKS +} + +TEST_F(VFABIParserTest, MissingScalarName) { + EXPECT_FALSE(invokeParser("_ZGVnN2v_")); +} + +TEST_F(VFABIParserTest, MissingVectorName) { + EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()")); +} + +TEST_F(VFABIParserTest, MissingVectorNameTermination) { + EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar")); +} + +TEST_F(VFABIParserTest, ParseMaskingNEON) { + EXPECT_TRUE(invokeParser("_ZGVnM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +} + +TEST_F(VFABIParserTest, ParseMaskingSVE) { + EXPECT_TRUE(invokeParser("_ZGVsM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::SVE); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +} + +TEST_F(VFABIParserTest, ParseMaskingSSE) { + EXPECT_TRUE(invokeParser("_ZGVbM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::SSE); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +} + +TEST_F(VFABIParserTest, ParseMaskingAVX) { + EXPECT_TRUE(invokeParser("_ZGVcM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::AVX); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +} + +TEST_F(VFABIParserTest, ParseMaskingAVX2) { + EXPECT_TRUE(invokeParser("_ZGVdM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::AVX2); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +} + +TEST_F(VFABIParserTest, ParseMaskingAVX512) { + EXPECT_TRUE(invokeParser("_ZGVeM2v_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_TRUE(IsMasked); + EXPECT_FALSE(IsScalable); + EXPECT_EQ(ISA, VFISAKind::AVX512); + EXPECT_EQ(Parameters.size(), (unsigned)2); + EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector})); + EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate})); + EXPECT_EQ(ScalarName, "sin"); +}