blob: cdfb9fbfaa084d3b597860f6774426d8dace9fa5 [file] [log] [blame]
//===- VFABIDemangler.cpp - Vector Function ABI demangler -----------------===//
//
// 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/IR/VFABIDemangler.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <limits>
using namespace llvm;
#define DEBUG_TYPE "vfabi-demangler"
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 `<isa>` information from the mangled string, and
/// sets the `ISA` accordingly. If successful, the <isa> token is removed
/// from the input string `MangledName`.
static ParseRet tryParseISA(StringRef &MangledName, VFISAKind &ISA) {
if (MangledName.empty())
return ParseRet::Error;
if (MangledName.consume_front(VFABI::_LLVM_)) {
ISA = VFISAKind::LLVM;
} else {
ISA = StringSwitch<VFISAKind>(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 `<mask>` information from the mangled string, and
/// sets `IsMasked` accordingly. If successful, the <mask> token is removed
/// from the input string `MangledName`.
static 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 `<vlen>` information from the mangled string, and
/// sets `ParsedVF` accordingly. A `<vlen> == "x"` token is interpreted as a
/// scalable vector length and the boolean is set to true, otherwise a nonzero
/// unsigned integer will be directly used as a VF. On success, the `<vlen>`
/// token is removed from the input string `ParseString`.
static ParseRet tryParseVLEN(StringRef &ParseString, VFISAKind ISA,
std::pair<unsigned, bool> &ParsedVF) {
if (ParseString.consume_front("x")) {
// SVE is the only scalable ISA currently supported.
if (ISA != VFISAKind::SVE) {
LLVM_DEBUG(dbgs() << "Vector function variant declared with scalable VF "
<< "but ISA is not SVE\n");
return ParseRet::Error;
}
// We can't determine the VF of a scalable vector by looking at the vlen
// string (just 'x'), so say we successfully parsed it but return a 'true'
// for the scalable field with an invalid VF field so that we know to look
// up the actual VF based on element types from the parameters or return.
ParsedVF = {0, true};
return ParseRet::OK;
}
unsigned VF = 0;
if (ParseString.consumeInteger(10, VF))
return ParseRet::Error;
// The token `0` is invalid for VLEN.
if (VF == 0)
return ParseRet::Error;
ParsedVF = {VF, false};
return ParseRet::OK;
}
/// The function looks for the following strings at the beginning of
/// the input string `ParseString`:
///
/// <token> <number>
///
/// On success, it removes the parsed parameter from `ParseString`,
/// sets `PKind` to the correspondent enum value, sets `Pos` to
/// <number>, and return success. On a syntax error, it return a
/// parsing error. If nothing is parsed, it returns std::nullopt.
///
/// The function expects <token> to be one of "ls", "Rs", "Us" or
/// "Ls".
static 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 string at the beginning of
/// the input string `ParseString`:
///
/// <token> <number>
///
/// <token> 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
/// <number>, and return success. On a syntax error, it return a
/// parsing error. If nothing is parsed, it returns std::nullopt.
static ParseRet tryParseLinearWithRuntimeStep(StringRef &ParseString,
VFParamKind &PKind,
int &StepOrPos) {
ParseRet Ret;
// "ls" <RuntimeStepPos>
Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "ls");
if (Ret != ParseRet::None)
return Ret;
// "Rs" <RuntimeStepPos>
Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Rs");
if (Ret != ParseRet::None)
return Ret;
// "Ls" <RuntimeStepPos>
Ret = tryParseLinearTokenWithRuntimeStep(ParseString, PKind, StepOrPos, "Ls");
if (Ret != ParseRet::None)
return Ret;
// "Us" <RuntimeStepPos>
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`:
///
/// <token> {"n"} <number>
///
/// On success, it removes the parsed parameter from `ParseString`,
/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
/// <number>, and return success. On a syntax error, it return a
/// parsing error. If nothing is parsed, it returns std::nullopt.
///
/// The function expects <token> to be one of "l", "R", "U" or
/// "L".
static 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"} <number>
///
/// On success, it removes the parsed parameter from `ParseString`,
/// sets `PKind` to the correspondent enum value, sets `LinearStep` to
/// <number>, and return success. On a syntax error, it return a
/// parsing error. If nothing is parsed, it returns std::nullopt.
static ParseRet tryParseLinearWithCompileTimeStep(StringRef &ParseString,
VFParamKind &PKind,
int &StepOrPos) {
// "l" {"n"} <CompileTimeStep>
if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "l") ==
ParseRet::OK)
return ParseRet::OK;
// "R" {"n"} <CompileTimeStep>
if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "R") ==
ParseRet::OK)
return ParseRet::OK;
// "L" {"n"} <CompileTimeStep>
if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "L") ==
ParseRet::OK)
return ParseRet::OK;
// "U" {"n"} <CompileTimeStep>
if (tryParseCompileTimeLinearToken(ParseString, PKind, StepOrPos, "U") ==
ParseRet::OK)
return ParseRet::OK;
return ParseRet::None;
}
/// Looks into the <parameters> 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 std::nullopt.
static ParseRet tryParseParameter(StringRef &ParseString, VFParamKind &PKind,
int &StepOrPos) {
if (ParseString.consume_front("v")) {
PKind = VFParamKind::Vector;
StepOrPos = 0;
return ParseRet::OK;
}
if (ParseString.consume_front("u")) {
PKind = VFParamKind::OMP_Uniform;
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;
return ParseRet::None;
}
/// Looks into the <parameters> 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 std::nullopt.
static ParseRet tryParseAlign(StringRef &ParseString, Align &Alignment) {
uint64_t Val;
// "a" <number>
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;
}
// Returns the 'natural' VF for a given scalar element type, based on the
// current architecture.
//
// For SVE (currently the only scalable architecture with a defined name
// mangling), we assume a minimum vector size of 128b and return a VF based on
// the number of elements of the given type which would fit in such a vector.
static std::optional<ElementCount> getElementCountForTy(const VFISAKind ISA,
const Type *Ty) {
// Only AArch64 SVE is supported at present.
assert(ISA == VFISAKind::SVE &&
"Scalable VF decoding only implemented for SVE\n");
if (Ty->isIntegerTy(64) || Ty->isDoubleTy() || Ty->isPointerTy())
return ElementCount::getScalable(2);
if (Ty->isIntegerTy(32) || Ty->isFloatTy())
return ElementCount::getScalable(4);
if (Ty->isIntegerTy(16) || Ty->is16bitFPTy())
return ElementCount::getScalable(8);
if (Ty->isIntegerTy(8))
return ElementCount::getScalable(16);
return std::nullopt;
}
// Extract the VectorizationFactor from a given function signature, based
// on the widest scalar element types that will become vector parameters.
static std::optional<ElementCount>
getScalableECFromSignature(const FunctionType *Signature, const VFISAKind ISA,
const SmallVectorImpl<VFParameter> &Params) {
// Start with a very wide EC and drop when we find smaller ECs based on type.
ElementCount MinEC =
ElementCount::getScalable(std::numeric_limits<unsigned int>::max());
for (auto &Param : Params) {
// Only vector parameters are used when determining the VF; uniform or
// linear are left as scalars, so do not affect VF.
if (Param.ParamKind == VFParamKind::Vector) {
Type *PTy = Signature->getParamType(Param.ParamPos);
std::optional<ElementCount> EC = getElementCountForTy(ISA, PTy);
// If we have an unknown scalar element type we can't find a reasonable
// VF.
if (!EC)
return std::nullopt;
// Find the smallest VF, based on the widest scalar type.
if (ElementCount::isKnownLT(*EC, MinEC))
MinEC = *EC;
}
}
// Also check the return type if not void.
Type *RetTy = Signature->getReturnType();
if (!RetTy->isVoidTy()) {
std::optional<ElementCount> ReturnEC = getElementCountForTy(ISA, RetTy);
// If we have an unknown scalar element type we can't find a reasonable VF.
if (!ReturnEC)
return std::nullopt;
if (ElementCount::isKnownLT(*ReturnEC, MinEC))
MinEC = *ReturnEC;
}
// The SVE Vector function call ABI bases the VF on the widest element types
// present, and vector arguments containing types of that width are always
// considered to be packed. Arguments with narrower elements are considered
// to be unpacked.
if (MinEC.getKnownMinValue() < std::numeric_limits<unsigned int>::max())
return MinEC;
return std::nullopt;
}
} // namespace
// Format of the ABI name:
// _ZGV<isa><mask><vlen><parameters>_<scalarname>[(<redirection>)]
std::optional<VFInfo> VFABI::tryDemangleForVFABI(StringRef MangledName,
const FunctionType *FTy) {
const StringRef OriginalName = MangledName;
// Assume there is no custom name <redirection>, and therefore the
// vector name consists of
// _ZGV<isa><mask><vlen><parameters>_<scalarname>.
StringRef VectorName = MangledName;
// Parse the fixed size part of the mangled name
if (!MangledName.consume_front("_ZGV"))
return std::nullopt;
// Extract ISA. An unknow ISA is also supported, so we accept all
// values.
VFISAKind ISA;
if (tryParseISA(MangledName, ISA) != ParseRet::OK)
return std::nullopt;
// Extract <mask>.
bool IsMasked;
if (tryParseMask(MangledName, IsMasked) != ParseRet::OK)
return std::nullopt;
// Parse the variable size, starting from <vlen>.
std::pair<unsigned, bool> ParsedVF;
if (tryParseVLEN(MangledName, ISA, ParsedVF) != ParseRet::OK)
return std::nullopt;
// Parse the <parameters>.
ParseRet ParamFound;
SmallVector<VFParameter, 8> Parameters;
do {
const unsigned ParameterPos = 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 std::nullopt;
if (ParamFound == ParseRet::OK) {
Align Alignment;
// Look for the alignment token "a <number>".
const ParseRet AlignFound = tryParseAlign(MangledName, Alignment);
// Bail off if there is a syntax error in the align token.
if (AlignFound == ParseRet::Error)
return std::nullopt;
// Add the parameter.
Parameters.push_back({ParameterPos, PKind, StepOrPos, Alignment});
}
} while (ParamFound == ParseRet::OK);
// A valid MangledName must have at least one valid entry in the
// <parameters>.
if (Parameters.empty())
return std::nullopt;
// If the number of arguments of the scalar function does not match the
// vector variant we have just demangled then reject the mapping.
if (Parameters.size() != FTy->getNumParams())
return std::nullopt;
// Figure out the number of lanes in vectors for this function variant. This
// is easy for fixed length, as the vlen encoding just gives us the value
// directly. However, if the vlen mangling indicated that this function
// variant expects scalable vectors we need to work it out based on the
// demangled parameter types and the scalar function signature.
std::optional<ElementCount> EC;
if (ParsedVF.second) {
EC = getScalableECFromSignature(FTy, ISA, Parameters);
if (!EC)
return std::nullopt;
} else
EC = ElementCount::getFixed(ParsedVF.first);
// Check for the <scalarname> and the optional <redirection>, which
// are separated from the prefix with "_"
if (!MangledName.consume_front("_"))
return std::nullopt;
// The rest of the string must be in the format:
// <scalarname>[(<redirection>)]
const StringRef ScalarName =
MangledName.take_while([](char In) { return In != '('; });
if (ScalarName.empty())
return std::nullopt;
// Reduce MangledName to [(<redirection>)].
MangledName = MangledName.ltrim(ScalarName);
// Find the optional custom name redirection.
if (MangledName.consume_front("(")) {
if (!MangledName.consume_back(")"))
return std::nullopt;
// Update the vector variant with the one specified by the user.
VectorName = MangledName;
// If the vector name is missing, bail out.
if (VectorName.empty())
return std::nullopt;
}
// LLVM internal mapping via the TargetLibraryInfo (TLI) must be
// redirected to an existing name.
if (ISA == VFISAKind::LLVM && VectorName == OriginalName)
return std::nullopt;
// When <mask> is "M", we need to add a parameter that is used as
// global predicate for the function.
if (IsMasked) {
const unsigned Pos = Parameters.size();
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 =
llvm::count_if(Parameters, [](const VFParameter &PK) {
return PK.ParamKind == VFParamKind::GlobalPredicate;
});
assert(NGlobalPreds < 2 && "Cannot have more than one global predicate.");
if (NGlobalPreds)
assert(Parameters.back().ParamKind == VFParamKind::GlobalPredicate &&
"The global predicate must be the last parameter");
const VFShape Shape({*EC, Parameters});
return VFInfo({Shape, std::string(ScalarName), std::string(VectorName), ISA});
}
VFParamKind VFABI::getVFParamKindFromString(const StringRef Token) {
const VFParamKind ParamKind = StringSwitch<VFParamKind>(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");
}
void VFABI::getVectorVariantNames(
const CallInst &CI, SmallVectorImpl<std::string> &VariantMappings) {
const StringRef S = CI.getFnAttr(VFABI::MappingsAttrName).getValueAsString();
if (S.empty())
return;
SmallVector<StringRef, 8> ListAttr;
S.split(ListAttr, ",");
for (const auto &S : SetVector<StringRef>(ListAttr.begin(), ListAttr.end())) {
std::optional<VFInfo> Info =
VFABI::tryDemangleForVFABI(S, CI.getFunctionType());
if (Info && CI.getModule()->getFunction(Info->VectorName)) {
LLVM_DEBUG(dbgs() << "VFABI: Adding mapping '" << S << "' for " << CI
<< "\n");
VariantMappings.push_back(std::string(S));
} else
LLVM_DEBUG(dbgs() << "VFABI: Invalid mapping '" << S << "'\n");
}
}
FunctionType *VFABI::createFunctionType(const VFInfo &Info,
const FunctionType *ScalarFTy) {
// Create vector parameter types
SmallVector<Type *, 8> VecTypes;
ElementCount VF = Info.Shape.VF;
int ScalarParamIndex = 0;
for (auto VFParam : Info.Shape.Parameters) {
if (VFParam.ParamKind == VFParamKind::GlobalPredicate) {
VectorType *MaskTy =
VectorType::get(Type::getInt1Ty(ScalarFTy->getContext()), VF);
VecTypes.push_back(MaskTy);
continue;
}
Type *OperandTy = ScalarFTy->getParamType(ScalarParamIndex++);
if (VFParam.ParamKind == VFParamKind::Vector)
OperandTy = VectorType::get(OperandTy, VF);
VecTypes.push_back(OperandTy);
}
auto *RetTy = ScalarFTy->getReturnType();
if (!RetTy->isVoidTy())
RetTy = VectorType::get(RetTy, VF);
return FunctionType::get(RetTy, VecTypes, false);
}
void VFABI::setVectorVariantNames(CallInst *CI,
ArrayRef<std::string> VariantMappings) {
if (VariantMappings.empty())
return;
SmallString<256> Buffer;
llvm::raw_svector_ostream Out(Buffer);
for (const std::string &VariantMapping : VariantMappings)
Out << VariantMapping << ",";
// Get rid of the trailing ','.
assert(!Buffer.str().empty() && "Must have at least one char.");
Buffer.pop_back();
Module *M = CI->getModule();
#ifndef NDEBUG
for (const std::string &VariantMapping : VariantMappings) {
LLVM_DEBUG(dbgs() << "VFABI: adding mapping '" << VariantMapping << "'\n");
std::optional<VFInfo> VI =
VFABI::tryDemangleForVFABI(VariantMapping, CI->getFunctionType());
assert(VI && "Cannot add an invalid VFABI name.");
assert(M->getNamedValue(VI->VectorName) &&
"Cannot add variant to attribute: "
"vector function declaration is missing.");
}
#endif
CI->addFnAttr(
Attribute::get(M->getContext(), MappingsAttrName, Buffer.str()));
}
bool VFShape::hasValidParameterList() const {
for (unsigned Pos = 0, NumParams = Parameters.size(); Pos < NumParams;
++Pos) {
assert(Parameters[Pos].ParamPos == Pos && "Broken parameter list.");
switch (Parameters[Pos].ParamKind) {
default: // Nothing to check.
break;
case VFParamKind::OMP_Linear:
case VFParamKind::OMP_LinearRef:
case VFParamKind::OMP_LinearVal:
case VFParamKind::OMP_LinearUVal:
// Compile time linear steps must be non-zero.
if (Parameters[Pos].LinearStepOrPos == 0)
return false;
break;
case VFParamKind::OMP_LinearPos:
case VFParamKind::OMP_LinearRefPos:
case VFParamKind::OMP_LinearValPos:
case VFParamKind::OMP_LinearUValPos:
// The runtime linear step must be referring to some other
// parameters in the signature.
if (Parameters[Pos].LinearStepOrPos >= int(NumParams))
return false;
// The linear step parameter must be marked as uniform.
if (Parameters[Parameters[Pos].LinearStepOrPos].ParamKind !=
VFParamKind::OMP_Uniform)
return false;
// The linear step parameter can't point at itself.
if (Parameters[Pos].LinearStepOrPos == int(Pos))
return false;
break;
case VFParamKind::GlobalPredicate:
// The global predicate must be the unique. Can be placed anywhere in the
// signature.
for (unsigned NextPos = Pos + 1; NextPos < NumParams; ++NextPos)
if (Parameters[NextPos].ParamKind == VFParamKind::GlobalPredicate)
return false;
break;
}
}
return true;
}