Files
clang-p2996/llvm/unittests/Analysis/VectorFunctionABITest.cpp
Paschalis Mpeis ddb6db4d09 [VFABI] Create FunctionType for vector functions (#75058)
`createFunctionType` returns a FunctionType  that may contain a mask,
which is currently placed as the last parameter to the Function.
The placement happens according to `VFParameters` of `VFInfo`, and it
should be able to handle VFABI specification changes.

Regarding the return type, it uses the scalar type of the input instruction,
as the specification does not encode in the mangled name such information.
If that ever happens, that information should be available from `VFInfo`.
2023-12-19 12:05:28 +00:00

858 lines
33 KiB
C++

//===------- VectorFunctionABITest.cpp - VFABI unit tests ---------===//
//
// 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/ADT/StringRef.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/InstIterator.h"
#include "gtest/gtest.h"
#include <optional>
using namespace llvm;
namespace {
static LLVMContext Ctx;
/// Perform tests against VFABI Rules. `invokeParser` creates a VFInfo object
/// and a scalar FunctionType, which are used by tests to check that:
/// 1. The scalar and vector names are correct.
/// 2. The number of parameters from the parsed mangled name matches the number
/// of arguments in the scalar function passed as FunctionType string.
/// 3. The number of vector parameters and their types match the values
/// specified in the test.
/// On masked functions it also checks that the last parameter is a mask (ie,
/// GlobalPredicate).
/// 4. The vector function is correctly found to have a mask.
///
class VFABIParserTest : public ::testing::Test {
private:
// Parser output.
VFInfo Info;
/// Reset the data needed for the test.
void reset(const StringRef ScalarFTyStr) {
M = parseAssemblyString("declare void @dummy()", Err, Ctx);
EXPECT_NE(M.get(), nullptr) << "Loading an invalid module.\n "
<< Err.getMessage() << "\n";
Type *Ty = parseType(ScalarFTyStr, Err, *(M.get()));
ScalarFTy = dyn_cast<FunctionType>(Ty);
EXPECT_NE(ScalarFTy, nullptr)
<< "Invalid function type string: " << ScalarFTyStr << "\n"
<< Err.getMessage() << "\n";
// Reset the VFInfo
Info = VFInfo();
}
// Data needed to load the optional IR passed to invokeParser
SMDiagnostic Err;
std::unique_ptr<Module> M;
FunctionType *ScalarFTy = nullptr;
protected:
// References to the parser output field.
ElementCount &VF = Info.Shape.VF;
VFISAKind &ISA = Info.ISA;
/// Parameters for the vectorized function
SmallVector<VFParameter, 8> &Parameters = Info.Shape.Parameters;
std::string &ScalarName = Info.ScalarName;
std::string &VectorName = Info.VectorName;
/// Invoke the parser. Every time this method is invoked the state of the test
/// is reset.
///
/// \p MangledName string the parser has to demangle.
///
/// \p ScalarFTyStr FunctionType string to get the signature of the scalar
/// function, which is used by `tryDemangleForVFABI` to check for the number
/// of arguments on scalable vectors, and by `matchParameters` to perform some
/// additional checking in the tests in this file.
bool invokeParser(const StringRef MangledName,
const StringRef ScalarFTyStr = "void()") {
// Reset the VFInfo to be able to call `invokeParser` multiple times in
// the same test.
reset(ScalarFTyStr);
const auto OptInfo = VFABI::tryDemangleForVFABI(MangledName, ScalarFTy);
if (OptInfo)
Info = *OptInfo;
return OptInfo.has_value();
}
/// Returns whether the parsed function contains a mask.
bool isMasked() const { return Info.isMasked(); }
/// Check if the number of vectorized parameters matches the scalar ones. This
/// requires a correct scalar FunctionType string to be fed to the
/// 'invokeParser'. Mask parameters that are only required by the vector
/// function call are ignored.
bool matchParametersNum() {
return (Parameters.size() - isMasked()) == ScalarFTy->getNumParams();
}
FunctionType *getFunctionType() {
return VFABI::createFunctionType(Info, ScalarFTy);
}
};
} // unnamed namespace
// Function Types commonly used in tests
FunctionType *FTyMaskVLen2_i32 = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getFixed(2)),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getFixed(2)),
},
false);
FunctionType *FTyNoMaskVLen2_i32 = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getFixed(2)),
},
false);
FunctionType *FTyMaskedVLA_i32 = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getScalable(4)),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getScalable(4)),
},
false);
// 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 <isa>.
EXPECT_FALSE(invokeParser("_ZGVN2v_foo"));
// Missing <mask>.
EXPECT_FALSE(invokeParser("_ZGVn2v_foo"));
// Missing <vlen>.
EXPECT_FALSE(invokeParser("_ZGVnNv_foo"));
// Missing <scalarname>.
EXPECT_FALSE(invokeParser("_ZGVnN2v_"));
// Missing _ separator.
EXPECT_FALSE(invokeParser("_ZGVnN2vfoo"));
// Missing <vectorname>.
EXPECT_FALSE(invokeParser("_ZGVnN2v_foo()"));
// Unterminated name.
EXPECT_FALSE(invokeParser("_ZGVnN2v_foo(bar"));
}
TEST_F(VFABIParserTest, ParamListParsing) {
EXPECT_TRUE(
invokeParser("_ZGVnN2vl16Ls32R3l_foo", "void(i32, i32, i32, ptr, i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_EQ(false, isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getFixed(2)),
Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(), Type::getInt32Ty(Ctx)},
false);
EXPECT_EQ(getFunctionType(), FTy);
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}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "_ZGVnN2vl16Ls32R3l_foo");
}
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());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
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());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ScalarNameAndVectorName_03) {
EXPECT_TRUE(
invokeParser("_ZGVnM2v___foo_bar_abc(fooBarAbcVec)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_EQ(true, isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(ScalarName, "__foo_bar_abc");
EXPECT_EQ(VectorName, "fooBarAbcVec");
}
TEST_F(VFABIParserTest, ScalarNameOnly) {
EXPECT_TRUE(invokeParser("_ZGVnM2v___foo_bar_abc"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_EQ(true, isMasked());
EXPECT_EQ(ScalarName, "__foo_bar_abc");
// no vector name specified (as it's optional), so it should have the entire
// mangled name.
EXPECT_EQ(VectorName, "_ZGVnM2v___foo_bar_abc");
}
TEST_F(VFABIParserTest, Parse) {
EXPECT_TRUE(
invokeParser("_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_foo",
"void(i32, i32, i32, i32, ptr, i32, i32, i32, ptr)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getFixed(2)),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(),
},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000_foo");
}
TEST_F(VFABIParserTest, ParseVectorName) {
EXPECT_TRUE(invokeParser("_ZGVnN2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
EXPECT_EQ(Parameters.size(), (unsigned)1);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector, 0}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, LinearWithCompileTimeNegativeStep) {
EXPECT_TRUE(invokeParser("_ZGVnN2ln1Ln10Un100Rn1000_foo(vector_foo)",
"void(i32, i32, i32, ptr)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo()},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseScalableSVE) {
EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskedVLA_i32);
EXPECT_EQ(VF, ElementCount::getScalable(4));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseFixedWidthSVE) {
EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, NotAVectorFunctionABIName) {
// Vector names should start with `_ZGV`.
EXPECT_FALSE(invokeParser("ZGVnN2v_foo"));
}
TEST_F(VFABIParserTest, LinearWithRuntimeStep) {
EXPECT_FALSE(invokeParser("_ZGVnN2ls_foo"))
<< "A number should be present after \"ls\".";
EXPECT_TRUE(invokeParser("_ZGVnN2ls2_foo"));
EXPECT_FALSE(invokeParser("_ZGVnN2Rs_foo"))
<< "A number should be present after \"Rs\".";
EXPECT_TRUE(invokeParser("_ZGVnN2Rs4_foo"));
EXPECT_FALSE(invokeParser("_ZGVnN2Ls_foo"))
<< "A number should be present after \"Ls\".";
EXPECT_TRUE(invokeParser("_ZGVnN2Ls6_foo"));
EXPECT_FALSE(invokeParser("_ZGVnN2Us_foo"))
<< "A number should be present after \"Us\".";
EXPECT_TRUE(invokeParser("_ZGVnN2Us8_foo"));
}
TEST_F(VFABIParserTest, LinearWithoutCompileTime) {
EXPECT_TRUE(invokeParser("_ZGVnN3lLRUlnLnRnUn_foo(vector_foo)",
"void(i32, i32, ptr, i32, i32, i32, ptr, i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(), Type::getInt32Ty(Ctx)},
false);
EXPECT_EQ(getFunctionType(), FTy);
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}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, LLVM_ISA) {
EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_foo"));
EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::LLVM);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32);
EXPECT_EQ(Parameters.size(), (unsigned)1);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, InvalidMask) {
EXPECT_FALSE(invokeParser("_ZGVsK2v_foo"));
}
TEST_F(VFABIParserTest, InvalidParameter) {
EXPECT_FALSE(invokeParser("_ZGVsM2vX_foo"));
}
TEST_F(VFABIParserTest, Align) {
EXPECT_TRUE(invokeParser("_ZGVsN2l2a2_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(Parameters.size(), (unsigned)1);
EXPECT_EQ(Parameters[0].Alignment, Align(2));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
FunctionType *FTy =
FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)}, false);
EXPECT_EQ(getFunctionType(), FTy);
// Missing alignment value.
EXPECT_FALSE(invokeParser("_ZGVsM2l2a_foo"));
// Invalid alignment token "x".
EXPECT_FALSE(invokeParser("_ZGVsM2l2ax_foo"));
// Alignment MUST be associated to a paramater.
EXPECT_FALSE(invokeParser("_ZGVsM2a2_foo"));
// Alignment must be a power of 2.
EXPECT_FALSE(invokeParser("_ZGVsN2l2a0_foo"));
EXPECT_TRUE(invokeParser("_ZGVsN2l2a1_foo"));
EXPECT_FALSE(invokeParser("_ZGVsN2l2a3_foo"));
EXPECT_FALSE(invokeParser("_ZGVsN2l2a6_foo"));
}
TEST_F(VFABIParserTest, ParseUniform) {
EXPECT_TRUE(invokeParser("_ZGVnN2u_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy =
FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)}, false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getFixed(2));
EXPECT_EQ(Parameters.size(), (unsigned)1);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::OMP_Uniform, 0}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
// Uniform doesn't expect extra data.
EXPECT_FALSE(invokeParser("_ZGVnN2u0_foo"));
}
TEST_F(VFABIParserTest, ISAIndependentMangling) {
// This test makes sure that the mangling of the parameters in
// independent on the <isa> token.
const StringRef IRTy =
"void(i32, i32, i32, i32, ptr, i32, i32, i32, i32, i32)";
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getFixed(2)),
Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)->getPointerTo(), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt32Ty(Ctx),
Type::getInt32Ty(Ctx)},
false);
const SmallVector<VFParameter, 8> 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, 0}),
};
#define __COMMON_CHECKS \
do { \
EXPECT_EQ(VF, ElementCount::getFixed(2)); \
EXPECT_FALSE(isMasked()); \
EXPECT_TRUE(matchParametersNum()); \
EXPECT_EQ(getFunctionType(), FTy); \
EXPECT_EQ(Parameters.size(), (unsigned)10); \
EXPECT_EQ(Parameters, ExpectedParams); \
EXPECT_EQ(ScalarName, "foo"); \
EXPECT_EQ(VectorName, "vector_foo"); \
} while (0)
// Advanced SIMD: <isa> = "n"
EXPECT_TRUE(invokeParser(
"_ZGVnN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
__COMMON_CHECKS;
// SVE: <isa> = "s"
EXPECT_TRUE(invokeParser(
"_ZGVsN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::SVE);
__COMMON_CHECKS;
// SSE: <isa> = "b"
EXPECT_TRUE(invokeParser(
"_ZGVbN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::SSE);
__COMMON_CHECKS;
// AVX: <isa> = "c"
EXPECT_TRUE(invokeParser(
"_ZGVcN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::AVX);
__COMMON_CHECKS;
// AVX2: <isa> = "d"
EXPECT_TRUE(invokeParser(
"_ZGVdN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::AVX2);
__COMMON_CHECKS;
// AVX512: <isa> = "e"
EXPECT_TRUE(invokeParser(
"_ZGVeN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::AVX512);
__COMMON_CHECKS;
// LLVM: <isa> = "_LLVM_" internal vector function.
EXPECT_TRUE(invokeParser(
"_ZGV_LLVM_N2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::LLVM);
__COMMON_CHECKS;
// Unknown ISA (randomly using "q"). This test will need update if
// some targets decide to use "q" as their ISA token.
EXPECT_TRUE(invokeParser(
"_ZGVqN2vls2Ls27Us4Rs5l1L10U100R1000u_foo(vector_foo)", IRTy));
EXPECT_EQ(ISA, VFISAKind::Unknown);
__COMMON_CHECKS;
#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_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AdvancedSIMD);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseMaskingSVE) {
EXPECT_TRUE(invokeParser("_ZGVsM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseMaskingSSE) {
EXPECT_TRUE(invokeParser("_ZGVbM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SSE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseMaskingAVX) {
EXPECT_TRUE(invokeParser("_ZGVcM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AVX);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseMaskingAVX2) {
EXPECT_TRUE(invokeParser("_ZGVdM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AVX2);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseMaskingAVX512) {
EXPECT_TRUE(invokeParser("_ZGVeM2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::AVX512);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
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());
EXPECT_EQ(getFunctionType(), FTyMaskVLen2_i32);
EXPECT_EQ(VF, ElementCount::getFixed(2));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseScalableMaskingLLVM) {
EXPECT_FALSE(invokeParser("_ZGV_LLVM_Mxv_foo(vector_foo)"));
}
TEST_F(VFABIParserTest, LLVM_InternalISA) {
EXPECT_FALSE(invokeParser("_ZGV_LLVM_N2v_foo"));
EXPECT_TRUE(invokeParser("_ZGV_LLVM_N2v_foo(vector_foo)", "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::LLVM);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyNoMaskVLen2_i32);
EXPECT_EQ(Parameters.size(), (unsigned)1);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, LLVM_Intrinsics) {
EXPECT_TRUE(invokeParser("_ZGV_LLVM_N4vv_llvm.pow.f32(__svml_powf4)",
"void(float, float)"));
EXPECT_EQ(ISA, VFISAKind::LLVM);
EXPECT_FALSE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getFloatTy(Ctx), ElementCount::getFixed(4)),
VectorType::get(Type::getFloatTy(Ctx), ElementCount::getFixed(4)),
},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getFixed(4));
EXPECT_EQ(Parameters.size(), (unsigned)2);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector}));
EXPECT_EQ(ScalarName, "llvm.pow.f32");
EXPECT_EQ(VectorName, "__svml_powf4");
}
TEST_F(VFABIParserTest, ParseScalableRequiresDeclaration) {
const char *MangledName = "_ZGVsMxv_sin(custom_vg)";
EXPECT_FALSE(invokeParser(MangledName));
EXPECT_TRUE(invokeParser(MangledName, "void(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
EXPECT_EQ(getFunctionType(), FTyMaskedVLA_i32);
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");
EXPECT_EQ(VectorName, "custom_vg");
}
TEST_F(VFABIParserTest, ZeroIsInvalidVLEN) {
EXPECT_FALSE(invokeParser("_ZGVeM0v_foo"));
EXPECT_FALSE(invokeParser("_ZGVeN0v_foo"));
EXPECT_FALSE(invokeParser("_ZGVsM0v_foo"));
EXPECT_FALSE(invokeParser("_ZGVsN0v_foo"));
}
TEST_F(VFABIParserTest, ParseScalableMaskingSVE) {
EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "i32(i32)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getScalable(4)),
{VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getScalable(4)),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getScalable(4))},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getScalable(4));
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, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
TEST_F(VFABIParserTest, ParseScalableMaskingSVESincos) {
EXPECT_TRUE(invokeParser("_ZGVsMxvl8l8_sincos(custom_vector_sincos)",
"void(double, ptr, ptr)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getDoubleTy(Ctx), ElementCount::getScalable(2)),
Type::getInt32Ty(Ctx)->getPointerTo(),
Type::getInt32Ty(Ctx)->getPointerTo(),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getScalable(2)),
},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(VF, ElementCount::getScalable(2));
EXPECT_EQ(Parameters.size(), (unsigned)4);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::OMP_Linear, 8}));
EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::OMP_Linear, 8}));
EXPECT_EQ(Parameters[3], VFParameter({3, VFParamKind::GlobalPredicate}));
EXPECT_EQ(ScalarName, "sincos");
EXPECT_EQ(VectorName, "custom_vector_sincos");
}
// Make sure that we get the correct VF if the return type is wider than any
// parameter type.
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());
FunctionType *FTy = FunctionType::get(
VectorType::get(Type::getInt64Ty(Ctx), ElementCount::getScalable(2)),
{
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getScalable(2)),
VectorType::get(Type::getInt32Ty(Ctx), ElementCount::getScalable(2)),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getScalable(2)),
},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(Parameters.size(), (unsigned)3);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::Vector}));
EXPECT_EQ(Parameters[2], VFParameter({2, VFParamKind::GlobalPredicate}));
EXPECT_EQ(VF, ElementCount::getScalable(2));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
// Make sure we handle void return types.
TEST_F(VFABIParserTest, ParseVoidReturnTypeSVE) {
EXPECT_TRUE(invokeParser("_ZGVsMxv_foo(vector_foo)", "void(i16)"));
EXPECT_EQ(ISA, VFISAKind::SVE);
EXPECT_TRUE(isMasked());
EXPECT_TRUE(matchParametersNum());
FunctionType *FTy = FunctionType::get(
Type::getVoidTy(Ctx),
{
VectorType::get(Type::getInt16Ty(Ctx), ElementCount::getScalable(8)),
VectorType::get(Type::getInt1Ty(Ctx), ElementCount::getScalable(8)),
},
false);
EXPECT_EQ(getFunctionType(), FTy);
EXPECT_EQ(Parameters.size(), (unsigned)2);
EXPECT_EQ(Parameters[0], VFParameter({0, VFParamKind::Vector}));
EXPECT_EQ(Parameters[1], VFParameter({1, VFParamKind::GlobalPredicate}));
EXPECT_EQ(VF, ElementCount::getScalable(8));
EXPECT_EQ(ScalarName, "foo");
EXPECT_EQ(VectorName, "vector_foo");
}
// Make sure we reject unsupported parameter types.
TEST_F(VFABIParserTest, ParseUnsupportedElementTypeSVE) {
EXPECT_FALSE(invokeParser("_ZGVsMxv_foo(vector_foo)", "void(i128)"));
}
// Make sure we reject unsupported return types
TEST_F(VFABIParserTest, ParseUnsupportedReturnTypeSVE) {
EXPECT_FALSE(invokeParser("_ZGVsMxv_foo(vector_foo)", "fp128(float)"));
}
class VFABIAttrTest : public testing::Test {
protected:
void SetUp() override {
M = parseAssemblyString(IR, Err, Ctx);
// Get the only call instruction in the block, which is the first
// instruction.
CI = dyn_cast<CallInst>(&*(instructions(M->getFunction("f")).begin()));
}
const char *IR = "define i32 @f(i32 %a) {\n"
" %1 = call i32 @g(i32 %a) #0\n"
" ret i32 %1\n"
"}\n"
"declare i32 @g(i32)\n"
"declare <2 x i32> @custom_vg(<2 x i32>)"
"declare <4 x i32> @_ZGVnN4v_g(<4 x i32>)"
"declare <8 x i32> @_ZGVnN8v_g(<8 x i32>)"
"attributes #0 = { "
"\"vector-function-abi-variant\"=\""
"_ZGVnN2v_g(custom_vg),_ZGVnN4v_g\" }";
LLVMContext Ctx;
SMDiagnostic Err;
std::unique_ptr<Module> M;
CallInst *CI;
SmallVector<std::string, 8> Mappings;
};
TEST_F(VFABIAttrTest, Read) {
VFABI::getVectorVariantNames(*CI, Mappings);
SmallVector<std::string, 8> Exp;
Exp.push_back("_ZGVnN2v_g(custom_vg)");
Exp.push_back("_ZGVnN4v_g");
EXPECT_EQ(Mappings, Exp);
}
static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
if (!Mod)
Err.print("VectorFunctionABITests", errs());
return Mod;
}
TEST(VFABIGetMappingsTest, IndirectCallInst) {
LLVMContext C;
std::unique_ptr<Module> M = parseIR(C, R"IR(
define void @call(void () * %f) {
entry:
call void %f()
ret void
}
)IR");
auto *F = dyn_cast_or_null<Function>(M->getNamedValue("call"));
ASSERT_TRUE(F);
auto *CI = dyn_cast<CallInst>(&F->front().front());
ASSERT_TRUE(CI);
ASSERT_TRUE(CI->isIndirectCall());
auto Mappings = VFDatabase::getMappings(*CI);
EXPECT_EQ(Mappings.size(), (unsigned)0);
}