Index: llvm/include/llvm/Analysis/VectorUtils.h =================================================================== --- llvm/include/llvm/Analysis/VectorUtils.h +++ llvm/include/llvm/Analysis/VectorUtils.h @@ -20,6 +20,153 @@ namespace llvm { +/// Describes the type of Parameters +enum class VFParamKind { + Vector, // declare simd + 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) + Invalid +}; + +/// Describes the type of Instruction Set Architecture +enum class VFISAKind { + ISA_AdvancedSIMD, // AArch64 Advanced SIMD (NEON) + ISA_SVE, // AArch64 Scalable Vector Extension + ISA_SSE, // x86 SSE + ISA_AVX, // x86 AVX + ISA_AVX2, // x86 AVX2 + ISA_AVX512, // x86 AVX512 + ISA_Invalid // Unsupported 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; // Step or Position of the Parameter. + unsigned Alignment = 1; // 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. +bool tryParserVFABIName(const StringRef mangledName, ParsedData &PD); + +/// Retrieve the `VFParamKind` from a string token. +Optional getVFParamKindFromString(const StringRef Token) { + 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::Invalid); + + if (ParamKind != VFParamKind::Invalid) + 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"); +} +} // end namespace VFABI + +/// Contains the information about the kind of vectoriazation +/// 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 IsMasked; // True if Masked. + bool IsScalable; // True if Scalable. + VFISAKind ISA; // Instruction Set Architecture. + SmallVector Parameters; // List of parameter informations. + StringRef ScalarName; // Scalar Function Name. + StringRef VectorName; // Vector Function Name associated to this VFShape. + + // Comparison operator. + bool operator==(const VFShape &other) const { + return (VF == other.VF && IsMasked == other.IsMasked && + IsScalable == other.IsScalable && ISA == other.ISA && + Parameters == other.Parameters); + } + + /// Static method to contruct a VFShape 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) { + VFABI::ParsedData PD; + if (VFABI::tryParserVFABIName(MangledName, PD)) + return VFShape({PD.VF, PD.IsMasked, PD.IsScalable, PD.ISA, PD.Parameters, + PD.ScalarName, PD.VectorName}); + + // The values of MangledNames are supposed to be valid once they + // are present in the backend. We throw a debug error if it is not + // the case. + llvm_unreachable("Invalid Vector Function ABI name."); + } +}; + 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,362 @@ +//===- 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. +/// +/// `get*` methods: extract values from fixed parts of the string, +/// without modifying it. +/// +/// `consume*` methods: find variable size tokens that are +/// mandatory. The string to be parsed is advanced. +/// +/// `tryParse` methods: find optional tokens and advance the string +/// to be parsed. + +/// Extracts the `` information from the mangled string, and +/// sets the `ISA` accordingly. The input string `MangledName` is left +/// unmodified. Return false in case of failure, true otherwise. +bool getISA(const StringRef MangledName, VFISAKind &ISA) { + // Capture the ISA value (one char): `_ZGV...`. + StringRef ISAValue = MangledName.substr(4, 1); + + ISA = StringSwitch(ISAValue) + .Case("n", VFISAKind::ISA_AdvancedSIMD) + .Case("s", VFISAKind::ISA_SVE) + .Case("b", VFISAKind::ISA_SSE) + .Case("c", VFISAKind::ISA_AVX) + .Case("d", VFISAKind::ISA_AVX2) + .Case("e", VFISAKind::ISA_AVX512) + .Default(VFISAKind::ISA_Invalid); + + return ISA != VFISAKind::ISA_Invalid; +} + +/// Extracts the `` information from the mangled string, and +/// sets `IsMasked` accordingly. The input string `MangledName` is +/// left unmodified. Return false in case of failure, true +/// otherwise. +bool getMask(const StringRef MangledName, bool &IsMasked) { + // Capture the MASK value (one char): `_ZGV...`. + StringRef Mask = MangledName.substr(5, 1); + + if (Mask == "M") { + IsMasked = true; + return true; + } + + if (Mask == "N") { + IsMasked = false; + return true; + } + + return false; +} + +/// Extract the `` information from the mangled string, and +/// sets `VF` accordingly. Returns false in case of failure, true +/// otherwise. A ` == "x"` token is interpreted as a scalable +/// vector length. On success, the `` token is removed from +/// the input string `ParseString`. +/// +bool consumeVLEN(StringRef &ParseString, unsigned &VF, bool &IsScalable) { + if (ParseString.consume_front("x")) { + VF = 0; + IsScalable = true; + return true; + } + + if (ParseString.consumeInteger(10, VF)) + return false; + + IsScalable = false; + return true; +} + +// Return types for the parser functions. +enum class ParseRet { + OK, // Found. + None, // Not found. + Error // Syntax error. +}; + +/// 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) { + assert((Token == "ls" || Token == "Rs" || Token == "Us" || Token == "Ls") && + "Not a valid token."); + if (ParseString.consume_front(Token)) { + PKind = VFABI::getVFParamKindFromString(Token).getValue(); + 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". +bool consumeCompileTimeLinearToken(StringRef &ParseString, VFParamKind &PKind, + int &LinearStep, const StringRef Token) { + assert((Token == "l" || Token == "R" || Token == "U" || Token == "L") && + "Not a valid token."); + if (ParseString.consume_front(Token)) { + PKind = VFABI::getVFParamKindFromString(Token).getValue(); + const bool Negate = ParseString.consume_front("n"); + if (ParseString.consumeInteger(10, LinearStep)) + LinearStep = 1; + if (Negate) + LinearStep *= -1; + return true; + } + + return false; +} + +/// 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 (consumeCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l")) + return ParseRet::OK; + + // "R" {"n"} + if (consumeCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R")) + return ParseRet::OK; + + // "L" {"n"} + if (consumeCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L")) + return ParseRet::OK; + + // "U" {"n"} + if (consumeCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U")) + 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).getValue(); + 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, unsigned &Align) { + // "a" + if (ParseString.consume_front("a")) { + if (ParseString.consumeInteger(10, Align)) + return ParseRet::Error; + return ParseRet::OK; + } + + return ParseRet::None; +} +} // namespace + +// Format of the ABI name: +// _ZGV_[()] +bool VFABI::tryParserVFABIName(const 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.startswith("_ZGV")) + return false; + + if (!getISA(MangledName, PD.ISA)) + return false; + + if (!getMask(MangledName, PD.IsMasked)) + return false; + + // First, drop the first 6 chars of the mangled name: _ZGV + StringRef ParseString = MangledName.drop_front(6); + + // Parse the variable size, starting from . + if (!consumeVLEN(ParseString, PD.VF, PD.IsScalable)) + return false; + + // Parse the . + ParseRet ParamFound; + do { + const unsigned ParameterPos = PD.Parameters.size(); + VFParamKind PKind; + int StepOrPos; + ParamFound = tryParseParameter(ParseString, PKind, StepOrPos); + + // Bail off if there is a parsing error in the parsing of the parameter. + if (ParamFound == ParseRet::Error) + return false; + + if (ParamFound == ParseRet::OK) { + VFParameter Parameter = {ParameterPos, PKind, StepOrPos}; + + // Look for the alignment token "a ". + ParseRet AlignFound = tryParseAlign(ParseString, Parameter.Alignment); + // Bail off if there is a syntax error in the align token. + if (AlignFound == ParseRet::Error) + return false; + + // Add the parameter. + PD.Parameters.push_back(Parameter); + } + } while (ParamFound == ParseRet::OK); + + // Check for the and the optional , which + // are separated from the prefix with "_" + if (!ParseString.consume_front("_")) + return false; + + // The rest of the string must be in the format: + // [()] + PD.ScalarName = + ParseString.take_while([](char In) -> bool { return In != '('; }); + + // Reduce ParseString to [()]. + ParseString = ParseString.ltrim(PD.ScalarName); + // Find the optional custom name redirection. + if (ParseString.consume_front("(")) { + if (!ParseString.consume_back(")")) + return false; + // Update the vector variant with the one specified by the user. + PD.VectorName = ParseString; + } + + return true; +} 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,36 @@ +//===-- 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); + unsigned VF; + bool IsMasked; + VFISAKind ISA; + SmallVector Parameters; + StringRef ScalarName; + StringRef VectorVariant; + bool IsScalable; + bool Ret = + VFABI::tryParserVFABIName(MangledName, VF, IsMasked, IsScalable, ISA, + Parameters, ScalarName, VectorVariant); + + // Do not optimize away A and B. Inspired by + // https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307-L345 + asm volatile("" : : "r,m"(Ret) : "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,300 @@ +//===------- 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; + +TEST(VectorFunctionABITests, ParamListParsing) { + // Testing "vl16Ls32R3l" + const auto OptVFS = VFShape::getFromVFABI("_ZGVnN2vl16Ls32R3l_foo"); + EXPECT_TRUE(OptVFS.hasValue()); + const VFShape VFS = OptVFS.getValue(); + EXPECT_EQ(VFS.Parameters.size(), (unsigned)5); + EXPECT_EQ(VFS.Parameters[0], VFParameter({0, VFParamKind::Vector, 0})); + EXPECT_EQ(VFS.Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 16})); + EXPECT_EQ(VFS.Parameters[2], + VFParameter({2, VFParamKind::OMP_LinearValPos, 32})); + EXPECT_EQ(VFS.Parameters[3], VFParameter({3, VFParamKind::OMP_LinearRef, 3})); + EXPECT_EQ(VFS.Parameters[4], VFParameter({4, VFParamKind::OMP_Linear, 1})); +} + +TEST(VectorFunctionABITests, ScalarNameAndVectorName) { + // Parse Scalar Name + const auto A = VFShape::getFromVFABI("_ZGVnM2v_sin"); + const auto B = VFShape::getFromVFABI("_ZGVnM2v_sin(UserFunc)"); + const auto C = VFShape::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::ISA_Invalid; + 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 bool Ret = VFABI::tryParserVFABIName(MangledName, PD); + return Ret; + } +}; +} // unnamed namespace + +TEST_F(VFABIParserTest, Parse) { + EXPECT_TRUE(invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_EQ(ISA, VFISAKind::ISA_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::ISA_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::ISA_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::ISA_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::ISA_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, ValidAndInvalidISA) { + EXPECT_FALSE(invokeParser("_ZGVqN2v_sin")); + + EXPECT_TRUE(invokeParser("_ZGVnN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AdvancedSIMD); + + EXPECT_TRUE(invokeParser("_ZGVsN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_SVE); + + EXPECT_TRUE(invokeParser("_ZGVbN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_SSE); + + EXPECT_TRUE(invokeParser("_ZGVcN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AVX); + + EXPECT_TRUE(invokeParser("_ZGVdN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AVX2); + + EXPECT_TRUE(invokeParser("_ZGVeN2v_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_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("_ZGVsM2l2a2_sin")); + EXPECT_EQ(Parameters.size(), (unsigned)1); + EXPECT_EQ(Parameters[0].Alignment, (unsigned)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")); +} + +TEST_F(VFABIParserTest, ParseUniform) { + EXPECT_TRUE(invokeParser("_ZGVnN2u0_sin")); + EXPECT_EQ(VF, (unsigned)2); + EXPECT_FALSE(IsMasked); + EXPECT_EQ(ISA, VFISAKind::ISA_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::ISA_AdvancedSIMD); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // SVE: = "s" + EXPECT_TRUE(invokeParser("_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_SVE); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // SSE: = "b" + EXPECT_TRUE(invokeParser("_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_SSE); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX: = "c" + EXPECT_TRUE(invokeParser("_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AVX); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX2: = "d" + EXPECT_TRUE(invokeParser("_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AVX2); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + + // AVX512: = "e" + EXPECT_TRUE(invokeParser("_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin")); + EXPECT_EQ(ISA, VFISAKind::ISA_AVX512); + __COMMON_CHECKS; + EXPECT_EQ(VectorName, "_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u2_sin"); + +#undef __COMMON_CHECKS +}