Path: blob/main/contrib/llvm-project/clang/utils/TableGen/MveEmitter.cpp
35230 views
//===- MveEmitter.cpp - Generate arm_mve.h for use with clang -*- C++ -*-=====//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This set of linked tablegen backends is responsible for emitting the bits9// and pieces that implement <arm_mve.h>, which is defined by the ACLE standard10// and provides a set of types and functions for (more or less) direct access11// to the MVE instruction set, including the scalar shifts as well as the12// vector instructions.13//14// MVE's standard intrinsic functions are unusual in that they have a system of15// polymorphism. For example, the function vaddq() can behave like vaddq_u16(),16// vaddq_f32(), vaddq_s8(), etc., depending on the types of the vector17// arguments you give it.18//19// This constrains the implementation strategies. The usual approach to making20// the user-facing functions polymorphic would be to either use21// __attribute__((overloadable)) to make a set of vaddq() functions that are22// all inline wrappers on the underlying clang builtins, or to define a single23// vaddq() macro which expands to an instance of _Generic.24//25// The inline-wrappers approach would work fine for most intrinsics, except for26// the ones that take an argument required to be a compile-time constant,27// because if you wrap an inline function around a call to a builtin, the28// constant nature of the argument is not passed through.29//30// The _Generic approach can be made to work with enough effort, but it takes a31// lot of machinery, because of the design feature of _Generic that even the32// untaken branches are required to pass all front-end validity checks such as33// type-correctness. You can work around that by nesting further _Generics all34// over the place to coerce things to the right type in untaken branches, but35// what you get out is complicated, hard to guarantee its correctness, and36// worst of all, gives _completely unreadable_ error messages if the user gets37// the types wrong for an intrinsic call.38//39// Therefore, my strategy is to introduce a new __attribute__ that allows a40// function to be mapped to a clang builtin even though it doesn't have the41// same name, and then declare all the user-facing MVE function names with that42// attribute, mapping each one directly to the clang builtin. And the43// polymorphic ones have __attribute__((overloadable)) as well. So once the44// compiler has resolved the overload, it knows the internal builtin ID of the45// selected function, and can check the immediate arguments against that; and46// if the user gets the types wrong in a call to a polymorphic intrinsic, they47// get a completely clear error message showing all the declarations of that48// function in the header file and explaining why each one doesn't fit their49// call.50//51// The downside of this is that if every clang builtin has to correspond52// exactly to a user-facing ACLE intrinsic, then you can't save work in the53// frontend by doing it in the header file: CGBuiltin.cpp has to do the entire54// job of converting an ACLE intrinsic call into LLVM IR. So the Tablegen55// description for an MVE intrinsic has to contain a full description of the56// sequence of IRBuilder calls that clang will need to make.57//58//===----------------------------------------------------------------------===//5960#include "llvm/ADT/APInt.h"61#include "llvm/ADT/StringRef.h"62#include "llvm/ADT/StringSwitch.h"63#include "llvm/Support/Casting.h"64#include "llvm/Support/raw_ostream.h"65#include "llvm/TableGen/Error.h"66#include "llvm/TableGen/Record.h"67#include "llvm/TableGen/StringToOffsetTable.h"68#include <cassert>69#include <cstddef>70#include <cstdint>71#include <list>72#include <map>73#include <memory>74#include <set>75#include <string>76#include <vector>7778using namespace llvm;7980namespace {8182class EmitterBase;83class Result;8485// -----------------------------------------------------------------------------86// A system of classes to represent all the types we'll need to deal with in87// the prototypes of intrinsics.88//89// Query methods include finding out the C name of a type; the "LLVM name" in90// the sense of a C++ code snippet that can be used in the codegen function;91// the suffix that represents the type in the ACLE intrinsic naming scheme92// (e.g. 's32' represents int32_t in intrinsics such as vaddq_s32); whether the93// type is floating-point related (hence should be under #ifdef in the MVE94// header so that it isn't included in integer-only MVE mode); and the type's95// size in bits. Not all subtypes support all these queries.9697class Type {98public:99enum class TypeKind {100// Void appears as a return type (for store intrinsics, which are pure101// side-effect). It's also used as the parameter type in the Tablegen102// when an intrinsic doesn't need to come in various suffixed forms like103// vfooq_s8,vfooq_u16,vfooq_f32.104Void,105106// Scalar is used for ordinary int and float types of all sizes.107Scalar,108109// Vector is used for anything that occupies exactly one MVE vector110// register, i.e. {uint,int,float}NxM_t.111Vector,112113// MultiVector is used for the {uint,int,float}NxMxK_t types used by the114// interleaving load/store intrinsics v{ld,st}{2,4}q.115MultiVector,116117// Predicate is used by all the predicated intrinsics. Its C118// representation is mve_pred16_t (which is just an alias for uint16_t).119// But we give more detail here, by indicating that a given predicate120// instruction is logically regarded as a vector of i1 containing the121// same number of lanes as the input vector type. So our Predicate type122// comes with a lane count, which we use to decide which kind of <n x i1>123// we'll invoke the pred_i2v IR intrinsic to translate it into.124Predicate,125126// Pointer is used for pointer types (obviously), and comes with a flag127// indicating whether it's a pointer to a const or mutable instance of128// the pointee type.129Pointer,130};131132private:133const TypeKind TKind;134135protected:136Type(TypeKind K) : TKind(K) {}137138public:139TypeKind typeKind() const { return TKind; }140virtual ~Type() = default;141virtual bool requiresFloat() const = 0;142virtual bool requiresMVE() const = 0;143virtual unsigned sizeInBits() const = 0;144virtual std::string cName() const = 0;145virtual std::string llvmName() const {146PrintFatalError("no LLVM type name available for type " + cName());147}148virtual std::string acleSuffix(std::string) const {149PrintFatalError("no ACLE suffix available for this type");150}151};152153enum class ScalarTypeKind { SignedInt, UnsignedInt, Float };154inline std::string toLetter(ScalarTypeKind kind) {155switch (kind) {156case ScalarTypeKind::SignedInt:157return "s";158case ScalarTypeKind::UnsignedInt:159return "u";160case ScalarTypeKind::Float:161return "f";162}163llvm_unreachable("Unhandled ScalarTypeKind enum");164}165inline std::string toCPrefix(ScalarTypeKind kind) {166switch (kind) {167case ScalarTypeKind::SignedInt:168return "int";169case ScalarTypeKind::UnsignedInt:170return "uint";171case ScalarTypeKind::Float:172return "float";173}174llvm_unreachable("Unhandled ScalarTypeKind enum");175}176177class VoidType : public Type {178public:179VoidType() : Type(TypeKind::Void) {}180unsigned sizeInBits() const override { return 0; }181bool requiresFloat() const override { return false; }182bool requiresMVE() const override { return false; }183std::string cName() const override { return "void"; }184185static bool classof(const Type *T) { return T->typeKind() == TypeKind::Void; }186std::string acleSuffix(std::string) const override { return ""; }187};188189class PointerType : public Type {190const Type *Pointee;191bool Const;192193public:194PointerType(const Type *Pointee, bool Const)195: Type(TypeKind::Pointer), Pointee(Pointee), Const(Const) {}196unsigned sizeInBits() const override { return 32; }197bool requiresFloat() const override { return Pointee->requiresFloat(); }198bool requiresMVE() const override { return Pointee->requiresMVE(); }199std::string cName() const override {200std::string Name = Pointee->cName();201202// The syntax for a pointer in C is different when the pointee is203// itself a pointer. The MVE intrinsics don't contain any double204// pointers, so we don't need to worry about that wrinkle.205assert(!isa<PointerType>(Pointee) && "Pointer to pointer not supported");206207if (Const)208Name = "const " + Name;209return Name + " *";210}211std::string llvmName() const override {212return "llvm::PointerType::getUnqual(" + Pointee->llvmName() + ")";213}214const Type *getPointeeType() const { return Pointee; }215216static bool classof(const Type *T) {217return T->typeKind() == TypeKind::Pointer;218}219};220221// Base class for all the types that have a name of the form222// [prefix][numbers]_t, like int32_t, uint16x8_t, float32x4x2_t.223//224// For this sub-hierarchy we invent a cNameBase() method which returns the225// whole name except for the trailing "_t", so that Vector and MultiVector can226// append an extra "x2" or whatever to their element type's cNameBase(). Then227// the main cName() query method puts "_t" on the end for the final type name.228229class CRegularNamedType : public Type {230using Type::Type;231virtual std::string cNameBase() const = 0;232233public:234std::string cName() const override { return cNameBase() + "_t"; }235};236237class ScalarType : public CRegularNamedType {238ScalarTypeKind Kind;239unsigned Bits;240std::string NameOverride;241242public:243ScalarType(const Record *Record) : CRegularNamedType(TypeKind::Scalar) {244Kind = StringSwitch<ScalarTypeKind>(Record->getValueAsString("kind"))245.Case("s", ScalarTypeKind::SignedInt)246.Case("u", ScalarTypeKind::UnsignedInt)247.Case("f", ScalarTypeKind::Float);248Bits = Record->getValueAsInt("size");249NameOverride = std::string(Record->getValueAsString("nameOverride"));250}251unsigned sizeInBits() const override { return Bits; }252ScalarTypeKind kind() const { return Kind; }253std::string suffix() const { return toLetter(Kind) + utostr(Bits); }254std::string cNameBase() const override {255return toCPrefix(Kind) + utostr(Bits);256}257std::string cName() const override {258if (NameOverride.empty())259return CRegularNamedType::cName();260return NameOverride;261}262std::string llvmName() const override {263if (Kind == ScalarTypeKind::Float) {264if (Bits == 16)265return "HalfTy";266if (Bits == 32)267return "FloatTy";268if (Bits == 64)269return "DoubleTy";270PrintFatalError("bad size for floating type");271}272return "Int" + utostr(Bits) + "Ty";273}274std::string acleSuffix(std::string overrideLetter) const override {275return "_" + (overrideLetter.size() ? overrideLetter : toLetter(Kind))276+ utostr(Bits);277}278bool isInteger() const { return Kind != ScalarTypeKind::Float; }279bool requiresFloat() const override { return !isInteger(); }280bool requiresMVE() const override { return false; }281bool hasNonstandardName() const { return !NameOverride.empty(); }282283static bool classof(const Type *T) {284return T->typeKind() == TypeKind::Scalar;285}286};287288class VectorType : public CRegularNamedType {289const ScalarType *Element;290unsigned Lanes;291292public:293VectorType(const ScalarType *Element, unsigned Lanes)294: CRegularNamedType(TypeKind::Vector), Element(Element), Lanes(Lanes) {}295unsigned sizeInBits() const override { return Lanes * Element->sizeInBits(); }296unsigned lanes() const { return Lanes; }297bool requiresFloat() const override { return Element->requiresFloat(); }298bool requiresMVE() const override { return true; }299std::string cNameBase() const override {300return Element->cNameBase() + "x" + utostr(Lanes);301}302std::string llvmName() const override {303return "llvm::FixedVectorType::get(" + Element->llvmName() + ", " +304utostr(Lanes) + ")";305}306307static bool classof(const Type *T) {308return T->typeKind() == TypeKind::Vector;309}310};311312class MultiVectorType : public CRegularNamedType {313const VectorType *Element;314unsigned Registers;315316public:317MultiVectorType(unsigned Registers, const VectorType *Element)318: CRegularNamedType(TypeKind::MultiVector), Element(Element),319Registers(Registers) {}320unsigned sizeInBits() const override {321return Registers * Element->sizeInBits();322}323unsigned registers() const { return Registers; }324bool requiresFloat() const override { return Element->requiresFloat(); }325bool requiresMVE() const override { return true; }326std::string cNameBase() const override {327return Element->cNameBase() + "x" + utostr(Registers);328}329330// MultiVectorType doesn't override llvmName, because we don't expect to do331// automatic code generation for the MVE intrinsics that use it: the {vld2,332// vld4, vst2, vst4} family are the only ones that use these types, so it was333// easier to hand-write the codegen for dealing with these structs than to334// build in lots of extra automatic machinery that would only be used once.335336static bool classof(const Type *T) {337return T->typeKind() == TypeKind::MultiVector;338}339};340341class PredicateType : public CRegularNamedType {342unsigned Lanes;343344public:345PredicateType(unsigned Lanes)346: CRegularNamedType(TypeKind::Predicate), Lanes(Lanes) {}347unsigned sizeInBits() const override { return 16; }348std::string cNameBase() const override { return "mve_pred16"; }349bool requiresFloat() const override { return false; };350bool requiresMVE() const override { return true; }351std::string llvmName() const override {352return "llvm::FixedVectorType::get(Builder.getInt1Ty(), " + utostr(Lanes) +353")";354}355356static bool classof(const Type *T) {357return T->typeKind() == TypeKind::Predicate;358}359};360361// -----------------------------------------------------------------------------362// Class to facilitate merging together the code generation for many intrinsics363// by means of varying a few constant or type parameters.364//365// Most obviously, the intrinsics in a single parametrised family will have366// code generation sequences that only differ in a type or two, e.g. vaddq_s8367// and vaddq_u16 will look the same apart from putting a different vector type368// in the call to CGM.getIntrinsic(). But also, completely different intrinsics369// will often code-generate in the same way, with only a different choice of370// _which_ IR intrinsic they lower to (e.g. vaddq_m_s8 and vmulq_m_s8), but371// marshalling the arguments and return values of the IR intrinsic in exactly372// the same way. And others might differ only in some other kind of constant,373// such as a lane index.374//375// So, when we generate the IR-building code for all these intrinsics, we keep376// track of every value that could possibly be pulled out of the code and377// stored ahead of time in a local variable. Then we group together intrinsics378// by textual equivalence of the code that would result if _all_ those379// parameters were stored in local variables. That gives us maximal sets that380// can be implemented by a single piece of IR-building code by changing381// parameter values ahead of time.382//383// After we've done that, we do a second pass in which we only allocate _some_384// of the parameters into local variables, by tracking which ones have the same385// values as each other (so that a single variable can be reused) and which386// ones are the same across the whole set (so that no variable is needed at387// all).388//389// Hence the class below. Its allocParam method is invoked during code390// generation by every method of a Result subclass (see below) that wants to391// give it the opportunity to pull something out into a switchable parameter.392// It returns a variable name for the parameter, or (if it's being used in the393// second pass once we've decided that some parameters don't need to be stored394// in variables after all) it might just return the input expression unchanged.395396struct CodeGenParamAllocator {397// Accumulated during code generation398std::vector<std::string> *ParamTypes = nullptr;399std::vector<std::string> *ParamValues = nullptr;400401// Provided ahead of time in pass 2, to indicate which parameters are being402// assigned to what. This vector contains an entry for each call to403// allocParam expected during code gen (which we counted up in pass 1), and404// indicates the number of the parameter variable that should be returned, or405// -1 if this call shouldn't allocate a parameter variable at all.406//407// We rely on the recursive code generation working identically in passes 1408// and 2, so that the same list of calls to allocParam happen in the same409// order. That guarantees that the parameter numbers recorded in pass 1 will410// match the entries in this vector that store what EmitterBase::EmitBuiltinCG411// decided to do about each one in pass 2.412std::vector<int> *ParamNumberMap = nullptr;413414// Internally track how many things we've allocated415unsigned nparams = 0;416417std::string allocParam(StringRef Type, StringRef Value) {418unsigned ParamNumber;419420if (!ParamNumberMap) {421// In pass 1, unconditionally assign a new parameter variable to every422// value we're asked to process.423ParamNumber = nparams++;424} else {425// In pass 2, consult the map provided by the caller to find out which426// variable we should be keeping things in.427int MapValue = (*ParamNumberMap)[nparams++];428if (MapValue < 0)429return std::string(Value);430ParamNumber = MapValue;431}432433// If we've allocated a new parameter variable for the first time, store434// its type and value to be retrieved after codegen.435if (ParamTypes && ParamTypes->size() == ParamNumber)436ParamTypes->push_back(std::string(Type));437if (ParamValues && ParamValues->size() == ParamNumber)438ParamValues->push_back(std::string(Value));439440// Unimaginative naming scheme for parameter variables.441return "Param" + utostr(ParamNumber);442}443};444445// -----------------------------------------------------------------------------446// System of classes that represent all the intermediate values used during447// code-generation for an intrinsic.448//449// The base class 'Result' can represent a value of the LLVM type 'Value', or450// sometimes 'Address' (for loads/stores, including an alignment requirement).451//452// In the case where the Tablegen provides a value in the codegen dag as a453// plain integer literal, the Result object we construct here will be one that454// returns true from hasIntegerConstantValue(). This allows the generated C++455// code to use the constant directly in contexts which can take a literal456// integer, such as Builder.CreateExtractValue(thing, 1), without going to the457// effort of calling llvm::ConstantInt::get() and then pulling the constant458// back out of the resulting llvm:Value later.459460class Result {461public:462// Convenient shorthand for the pointer type we'll be using everywhere.463using Ptr = std::shared_ptr<Result>;464465private:466Ptr Predecessor;467std::string VarName;468bool VarNameUsed = false;469unsigned Visited = 0;470471public:472virtual ~Result() = default;473using Scope = std::map<std::string, Ptr>;474virtual void genCode(raw_ostream &OS, CodeGenParamAllocator &) const = 0;475virtual bool hasIntegerConstantValue() const { return false; }476virtual uint32_t integerConstantValue() const { return 0; }477virtual bool hasIntegerValue() const { return false; }478virtual std::string getIntegerValue(const std::string &) {479llvm_unreachable("non-working Result::getIntegerValue called");480}481virtual std::string typeName() const { return "Value *"; }482483// Mostly, when a code-generation operation has a dependency on prior484// operations, it's because it uses the output values of those operations as485// inputs. But there's one exception, which is the use of 'seq' in Tablegen486// to indicate that operations have to be performed in sequence regardless of487// whether they use each others' output values.488//489// So, the actual generation of code is done by depth-first search, using the490// prerequisites() method to get a list of all the other Results that have to491// be computed before this one. That method divides into the 'predecessor',492// set by setPredecessor() while processing a 'seq' dag node, and the list493// returned by 'morePrerequisites', which each subclass implements to return494// a list of the Results it uses as input to whatever its own computation is495// doing.496497virtual void morePrerequisites(std::vector<Ptr> &output) const {}498std::vector<Ptr> prerequisites() const {499std::vector<Ptr> ToRet;500if (Predecessor)501ToRet.push_back(Predecessor);502morePrerequisites(ToRet);503return ToRet;504}505506void setPredecessor(Ptr p) {507// If the user has nested one 'seq' node inside another, and this508// method is called on the return value of the inner 'seq' (i.e.509// the final item inside it), then we can't link _this_ node to p,510// because it already has a predecessor. Instead, walk the chain511// until we find the first item in the inner seq, and link that to512// p, so that nesting seqs has the obvious effect of linking513// everything together into one long sequential chain.514Result *r = this;515while (r->Predecessor)516r = r->Predecessor.get();517r->Predecessor = p;518}519520// Each Result will be assigned a variable name in the output code, but not521// all those variable names will actually be used (e.g. the return value of522// Builder.CreateStore has void type, so nobody will want to refer to it). To523// prevent annoying compiler warnings, we track whether each Result's524// variable name was ever actually mentioned in subsequent statements, so525// that it can be left out of the final generated code.526std::string varname() {527VarNameUsed = true;528return VarName;529}530void setVarname(const StringRef s) { VarName = std::string(s); }531bool varnameUsed() const { return VarNameUsed; }532533// Emit code to generate this result as a Value *.534virtual std::string asValue() {535return varname();536}537538// Code generation happens in multiple passes. This method tracks whether a539// Result has yet been visited in a given pass, without the need for a540// tedious loop in between passes that goes through and resets a 'visited'541// flag back to false: you just set Pass=1 the first time round, and Pass=2542// the second time.543bool needsVisiting(unsigned Pass) {544bool ToRet = Visited < Pass;545Visited = Pass;546return ToRet;547}548};549550// Result subclass that retrieves one of the arguments to the clang builtin551// function. In cases where the argument has pointer type, we call552// EmitPointerWithAlignment and store the result in a variable of type Address,553// so that load and store IR nodes can know the right alignment. Otherwise, we554// call EmitScalarExpr.555//556// There are aggregate parameters in the MVE intrinsics API, but we don't deal557// with them in this Tablegen back end: they only arise in the vld2q/vld4q and558// vst2q/vst4q family, which is few enough that we just write the code by hand559// for those in CGBuiltin.cpp.560class BuiltinArgResult : public Result {561public:562unsigned ArgNum;563bool AddressType;564bool Immediate;565BuiltinArgResult(unsigned ArgNum, bool AddressType, bool Immediate)566: ArgNum(ArgNum), AddressType(AddressType), Immediate(Immediate) {}567void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {568OS << (AddressType ? "EmitPointerWithAlignment" : "EmitScalarExpr")569<< "(E->getArg(" << ArgNum << "))";570}571std::string typeName() const override {572return AddressType ? "Address" : Result::typeName();573}574// Emit code to generate this result as a Value *.575std::string asValue() override {576if (AddressType)577return "(" + varname() + ".emitRawPointer(*this))";578return Result::asValue();579}580bool hasIntegerValue() const override { return Immediate; }581std::string getIntegerValue(const std::string &IntType) override {582return "GetIntegerConstantValue<" + IntType + ">(E->getArg(" +583utostr(ArgNum) + "), getContext())";584}585};586587// Result subclass for an integer literal appearing in Tablegen. This may need588// to be turned into an llvm::Result by means of llvm::ConstantInt::get(), or589// it may be used directly as an integer, depending on which IRBuilder method590// it's being passed to.591class IntLiteralResult : public Result {592public:593const ScalarType *IntegerType;594uint32_t IntegerValue;595IntLiteralResult(const ScalarType *IntegerType, uint32_t IntegerValue)596: IntegerType(IntegerType), IntegerValue(IntegerValue) {}597void genCode(raw_ostream &OS,598CodeGenParamAllocator &ParamAlloc) const override {599OS << "llvm::ConstantInt::get("600<< ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName())601<< ", ";602OS << ParamAlloc.allocParam(IntegerType->cName(), utostr(IntegerValue))603<< ")";604}605bool hasIntegerConstantValue() const override { return true; }606uint32_t integerConstantValue() const override { return IntegerValue; }607};608609// Result subclass representing a cast between different integer types. We use610// our own ScalarType abstraction as the representation of the target type,611// which gives both size and signedness.612class IntCastResult : public Result {613public:614const ScalarType *IntegerType;615Ptr V;616IntCastResult(const ScalarType *IntegerType, Ptr V)617: IntegerType(IntegerType), V(V) {}618void genCode(raw_ostream &OS,619CodeGenParamAllocator &ParamAlloc) const override {620OS << "Builder.CreateIntCast(" << V->varname() << ", "621<< ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName()) << ", "622<< ParamAlloc.allocParam("bool",623IntegerType->kind() == ScalarTypeKind::SignedInt624? "true"625: "false")626<< ")";627}628void morePrerequisites(std::vector<Ptr> &output) const override {629output.push_back(V);630}631};632633// Result subclass representing a cast between different pointer types.634class PointerCastResult : public Result {635public:636const PointerType *PtrType;637Ptr V;638PointerCastResult(const PointerType *PtrType, Ptr V)639: PtrType(PtrType), V(V) {}640void genCode(raw_ostream &OS,641CodeGenParamAllocator &ParamAlloc) const override {642OS << "Builder.CreatePointerCast(" << V->asValue() << ", "643<< ParamAlloc.allocParam("llvm::Type *", PtrType->llvmName()) << ")";644}645void morePrerequisites(std::vector<Ptr> &output) const override {646output.push_back(V);647}648};649650// Result subclass representing a call to an IRBuilder method. Each IRBuilder651// method we want to use will have a Tablegen record giving the method name and652// describing any important details of how to call it, such as whether a653// particular argument should be an integer constant instead of an llvm::Value.654class IRBuilderResult : public Result {655public:656StringRef CallPrefix;657std::vector<Ptr> Args;658std::set<unsigned> AddressArgs;659std::map<unsigned, std::string> IntegerArgs;660IRBuilderResult(StringRef CallPrefix, const std::vector<Ptr> &Args,661const std::set<unsigned> &AddressArgs,662const std::map<unsigned, std::string> &IntegerArgs)663: CallPrefix(CallPrefix), Args(Args), AddressArgs(AddressArgs),664IntegerArgs(IntegerArgs) {}665void genCode(raw_ostream &OS,666CodeGenParamAllocator &ParamAlloc) const override {667OS << CallPrefix;668const char *Sep = "";669for (unsigned i = 0, e = Args.size(); i < e; ++i) {670Ptr Arg = Args[i];671auto it = IntegerArgs.find(i);672673OS << Sep;674Sep = ", ";675676if (it != IntegerArgs.end()) {677if (Arg->hasIntegerConstantValue())678OS << "static_cast<" << it->second << ">("679<< ParamAlloc.allocParam(it->second,680utostr(Arg->integerConstantValue()))681<< ")";682else if (Arg->hasIntegerValue())683OS << ParamAlloc.allocParam(it->second,684Arg->getIntegerValue(it->second));685} else {686OS << Arg->varname();687}688}689OS << ")";690}691void morePrerequisites(std::vector<Ptr> &output) const override {692for (unsigned i = 0, e = Args.size(); i < e; ++i) {693Ptr Arg = Args[i];694if (IntegerArgs.find(i) != IntegerArgs.end())695continue;696output.push_back(Arg);697}698}699};700701// Result subclass representing making an Address out of a Value.702class AddressResult : public Result {703public:704Ptr Arg;705const Type *Ty;706unsigned Align;707AddressResult(Ptr Arg, const Type *Ty, unsigned Align)708: Arg(Arg), Ty(Ty), Align(Align) {}709void genCode(raw_ostream &OS,710CodeGenParamAllocator &ParamAlloc) const override {711OS << "Address(" << Arg->varname() << ", " << Ty->llvmName()712<< ", CharUnits::fromQuantity(" << Align << "))";713}714std::string typeName() const override {715return "Address";716}717void morePrerequisites(std::vector<Ptr> &output) const override {718output.push_back(Arg);719}720};721722// Result subclass representing a call to an IR intrinsic, which we first have723// to look up using an Intrinsic::ID constant and an array of types.724class IRIntrinsicResult : public Result {725public:726std::string IntrinsicID;727std::vector<const Type *> ParamTypes;728std::vector<Ptr> Args;729IRIntrinsicResult(StringRef IntrinsicID,730const std::vector<const Type *> &ParamTypes,731const std::vector<Ptr> &Args)732: IntrinsicID(std::string(IntrinsicID)), ParamTypes(ParamTypes),733Args(Args) {}734void genCode(raw_ostream &OS,735CodeGenParamAllocator &ParamAlloc) const override {736std::string IntNo = ParamAlloc.allocParam(737"Intrinsic::ID", "Intrinsic::" + IntrinsicID);738OS << "Builder.CreateCall(CGM.getIntrinsic(" << IntNo;739if (!ParamTypes.empty()) {740OS << ", {";741const char *Sep = "";742for (auto T : ParamTypes) {743OS << Sep << ParamAlloc.allocParam("llvm::Type *", T->llvmName());744Sep = ", ";745}746OS << "}";747}748OS << "), {";749const char *Sep = "";750for (auto Arg : Args) {751OS << Sep << Arg->asValue();752Sep = ", ";753}754OS << "})";755}756void morePrerequisites(std::vector<Ptr> &output) const override {757output.insert(output.end(), Args.begin(), Args.end());758}759};760761// Result subclass that specifies a type, for use in IRBuilder operations such762// as CreateBitCast that take a type argument.763class TypeResult : public Result {764public:765const Type *T;766TypeResult(const Type *T) : T(T) {}767void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {768OS << T->llvmName();769}770std::string typeName() const override {771return "llvm::Type *";772}773};774775// -----------------------------------------------------------------------------776// Class that describes a single ACLE intrinsic.777//778// A Tablegen record will typically describe more than one ACLE intrinsic, by779// means of setting the 'list<Type> Params' field to a list of multiple780// parameter types, so as to define vaddq_{s8,u8,...,f16,f32} all in one go.781// We'll end up with one instance of ACLEIntrinsic for *each* parameter type,782// rather than a single one for all of them. Hence, the constructor takes both783// a Tablegen record and the current value of the parameter type.784785class ACLEIntrinsic {786// Structure documenting that one of the intrinsic's arguments is required to787// be a compile-time constant integer, and what constraints there are on its788// value. Used when generating Sema checking code.789struct ImmediateArg {790enum class BoundsType { ExplicitRange, UInt };791BoundsType boundsType;792int64_t i1, i2;793StringRef ExtraCheckType, ExtraCheckArgs;794const Type *ArgType;795};796797// For polymorphic intrinsics, FullName is the explicit name that uniquely798// identifies this variant of the intrinsic, and ShortName is the name it799// shares with at least one other intrinsic.800std::string ShortName, FullName;801802// Name of the architecture extension, used in the Clang builtin name803StringRef BuiltinExtension;804805// A very small number of intrinsics _only_ have a polymorphic806// variant (vuninitializedq taking an unevaluated argument).807bool PolymorphicOnly;808809// Another rarely-used flag indicating that the builtin doesn't810// evaluate its argument(s) at all.811bool NonEvaluating;812813// True if the intrinsic needs only the C header part (no codegen, semantic814// checks, etc). Used for redeclaring MVE intrinsics in the arm_cde.h header.815bool HeaderOnly;816817const Type *ReturnType;818std::vector<const Type *> ArgTypes;819std::map<unsigned, ImmediateArg> ImmediateArgs;820Result::Ptr Code;821822std::map<std::string, std::string> CustomCodeGenArgs;823824// Recursive function that does the internals of code generation.825void genCodeDfs(Result::Ptr V, std::list<Result::Ptr> &Used,826unsigned Pass) const {827if (!V->needsVisiting(Pass))828return;829830for (Result::Ptr W : V->prerequisites())831genCodeDfs(W, Used, Pass);832833Used.push_back(V);834}835836public:837const std::string &shortName() const { return ShortName; }838const std::string &fullName() const { return FullName; }839StringRef builtinExtension() const { return BuiltinExtension; }840const Type *returnType() const { return ReturnType; }841const std::vector<const Type *> &argTypes() const { return ArgTypes; }842bool requiresFloat() const {843if (ReturnType->requiresFloat())844return true;845for (const Type *T : ArgTypes)846if (T->requiresFloat())847return true;848return false;849}850bool requiresMVE() const {851return ReturnType->requiresMVE() ||852any_of(ArgTypes, [](const Type *T) { return T->requiresMVE(); });853}854bool polymorphic() const { return ShortName != FullName; }855bool polymorphicOnly() const { return PolymorphicOnly; }856bool nonEvaluating() const { return NonEvaluating; }857bool headerOnly() const { return HeaderOnly; }858859// External entry point for code generation, called from EmitterBase.860void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc,861unsigned Pass) const {862assert(!headerOnly() && "Called genCode for header-only intrinsic");863if (!hasCode()) {864for (auto kv : CustomCodeGenArgs)865OS << " " << kv.first << " = " << kv.second << ";\n";866OS << " break; // custom code gen\n";867return;868}869std::list<Result::Ptr> Used;870genCodeDfs(Code, Used, Pass);871872unsigned varindex = 0;873for (Result::Ptr V : Used)874if (V->varnameUsed())875V->setVarname("Val" + utostr(varindex++));876877for (Result::Ptr V : Used) {878OS << " ";879if (V == Used.back()) {880assert(!V->varnameUsed());881OS << "return "; // FIXME: what if the top-level thing is void?882} else if (V->varnameUsed()) {883std::string Type = V->typeName();884OS << V->typeName();885if (!StringRef(Type).ends_with("*"))886OS << " ";887OS << V->varname() << " = ";888}889V->genCode(OS, ParamAlloc);890OS << ";\n";891}892}893bool hasCode() const { return Code != nullptr; }894895static std::string signedHexLiteral(const llvm::APInt &iOrig) {896llvm::APInt i = iOrig.trunc(64);897SmallString<40> s;898i.toString(s, 16, true, true);899return std::string(s);900}901902std::string genSema() const {903assert(!headerOnly() && "Called genSema for header-only intrinsic");904std::vector<std::string> SemaChecks;905906for (const auto &kv : ImmediateArgs) {907const ImmediateArg &IA = kv.second;908909llvm::APInt lo(128, 0), hi(128, 0);910switch (IA.boundsType) {911case ImmediateArg::BoundsType::ExplicitRange:912lo = IA.i1;913hi = IA.i2;914break;915case ImmediateArg::BoundsType::UInt:916lo = 0;917hi = llvm::APInt::getMaxValue(IA.i1).zext(128);918break;919}920921std::string Index = utostr(kv.first);922923// Emit a range check if the legal range of values for the924// immediate is smaller than the _possible_ range of values for925// its type.926unsigned ArgTypeBits = IA.ArgType->sizeInBits();927llvm::APInt ArgTypeRange = llvm::APInt::getMaxValue(ArgTypeBits).zext(128);928llvm::APInt ActualRange = (hi-lo).trunc(64).sext(128);929if (ActualRange.ult(ArgTypeRange))930SemaChecks.push_back("SemaRef.BuiltinConstantArgRange(TheCall, " +931Index + ", " + signedHexLiteral(lo) + ", " +932signedHexLiteral(hi) + ")");933934if (!IA.ExtraCheckType.empty()) {935std::string Suffix;936if (!IA.ExtraCheckArgs.empty()) {937std::string tmp;938StringRef Arg = IA.ExtraCheckArgs;939if (Arg == "!lanesize") {940tmp = utostr(IA.ArgType->sizeInBits());941Arg = tmp;942}943Suffix = (Twine(", ") + Arg).str();944}945SemaChecks.push_back((Twine("SemaRef.BuiltinConstantArg") +946IA.ExtraCheckType + "(TheCall, " + Index +947Suffix + ")")948.str());949}950951assert(!SemaChecks.empty());952}953if (SemaChecks.empty())954return "";955return join(std::begin(SemaChecks), std::end(SemaChecks),956" ||\n ") +957";\n";958}959960ACLEIntrinsic(EmitterBase &ME, Record *R, const Type *Param);961};962963// -----------------------------------------------------------------------------964// The top-level class that holds all the state from analyzing the entire965// Tablegen input.966967class EmitterBase {968protected:969// EmitterBase holds a collection of all the types we've instantiated.970VoidType Void;971std::map<std::string, std::unique_ptr<ScalarType>> ScalarTypes;972std::map<std::tuple<ScalarTypeKind, unsigned, unsigned>,973std::unique_ptr<VectorType>>974VectorTypes;975std::map<std::pair<std::string, unsigned>, std::unique_ptr<MultiVectorType>>976MultiVectorTypes;977std::map<unsigned, std::unique_ptr<PredicateType>> PredicateTypes;978std::map<std::string, std::unique_ptr<PointerType>> PointerTypes;979980// And all the ACLEIntrinsic instances we've created.981std::map<std::string, std::unique_ptr<ACLEIntrinsic>> ACLEIntrinsics;982983public:984// Methods to create a Type object, or return the right existing one from the985// maps stored in this object.986const VoidType *getVoidType() { return &Void; }987const ScalarType *getScalarType(StringRef Name) {988return ScalarTypes[std::string(Name)].get();989}990const ScalarType *getScalarType(Record *R) {991return getScalarType(R->getName());992}993const VectorType *getVectorType(const ScalarType *ST, unsigned Lanes) {994std::tuple<ScalarTypeKind, unsigned, unsigned> key(ST->kind(),995ST->sizeInBits(), Lanes);996if (VectorTypes.find(key) == VectorTypes.end())997VectorTypes[key] = std::make_unique<VectorType>(ST, Lanes);998return VectorTypes[key].get();999}1000const VectorType *getVectorType(const ScalarType *ST) {1001return getVectorType(ST, 128 / ST->sizeInBits());1002}1003const MultiVectorType *getMultiVectorType(unsigned Registers,1004const VectorType *VT) {1005std::pair<std::string, unsigned> key(VT->cNameBase(), Registers);1006if (MultiVectorTypes.find(key) == MultiVectorTypes.end())1007MultiVectorTypes[key] = std::make_unique<MultiVectorType>(Registers, VT);1008return MultiVectorTypes[key].get();1009}1010const PredicateType *getPredicateType(unsigned Lanes) {1011unsigned key = Lanes;1012if (PredicateTypes.find(key) == PredicateTypes.end())1013PredicateTypes[key] = std::make_unique<PredicateType>(Lanes);1014return PredicateTypes[key].get();1015}1016const PointerType *getPointerType(const Type *T, bool Const) {1017PointerType PT(T, Const);1018std::string key = PT.cName();1019if (PointerTypes.find(key) == PointerTypes.end())1020PointerTypes[key] = std::make_unique<PointerType>(PT);1021return PointerTypes[key].get();1022}10231024// Methods to construct a type from various pieces of Tablegen. These are1025// always called in the context of setting up a particular ACLEIntrinsic, so1026// there's always an ambient parameter type (because we're iterating through1027// the Params list in the Tablegen record for the intrinsic), which is used1028// to expand Tablegen classes like 'Vector' which mean something different in1029// each member of a parametric family.1030const Type *getType(Record *R, const Type *Param);1031const Type *getType(DagInit *D, const Type *Param);1032const Type *getType(Init *I, const Type *Param);10331034// Functions that translate the Tablegen representation of an intrinsic's1035// code generation into a collection of Value objects (which will then be1036// reprocessed to read out the actual C++ code included by CGBuiltin.cpp).1037Result::Ptr getCodeForDag(DagInit *D, const Result::Scope &Scope,1038const Type *Param);1039Result::Ptr getCodeForDagArg(DagInit *D, unsigned ArgNum,1040const Result::Scope &Scope, const Type *Param);1041Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType, bool Promote,1042bool Immediate);10431044void GroupSemaChecks(std::map<std::string, std::set<std::string>> &Checks);10451046// Constructor and top-level functions.10471048EmitterBase(RecordKeeper &Records);1049virtual ~EmitterBase() = default;10501051virtual void EmitHeader(raw_ostream &OS) = 0;1052virtual void EmitBuiltinDef(raw_ostream &OS) = 0;1053virtual void EmitBuiltinSema(raw_ostream &OS) = 0;1054void EmitBuiltinCG(raw_ostream &OS);1055void EmitBuiltinAliases(raw_ostream &OS);1056};10571058const Type *EmitterBase::getType(Init *I, const Type *Param) {1059if (auto Dag = dyn_cast<DagInit>(I))1060return getType(Dag, Param);1061if (auto Def = dyn_cast<DefInit>(I))1062return getType(Def->getDef(), Param);10631064PrintFatalError("Could not convert this value into a type");1065}10661067const Type *EmitterBase::getType(Record *R, const Type *Param) {1068// Pass to a subfield of any wrapper records. We don't expect more than one1069// of these: immediate operands are used as plain numbers rather than as1070// llvm::Value, so it's meaningless to promote their type anyway.1071if (R->isSubClassOf("Immediate"))1072R = R->getValueAsDef("type");1073else if (R->isSubClassOf("unpromoted"))1074R = R->getValueAsDef("underlying_type");10751076if (R->getName() == "Void")1077return getVoidType();1078if (R->isSubClassOf("PrimitiveType"))1079return getScalarType(R);1080if (R->isSubClassOf("ComplexType"))1081return getType(R->getValueAsDag("spec"), Param);10821083PrintFatalError(R->getLoc(), "Could not convert this record into a type");1084}10851086const Type *EmitterBase::getType(DagInit *D, const Type *Param) {1087// The meat of the getType system: types in the Tablegen are represented by a1088// dag whose operators select sub-cases of this function.10891090Record *Op = cast<DefInit>(D->getOperator())->getDef();1091if (!Op->isSubClassOf("ComplexTypeOp"))1092PrintFatalError(1093"Expected ComplexTypeOp as dag operator in type expression");10941095if (Op->getName() == "CTO_Parameter") {1096if (isa<VoidType>(Param))1097PrintFatalError("Parametric type in unparametrised context");1098return Param;1099}11001101if (Op->getName() == "CTO_Vec") {1102const Type *Element = getType(D->getArg(0), Param);1103if (D->getNumArgs() == 1) {1104return getVectorType(cast<ScalarType>(Element));1105} else {1106const Type *ExistingVector = getType(D->getArg(1), Param);1107return getVectorType(cast<ScalarType>(Element),1108cast<VectorType>(ExistingVector)->lanes());1109}1110}11111112if (Op->getName() == "CTO_Pred") {1113const Type *Element = getType(D->getArg(0), Param);1114return getPredicateType(128 / Element->sizeInBits());1115}11161117if (Op->isSubClassOf("CTO_Tuple")) {1118unsigned Registers = Op->getValueAsInt("n");1119const Type *Element = getType(D->getArg(0), Param);1120return getMultiVectorType(Registers, cast<VectorType>(Element));1121}11221123if (Op->isSubClassOf("CTO_Pointer")) {1124const Type *Pointee = getType(D->getArg(0), Param);1125return getPointerType(Pointee, Op->getValueAsBit("const"));1126}11271128if (Op->getName() == "CTO_CopyKind") {1129const ScalarType *STSize = cast<ScalarType>(getType(D->getArg(0), Param));1130const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(1), Param));1131for (const auto &kv : ScalarTypes) {1132const ScalarType *RT = kv.second.get();1133if (RT->kind() == STKind->kind() && RT->sizeInBits() == STSize->sizeInBits())1134return RT;1135}1136PrintFatalError("Cannot find a type to satisfy CopyKind");1137}11381139if (Op->isSubClassOf("CTO_ScaleSize")) {1140const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(0), Param));1141int Num = Op->getValueAsInt("num"), Denom = Op->getValueAsInt("denom");1142unsigned DesiredSize = STKind->sizeInBits() * Num / Denom;1143for (const auto &kv : ScalarTypes) {1144const ScalarType *RT = kv.second.get();1145if (RT->kind() == STKind->kind() && RT->sizeInBits() == DesiredSize)1146return RT;1147}1148PrintFatalError("Cannot find a type to satisfy ScaleSize");1149}11501151PrintFatalError("Bad operator in type dag expression");1152}11531154Result::Ptr EmitterBase::getCodeForDag(DagInit *D, const Result::Scope &Scope,1155const Type *Param) {1156Record *Op = cast<DefInit>(D->getOperator())->getDef();11571158if (Op->getName() == "seq") {1159Result::Scope SubScope = Scope;1160Result::Ptr PrevV = nullptr;1161for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i) {1162// We don't use getCodeForDagArg here, because the argument name1163// has different semantics in a seq1164Result::Ptr V =1165getCodeForDag(cast<DagInit>(D->getArg(i)), SubScope, Param);1166StringRef ArgName = D->getArgNameStr(i);1167if (!ArgName.empty())1168SubScope[std::string(ArgName)] = V;1169if (PrevV)1170V->setPredecessor(PrevV);1171PrevV = V;1172}1173return PrevV;1174} else if (Op->isSubClassOf("Type")) {1175if (D->getNumArgs() != 1)1176PrintFatalError("Type casts should have exactly one argument");1177const Type *CastType = getType(Op, Param);1178Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);1179if (const auto *ST = dyn_cast<ScalarType>(CastType)) {1180if (!ST->requiresFloat()) {1181if (Arg->hasIntegerConstantValue())1182return std::make_shared<IntLiteralResult>(1183ST, Arg->integerConstantValue());1184else1185return std::make_shared<IntCastResult>(ST, Arg);1186}1187} else if (const auto *PT = dyn_cast<PointerType>(CastType)) {1188return std::make_shared<PointerCastResult>(PT, Arg);1189}1190PrintFatalError("Unsupported type cast");1191} else if (Op->getName() == "address") {1192if (D->getNumArgs() != 2)1193PrintFatalError("'address' should have two arguments");1194Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);11951196const Type *Ty = nullptr;1197if (auto *DI = dyn_cast<DagInit>(D->getArg(0)))1198if (auto *PTy = dyn_cast<PointerType>(getType(DI->getOperator(), Param)))1199Ty = PTy->getPointeeType();1200if (!Ty)1201PrintFatalError("'address' pointer argument should be a pointer");12021203unsigned Alignment;1204if (auto *II = dyn_cast<IntInit>(D->getArg(1))) {1205Alignment = II->getValue();1206} else {1207PrintFatalError("'address' alignment argument should be an integer");1208}1209return std::make_shared<AddressResult>(Arg, Ty, Alignment);1210} else if (Op->getName() == "unsignedflag") {1211if (D->getNumArgs() != 1)1212PrintFatalError("unsignedflag should have exactly one argument");1213Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef();1214if (!TypeRec->isSubClassOf("Type"))1215PrintFatalError("unsignedflag's argument should be a type");1216if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) {1217return std::make_shared<IntLiteralResult>(1218getScalarType("u32"), ST->kind() == ScalarTypeKind::UnsignedInt);1219} else {1220PrintFatalError("unsignedflag's argument should be a scalar type");1221}1222} else if (Op->getName() == "bitsize") {1223if (D->getNumArgs() != 1)1224PrintFatalError("bitsize should have exactly one argument");1225Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef();1226if (!TypeRec->isSubClassOf("Type"))1227PrintFatalError("bitsize's argument should be a type");1228if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) {1229return std::make_shared<IntLiteralResult>(getScalarType("u32"),1230ST->sizeInBits());1231} else {1232PrintFatalError("bitsize's argument should be a scalar type");1233}1234} else {1235std::vector<Result::Ptr> Args;1236for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i)1237Args.push_back(getCodeForDagArg(D, i, Scope, Param));1238if (Op->isSubClassOf("IRBuilderBase")) {1239std::set<unsigned> AddressArgs;1240std::map<unsigned, std::string> IntegerArgs;1241for (Record *sp : Op->getValueAsListOfDefs("special_params")) {1242unsigned Index = sp->getValueAsInt("index");1243if (sp->isSubClassOf("IRBuilderAddrParam")) {1244AddressArgs.insert(Index);1245} else if (sp->isSubClassOf("IRBuilderIntParam")) {1246IntegerArgs[Index] = std::string(sp->getValueAsString("type"));1247}1248}1249return std::make_shared<IRBuilderResult>(Op->getValueAsString("prefix"),1250Args, AddressArgs, IntegerArgs);1251} else if (Op->isSubClassOf("IRIntBase")) {1252std::vector<const Type *> ParamTypes;1253for (Record *RParam : Op->getValueAsListOfDefs("params"))1254ParamTypes.push_back(getType(RParam, Param));1255std::string IntName = std::string(Op->getValueAsString("intname"));1256if (Op->getValueAsBit("appendKind"))1257IntName += "_" + toLetter(cast<ScalarType>(Param)->kind());1258return std::make_shared<IRIntrinsicResult>(IntName, ParamTypes, Args);1259} else {1260PrintFatalError("Unsupported dag node " + Op->getName());1261}1262}1263}12641265Result::Ptr EmitterBase::getCodeForDagArg(DagInit *D, unsigned ArgNum,1266const Result::Scope &Scope,1267const Type *Param) {1268Init *Arg = D->getArg(ArgNum);1269StringRef Name = D->getArgNameStr(ArgNum);12701271if (!Name.empty()) {1272if (!isa<UnsetInit>(Arg))1273PrintFatalError(1274"dag operator argument should not have both a value and a name");1275auto it = Scope.find(std::string(Name));1276if (it == Scope.end())1277PrintFatalError("unrecognized variable name '" + Name + "'");1278return it->second;1279}12801281// Sometimes the Arg is a bit. Prior to multiclass template argument1282// checking, integers would sneak through the bit declaration,1283// but now they really are bits.1284if (auto *BI = dyn_cast<BitInit>(Arg))1285return std::make_shared<IntLiteralResult>(getScalarType("u32"),1286BI->getValue());12871288if (auto *II = dyn_cast<IntInit>(Arg))1289return std::make_shared<IntLiteralResult>(getScalarType("u32"),1290II->getValue());12911292if (auto *DI = dyn_cast<DagInit>(Arg))1293return getCodeForDag(DI, Scope, Param);12941295if (auto *DI = dyn_cast<DefInit>(Arg)) {1296Record *Rec = DI->getDef();1297if (Rec->isSubClassOf("Type")) {1298const Type *T = getType(Rec, Param);1299return std::make_shared<TypeResult>(T);1300}1301}13021303PrintError("bad DAG argument type for code generation");1304PrintNote("DAG: " + D->getAsString());1305if (TypedInit *Typed = dyn_cast<TypedInit>(Arg))1306PrintNote("argument type: " + Typed->getType()->getAsString());1307PrintFatalNote("argument number " + Twine(ArgNum) + ": " + Arg->getAsString());1308}13091310Result::Ptr EmitterBase::getCodeForArg(unsigned ArgNum, const Type *ArgType,1311bool Promote, bool Immediate) {1312Result::Ptr V = std::make_shared<BuiltinArgResult>(1313ArgNum, isa<PointerType>(ArgType), Immediate);13141315if (Promote) {1316if (const auto *ST = dyn_cast<ScalarType>(ArgType)) {1317if (ST->isInteger() && ST->sizeInBits() < 32)1318V = std::make_shared<IntCastResult>(getScalarType("u32"), V);1319} else if (const auto *PT = dyn_cast<PredicateType>(ArgType)) {1320V = std::make_shared<IntCastResult>(getScalarType("u32"), V);1321V = std::make_shared<IRIntrinsicResult>("arm_mve_pred_i2v",1322std::vector<const Type *>{PT},1323std::vector<Result::Ptr>{V});1324}1325}13261327return V;1328}13291330ACLEIntrinsic::ACLEIntrinsic(EmitterBase &ME, Record *R, const Type *Param)1331: ReturnType(ME.getType(R->getValueAsDef("ret"), Param)) {1332// Derive the intrinsic's full name, by taking the name of the1333// Tablegen record (or override) and appending the suffix from its1334// parameter type. (If the intrinsic is unparametrised, its1335// parameter type will be given as Void, which returns the empty1336// string for acleSuffix.)1337StringRef BaseName =1338(R->isSubClassOf("NameOverride") ? R->getValueAsString("basename")1339: R->getName());1340StringRef overrideLetter = R->getValueAsString("overrideKindLetter");1341FullName =1342(Twine(BaseName) + Param->acleSuffix(std::string(overrideLetter))).str();13431344// Derive the intrinsic's polymorphic name, by removing components from the1345// full name as specified by its 'pnt' member ('polymorphic name type'),1346// which indicates how many type suffixes to remove, and any other piece of1347// the name that should be removed.1348Record *PolymorphicNameType = R->getValueAsDef("pnt");1349SmallVector<StringRef, 8> NameParts;1350StringRef(FullName).split(NameParts, '_');1351for (unsigned i = 0, e = PolymorphicNameType->getValueAsInt(1352"NumTypeSuffixesToDiscard");1353i < e; ++i)1354NameParts.pop_back();1355if (!PolymorphicNameType->isValueUnset("ExtraSuffixToDiscard")) {1356StringRef ExtraSuffix =1357PolymorphicNameType->getValueAsString("ExtraSuffixToDiscard");1358auto it = NameParts.end();1359while (it != NameParts.begin()) {1360--it;1361if (*it == ExtraSuffix) {1362NameParts.erase(it);1363break;1364}1365}1366}1367ShortName = join(std::begin(NameParts), std::end(NameParts), "_");13681369BuiltinExtension = R->getValueAsString("builtinExtension");13701371PolymorphicOnly = R->getValueAsBit("polymorphicOnly");1372NonEvaluating = R->getValueAsBit("nonEvaluating");1373HeaderOnly = R->getValueAsBit("headerOnly");13741375// Process the intrinsic's argument list.1376DagInit *ArgsDag = R->getValueAsDag("args");1377Result::Scope Scope;1378for (unsigned i = 0, e = ArgsDag->getNumArgs(); i < e; ++i) {1379Init *TypeInit = ArgsDag->getArg(i);13801381bool Promote = true;1382if (auto TypeDI = dyn_cast<DefInit>(TypeInit))1383if (TypeDI->getDef()->isSubClassOf("unpromoted"))1384Promote = false;13851386// Work out the type of the argument, for use in the function prototype in1387// the header file.1388const Type *ArgType = ME.getType(TypeInit, Param);1389ArgTypes.push_back(ArgType);13901391// If the argument is a subclass of Immediate, record the details about1392// what values it can take, for Sema checking.1393bool Immediate = false;1394if (auto TypeDI = dyn_cast<DefInit>(TypeInit)) {1395Record *TypeRec = TypeDI->getDef();1396if (TypeRec->isSubClassOf("Immediate")) {1397Immediate = true;13981399Record *Bounds = TypeRec->getValueAsDef("bounds");1400ImmediateArg &IA = ImmediateArgs[i];1401if (Bounds->isSubClassOf("IB_ConstRange")) {1402IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1403IA.i1 = Bounds->getValueAsInt("lo");1404IA.i2 = Bounds->getValueAsInt("hi");1405} else if (Bounds->getName() == "IB_UEltValue") {1406IA.boundsType = ImmediateArg::BoundsType::UInt;1407IA.i1 = Param->sizeInBits();1408} else if (Bounds->getName() == "IB_LaneIndex") {1409IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1410IA.i1 = 0;1411IA.i2 = 128 / Param->sizeInBits() - 1;1412} else if (Bounds->isSubClassOf("IB_EltBit")) {1413IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1414IA.i1 = Bounds->getValueAsInt("base");1415const Type *T = ME.getType(Bounds->getValueAsDef("type"), Param);1416IA.i2 = IA.i1 + T->sizeInBits() - 1;1417} else {1418PrintFatalError("unrecognised ImmediateBounds subclass");1419}14201421IA.ArgType = ArgType;14221423if (!TypeRec->isValueUnset("extra")) {1424IA.ExtraCheckType = TypeRec->getValueAsString("extra");1425if (!TypeRec->isValueUnset("extraarg"))1426IA.ExtraCheckArgs = TypeRec->getValueAsString("extraarg");1427}1428}1429}14301431// The argument will usually have a name in the arguments dag, which goes1432// into the variable-name scope that the code gen will refer to.1433StringRef ArgName = ArgsDag->getArgNameStr(i);1434if (!ArgName.empty())1435Scope[std::string(ArgName)] =1436ME.getCodeForArg(i, ArgType, Promote, Immediate);1437}14381439// Finally, go through the codegen dag and translate it into a Result object1440// (with an arbitrary DAG of depended-on Results hanging off it).1441DagInit *CodeDag = R->getValueAsDag("codegen");1442Record *MainOp = cast<DefInit>(CodeDag->getOperator())->getDef();1443if (MainOp->isSubClassOf("CustomCodegen")) {1444// Or, if it's the special case of CustomCodegen, just accumulate1445// a list of parameters we're going to assign to variables before1446// breaking from the loop.1447CustomCodeGenArgs["CustomCodeGenType"] =1448(Twine("CustomCodeGen::") + MainOp->getValueAsString("type")).str();1449for (unsigned i = 0, e = CodeDag->getNumArgs(); i < e; ++i) {1450StringRef Name = CodeDag->getArgNameStr(i);1451if (Name.empty()) {1452PrintFatalError("Operands to CustomCodegen should have names");1453} else if (auto *II = dyn_cast<IntInit>(CodeDag->getArg(i))) {1454CustomCodeGenArgs[std::string(Name)] = itostr(II->getValue());1455} else if (auto *SI = dyn_cast<StringInit>(CodeDag->getArg(i))) {1456CustomCodeGenArgs[std::string(Name)] = std::string(SI->getValue());1457} else {1458PrintFatalError("Operands to CustomCodegen should be integers");1459}1460}1461} else {1462Code = ME.getCodeForDag(CodeDag, Scope, Param);1463}1464}14651466EmitterBase::EmitterBase(RecordKeeper &Records) {1467// Construct the whole EmitterBase.14681469// First, look up all the instances of PrimitiveType. This gives us the list1470// of vector typedefs we have to put in arm_mve.h, and also allows us to1471// collect all the useful ScalarType instances into a big list so that we can1472// use it for operations such as 'find the unsigned version of this signed1473// integer type'.1474for (Record *R : Records.getAllDerivedDefinitions("PrimitiveType"))1475ScalarTypes[std::string(R->getName())] = std::make_unique<ScalarType>(R);14761477// Now go through the instances of Intrinsic, and for each one, iterate1478// through its list of type parameters making an ACLEIntrinsic for each one.1479for (Record *R : Records.getAllDerivedDefinitions("Intrinsic")) {1480for (Record *RParam : R->getValueAsListOfDefs("params")) {1481const Type *Param = getType(RParam, getVoidType());1482auto Intrinsic = std::make_unique<ACLEIntrinsic>(*this, R, Param);1483ACLEIntrinsics[Intrinsic->fullName()] = std::move(Intrinsic);1484}1485}1486}14871488/// A wrapper on raw_string_ostream that contains its own buffer rather than1489/// having to point it at one elsewhere. (In other words, it works just like1490/// std::ostringstream; also, this makes it convenient to declare a whole array1491/// of them at once.)1492///1493/// We have to set this up using multiple inheritance, to ensure that the1494/// string member has been constructed before raw_string_ostream's constructor1495/// is given a pointer to it.1496class string_holder {1497protected:1498std::string S;1499};1500class raw_self_contained_string_ostream : private string_holder,1501public raw_string_ostream {1502public:1503raw_self_contained_string_ostream() : raw_string_ostream(S) {}1504};15051506const char LLVMLicenseHeader[] =1507" *\n"1508" *\n"1509" * Part of the LLVM Project, under the Apache License v2.0 with LLVM"1510" Exceptions.\n"1511" * See https://llvm.org/LICENSE.txt for license information.\n"1512" * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"1513" *\n"1514" *===-----------------------------------------------------------------"1515"------===\n"1516" */\n"1517"\n";15181519// Machinery for the grouping of intrinsics by similar codegen.1520//1521// The general setup is that 'MergeableGroup' stores the things that a set of1522// similarly shaped intrinsics have in common: the text of their code1523// generation, and the number and type of their parameter variables.1524// MergeableGroup is the key in a std::map whose value is a set of1525// OutputIntrinsic, which stores the ways in which a particular intrinsic1526// specializes the MergeableGroup's generic description: the function name and1527// the _values_ of the parameter variables.15281529struct ComparableStringVector : std::vector<std::string> {1530// Infrastructure: a derived class of vector<string> which comes with an1531// ordering, so that it can be used as a key in maps and an element in sets.1532// There's no requirement on the ordering beyond being deterministic.1533bool operator<(const ComparableStringVector &rhs) const {1534if (size() != rhs.size())1535return size() < rhs.size();1536for (size_t i = 0, e = size(); i < e; ++i)1537if ((*this)[i] != rhs[i])1538return (*this)[i] < rhs[i];1539return false;1540}1541};15421543struct OutputIntrinsic {1544const ACLEIntrinsic *Int;1545std::string Name;1546ComparableStringVector ParamValues;1547bool operator<(const OutputIntrinsic &rhs) const {1548if (Name != rhs.Name)1549return Name < rhs.Name;1550return ParamValues < rhs.ParamValues;1551}1552};1553struct MergeableGroup {1554std::string Code;1555ComparableStringVector ParamTypes;1556bool operator<(const MergeableGroup &rhs) const {1557if (Code != rhs.Code)1558return Code < rhs.Code;1559return ParamTypes < rhs.ParamTypes;1560}1561};15621563void EmitterBase::EmitBuiltinCG(raw_ostream &OS) {1564// Pass 1: generate code for all the intrinsics as if every type or constant1565// that can possibly be abstracted out into a parameter variable will be.1566// This identifies the sets of intrinsics we'll group together into a single1567// piece of code generation.15681569std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroupsPrelim;15701571for (const auto &kv : ACLEIntrinsics) {1572const ACLEIntrinsic &Int = *kv.second;1573if (Int.headerOnly())1574continue;15751576MergeableGroup MG;1577OutputIntrinsic OI;15781579OI.Int = ∬1580OI.Name = Int.fullName();1581CodeGenParamAllocator ParamAllocPrelim{&MG.ParamTypes, &OI.ParamValues};1582raw_string_ostream OS(MG.Code);1583Int.genCode(OS, ParamAllocPrelim, 1);1584OS.flush();15851586MergeableGroupsPrelim[MG].insert(OI);1587}15881589// Pass 2: for each of those groups, optimize the parameter variable set by1590// eliminating 'parameters' that are the same for all intrinsics in the1591// group, and merging together pairs of parameter variables that take the1592// same values as each other for all intrinsics in the group.15931594std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroups;15951596for (const auto &kv : MergeableGroupsPrelim) {1597const MergeableGroup &MG = kv.first;1598std::vector<int> ParamNumbers;1599std::map<ComparableStringVector, int> ParamNumberMap;16001601// Loop over the parameters for this group.1602for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {1603// Is this parameter the same for all intrinsics in the group?1604const OutputIntrinsic &OI_first = *kv.second.begin();1605bool Constant = all_of(kv.second, [&](const OutputIntrinsic &OI) {1606return OI.ParamValues[i] == OI_first.ParamValues[i];1607});16081609// If so, record it as -1, meaning 'no parameter variable needed'. Then1610// the corresponding call to allocParam in pass 2 will not generate a1611// variable at all, and just use the value inline.1612if (Constant) {1613ParamNumbers.push_back(-1);1614continue;1615}16161617// Otherwise, make a list of the values this parameter takes for each1618// intrinsic, and see if that value vector matches anything we already1619// have. We also record the parameter type, so that we don't accidentally1620// match up two parameter variables with different types. (Not that1621// there's much chance of them having textually equivalent values, but in1622// _principle_ it could happen.)1623ComparableStringVector key;1624key.push_back(MG.ParamTypes[i]);1625for (const auto &OI : kv.second)1626key.push_back(OI.ParamValues[i]);16271628auto Found = ParamNumberMap.find(key);1629if (Found != ParamNumberMap.end()) {1630// Yes, an existing parameter variable can be reused for this.1631ParamNumbers.push_back(Found->second);1632continue;1633}16341635// No, we need a new parameter variable.1636int ExistingIndex = ParamNumberMap.size();1637ParamNumberMap[key] = ExistingIndex;1638ParamNumbers.push_back(ExistingIndex);1639}16401641// Now we're ready to do the pass 2 code generation, which will emit the1642// reduced set of parameter variables we've just worked out.16431644for (const auto &OI_prelim : kv.second) {1645const ACLEIntrinsic *Int = OI_prelim.Int;16461647MergeableGroup MG;1648OutputIntrinsic OI;16491650OI.Int = OI_prelim.Int;1651OI.Name = OI_prelim.Name;1652CodeGenParamAllocator ParamAlloc{&MG.ParamTypes, &OI.ParamValues,1653&ParamNumbers};1654raw_string_ostream OS(MG.Code);1655Int->genCode(OS, ParamAlloc, 2);1656OS.flush();16571658MergeableGroups[MG].insert(OI);1659}1660}16611662// Output the actual C++ code.16631664for (const auto &kv : MergeableGroups) {1665const MergeableGroup &MG = kv.first;16661667// List of case statements in the main switch on BuiltinID, and an open1668// brace.1669const char *prefix = "";1670for (const auto &OI : kv.second) {1671OS << prefix << "case ARM::BI__builtin_arm_" << OI.Int->builtinExtension()1672<< "_" << OI.Name << ":";16731674prefix = "\n";1675}1676OS << " {\n";16771678if (!MG.ParamTypes.empty()) {1679// If we've got some parameter variables, then emit their declarations...1680for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {1681StringRef Type = MG.ParamTypes[i];1682OS << " " << Type;1683if (!Type.ends_with("*"))1684OS << " ";1685OS << " Param" << utostr(i) << ";\n";1686}16871688// ... and an inner switch on BuiltinID that will fill them in with each1689// individual intrinsic's values.1690OS << " switch (BuiltinID) {\n";1691for (const auto &OI : kv.second) {1692OS << " case ARM::BI__builtin_arm_" << OI.Int->builtinExtension()1693<< "_" << OI.Name << ":\n";1694for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i)1695OS << " Param" << utostr(i) << " = " << OI.ParamValues[i] << ";\n";1696OS << " break;\n";1697}1698OS << " }\n";1699}17001701// And finally, output the code, and close the outer pair of braces. (The1702// code will always end with a 'return' statement, so we need not insert a1703// 'break' here.)1704OS << MG.Code << "}\n";1705}1706}17071708void EmitterBase::EmitBuiltinAliases(raw_ostream &OS) {1709// Build a sorted table of:1710// - intrinsic id number1711// - full name1712// - polymorphic name or -11713StringToOffsetTable StringTable;1714OS << "static const IntrinToName MapData[] = {\n";1715for (const auto &kv : ACLEIntrinsics) {1716const ACLEIntrinsic &Int = *kv.second;1717if (Int.headerOnly())1718continue;1719int32_t ShortNameOffset =1720Int.polymorphic() ? StringTable.GetOrAddStringOffset(Int.shortName())1721: -1;1722OS << " { ARM::BI__builtin_arm_" << Int.builtinExtension() << "_"1723<< Int.fullName() << ", "1724<< StringTable.GetOrAddStringOffset(Int.fullName()) << ", "1725<< ShortNameOffset << "},\n";1726}1727OS << "};\n\n";17281729OS << "ArrayRef<IntrinToName> Map(MapData);\n\n";17301731OS << "static const char IntrinNames[] = {\n";1732StringTable.EmitString(OS);1733OS << "};\n\n";1734}17351736void EmitterBase::GroupSemaChecks(1737std::map<std::string, std::set<std::string>> &Checks) {1738for (const auto &kv : ACLEIntrinsics) {1739const ACLEIntrinsic &Int = *kv.second;1740if (Int.headerOnly())1741continue;1742std::string Check = Int.genSema();1743if (!Check.empty())1744Checks[Check].insert(Int.fullName());1745}1746}17471748// -----------------------------------------------------------------------------1749// The class used for generating arm_mve.h and related Clang bits1750//17511752class MveEmitter : public EmitterBase {1753public:1754MveEmitter(RecordKeeper &Records) : EmitterBase(Records){};1755void EmitHeader(raw_ostream &OS) override;1756void EmitBuiltinDef(raw_ostream &OS) override;1757void EmitBuiltinSema(raw_ostream &OS) override;1758};17591760void MveEmitter::EmitHeader(raw_ostream &OS) {1761// Accumulate pieces of the header file that will be enabled under various1762// different combinations of #ifdef. The index into parts[] is made up of1763// the following bit flags.1764constexpr unsigned Float = 1;1765constexpr unsigned UseUserNamespace = 2;17661767constexpr unsigned NumParts = 4;1768raw_self_contained_string_ostream parts[NumParts];17691770// Write typedefs for all the required vector types, and a few scalar1771// types that don't already have the name we want them to have.17721773parts[0] << "typedef uint16_t mve_pred16_t;\n";1774parts[Float] << "typedef __fp16 float16_t;\n"1775"typedef float float32_t;\n";1776for (const auto &kv : ScalarTypes) {1777const ScalarType *ST = kv.second.get();1778if (ST->hasNonstandardName())1779continue;1780raw_ostream &OS = parts[ST->requiresFloat() ? Float : 0];1781const VectorType *VT = getVectorType(ST);17821783OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes()1784<< "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " "1785<< VT->cName() << ";\n";17861787// Every vector type also comes with a pair of multi-vector types for1788// the VLD2 and VLD4 instructions.1789for (unsigned n = 2; n <= 4; n += 2) {1790const MultiVectorType *MT = getMultiVectorType(n, VT);1791OS << "typedef struct { " << VT->cName() << " val[" << n << "]; } "1792<< MT->cName() << ";\n";1793}1794}1795parts[0] << "\n";1796parts[Float] << "\n";17971798// Write declarations for all the intrinsics.17991800for (const auto &kv : ACLEIntrinsics) {1801const ACLEIntrinsic &Int = *kv.second;18021803// We generate each intrinsic twice, under its full unambiguous1804// name and its shorter polymorphic name (if the latter exists).1805for (bool Polymorphic : {false, true}) {1806if (Polymorphic && !Int.polymorphic())1807continue;1808if (!Polymorphic && Int.polymorphicOnly())1809continue;18101811// We also generate each intrinsic under a name like __arm_vfooq1812// (which is in C language implementation namespace, so it's1813// safe to define in any conforming user program) and a shorter1814// one like vfooq (which is in user namespace, so a user might1815// reasonably have used it for something already). If so, they1816// can #define __ARM_MVE_PRESERVE_USER_NAMESPACE before1817// including the header, which will suppress the shorter names1818// and leave only the implementation-namespace ones. Then they1819// have to write __arm_vfooq everywhere, of course.18201821for (bool UserNamespace : {false, true}) {1822raw_ostream &OS = parts[(Int.requiresFloat() ? Float : 0) |1823(UserNamespace ? UseUserNamespace : 0)];18241825// Make the name of the function in this declaration.18261827std::string FunctionName =1828Polymorphic ? Int.shortName() : Int.fullName();1829if (!UserNamespace)1830FunctionName = "__arm_" + FunctionName;18311832// Make strings for the types involved in the function's1833// prototype.18341835std::string RetTypeName = Int.returnType()->cName();1836if (!StringRef(RetTypeName).ends_with("*"))1837RetTypeName += " ";18381839std::vector<std::string> ArgTypeNames;1840for (const Type *ArgTypePtr : Int.argTypes())1841ArgTypeNames.push_back(ArgTypePtr->cName());1842std::string ArgTypesString =1843join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", ");18441845// Emit the actual declaration. All these functions are1846// declared 'static inline' without a body, which is fine1847// provided clang recognizes them as builtins, and has the1848// effect that this type signature is used in place of the one1849// that Builtins.td didn't provide. That's how we can get1850// structure types that weren't defined until this header was1851// included to be part of the type signature of a builtin that1852// was known to clang already.1853//1854// The declarations use __attribute__(__clang_arm_builtin_alias),1855// so that each function declared will be recognized as the1856// appropriate MVE builtin in spite of its user-facing name.1857//1858// (That's better than making them all wrapper functions,1859// partly because it avoids any compiler error message citing1860// the wrapper function definition instead of the user's code,1861// and mostly because some MVE intrinsics have arguments1862// required to be compile-time constants, and that property1863// can't be propagated through a wrapper function. It can be1864// propagated through a macro, but macros can't be overloaded1865// on argument types very easily - you have to use _Generic,1866// which makes error messages very confusing when the user1867// gets it wrong.)1868//1869// Finally, the polymorphic versions of the intrinsics are1870// also defined with __attribute__(overloadable), so that when1871// the same name is defined with several type signatures, the1872// right thing happens. Each one of the overloaded1873// declarations is given a different builtin id, which1874// has exactly the effect we want: first clang resolves the1875// overload to the right function, then it knows which builtin1876// it's referring to, and then the Sema checking for that1877// builtin can check further things like the constant1878// arguments.1879//1880// One more subtlety is the newline just before the return1881// type name. That's a cosmetic tweak to make the error1882// messages legible if the user gets the types wrong in a call1883// to a polymorphic function: this way, clang will print just1884// the _final_ line of each declaration in the header, to show1885// the type signatures that would have been legal. So all the1886// confusing machinery with __attribute__ is left out of the1887// error message, and the user sees something that's more or1888// less self-documenting: "here's a list of actually readable1889// type signatures for vfooq(), and here's why each one didn't1890// match your call".18911892OS << "static __inline__ __attribute__(("1893<< (Polymorphic ? "__overloadable__, " : "")1894<< "__clang_arm_builtin_alias(__builtin_arm_mve_" << Int.fullName()1895<< ")))\n"1896<< RetTypeName << FunctionName << "(" << ArgTypesString << ");\n";1897}1898}1899}1900for (auto &part : parts)1901part << "\n";19021903// Now we've finished accumulating bits and pieces into the parts[] array.1904// Put it all together to write the final output file.19051906OS << "/*===---- arm_mve.h - ARM MVE intrinsics "1907"-----------------------------------===\n"1908<< LLVMLicenseHeader1909<< "#ifndef __ARM_MVE_H\n"1910"#define __ARM_MVE_H\n"1911"\n"1912"#if !__ARM_FEATURE_MVE\n"1913"#error \"MVE support not enabled\"\n"1914"#endif\n"1915"\n"1916"#include <stdint.h>\n"1917"\n"1918"#ifdef __cplusplus\n"1919"extern \"C\" {\n"1920"#endif\n"1921"\n";19221923for (size_t i = 0; i < NumParts; ++i) {1924std::vector<std::string> conditions;1925if (i & Float)1926conditions.push_back("(__ARM_FEATURE_MVE & 2)");1927if (i & UseUserNamespace)1928conditions.push_back("(!defined __ARM_MVE_PRESERVE_USER_NAMESPACE)");19291930std::string condition =1931join(std::begin(conditions), std::end(conditions), " && ");1932if (!condition.empty())1933OS << "#if " << condition << "\n\n";1934OS << parts[i].str();1935if (!condition.empty())1936OS << "#endif /* " << condition << " */\n\n";1937}19381939OS << "#ifdef __cplusplus\n"1940"} /* extern \"C\" */\n"1941"#endif\n"1942"\n"1943"#endif /* __ARM_MVE_H */\n";1944}19451946void MveEmitter::EmitBuiltinDef(raw_ostream &OS) {1947for (const auto &kv : ACLEIntrinsics) {1948const ACLEIntrinsic &Int = *kv.second;1949OS << "BUILTIN(__builtin_arm_mve_" << Int.fullName()1950<< ", \"\", \"n\")\n";1951}19521953std::set<std::string> ShortNamesSeen;19541955for (const auto &kv : ACLEIntrinsics) {1956const ACLEIntrinsic &Int = *kv.second;1957if (Int.polymorphic()) {1958StringRef Name = Int.shortName();1959if (ShortNamesSeen.find(std::string(Name)) == ShortNamesSeen.end()) {1960OS << "BUILTIN(__builtin_arm_mve_" << Name << ", \"vi.\", \"nt";1961if (Int.nonEvaluating())1962OS << "u"; // indicate that this builtin doesn't evaluate its args1963OS << "\")\n";1964ShortNamesSeen.insert(std::string(Name));1965}1966}1967}1968}19691970void MveEmitter::EmitBuiltinSema(raw_ostream &OS) {1971std::map<std::string, std::set<std::string>> Checks;1972GroupSemaChecks(Checks);19731974for (const auto &kv : Checks) {1975for (StringRef Name : kv.second)1976OS << "case ARM::BI__builtin_arm_mve_" << Name << ":\n";1977OS << " return " << kv.first;1978}1979}19801981// -----------------------------------------------------------------------------1982// Class that describes an ACLE intrinsic implemented as a macro.1983//1984// This class is used when the intrinsic is polymorphic in 2 or 3 types, but we1985// want to avoid a combinatorial explosion by reinterpreting the arguments to1986// fixed types.19871988class FunctionMacro {1989std::vector<StringRef> Params;1990StringRef Definition;19911992public:1993FunctionMacro(const Record &R);19941995const std::vector<StringRef> &getParams() const { return Params; }1996StringRef getDefinition() const { return Definition; }1997};19981999FunctionMacro::FunctionMacro(const Record &R) {2000Params = R.getValueAsListOfStrings("params");2001Definition = R.getValueAsString("definition");2002}20032004// -----------------------------------------------------------------------------2005// The class used for generating arm_cde.h and related Clang bits2006//20072008class CdeEmitter : public EmitterBase {2009std::map<StringRef, FunctionMacro> FunctionMacros;20102011public:2012CdeEmitter(RecordKeeper &Records);2013void EmitHeader(raw_ostream &OS) override;2014void EmitBuiltinDef(raw_ostream &OS) override;2015void EmitBuiltinSema(raw_ostream &OS) override;2016};20172018CdeEmitter::CdeEmitter(RecordKeeper &Records) : EmitterBase(Records) {2019for (Record *R : Records.getAllDerivedDefinitions("FunctionMacro"))2020FunctionMacros.emplace(R->getName(), FunctionMacro(*R));2021}20222023void CdeEmitter::EmitHeader(raw_ostream &OS) {2024// Accumulate pieces of the header file that will be enabled under various2025// different combinations of #ifdef. The index into parts[] is one of the2026// following:2027constexpr unsigned None = 0;2028constexpr unsigned MVE = 1;2029constexpr unsigned MVEFloat = 2;20302031constexpr unsigned NumParts = 3;2032raw_self_contained_string_ostream parts[NumParts];20332034// Write typedefs for all the required vector types, and a few scalar2035// types that don't already have the name we want them to have.20362037parts[MVE] << "typedef uint16_t mve_pred16_t;\n";2038parts[MVEFloat] << "typedef __fp16 float16_t;\n"2039"typedef float float32_t;\n";2040for (const auto &kv : ScalarTypes) {2041const ScalarType *ST = kv.second.get();2042if (ST->hasNonstandardName())2043continue;2044// We don't have float64x2_t2045if (ST->kind() == ScalarTypeKind::Float && ST->sizeInBits() == 64)2046continue;2047raw_ostream &OS = parts[ST->requiresFloat() ? MVEFloat : MVE];2048const VectorType *VT = getVectorType(ST);20492050OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes()2051<< "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " "2052<< VT->cName() << ";\n";2053}2054parts[MVE] << "\n";2055parts[MVEFloat] << "\n";20562057// Write declarations for all the intrinsics.20582059for (const auto &kv : ACLEIntrinsics) {2060const ACLEIntrinsic &Int = *kv.second;20612062// We generate each intrinsic twice, under its full unambiguous2063// name and its shorter polymorphic name (if the latter exists).2064for (bool Polymorphic : {false, true}) {2065if (Polymorphic && !Int.polymorphic())2066continue;2067if (!Polymorphic && Int.polymorphicOnly())2068continue;20692070raw_ostream &OS =2071parts[Int.requiresFloat() ? MVEFloat2072: Int.requiresMVE() ? MVE : None];20732074// Make the name of the function in this declaration.2075std::string FunctionName =2076"__arm_" + (Polymorphic ? Int.shortName() : Int.fullName());20772078// Make strings for the types involved in the function's2079// prototype.2080std::string RetTypeName = Int.returnType()->cName();2081if (!StringRef(RetTypeName).ends_with("*"))2082RetTypeName += " ";20832084std::vector<std::string> ArgTypeNames;2085for (const Type *ArgTypePtr : Int.argTypes())2086ArgTypeNames.push_back(ArgTypePtr->cName());2087std::string ArgTypesString =2088join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", ");20892090// Emit the actual declaration. See MveEmitter::EmitHeader for detailed2091// comments2092OS << "static __inline__ __attribute__(("2093<< (Polymorphic ? "__overloadable__, " : "")2094<< "__clang_arm_builtin_alias(__builtin_arm_" << Int.builtinExtension()2095<< "_" << Int.fullName() << ")))\n"2096<< RetTypeName << FunctionName << "(" << ArgTypesString << ");\n";2097}2098}20992100for (const auto &kv : FunctionMacros) {2101StringRef Name = kv.first;2102const FunctionMacro &FM = kv.second;21032104raw_ostream &OS = parts[MVE];2105OS << "#define "2106<< "__arm_" << Name << "(" << join(FM.getParams(), ", ") << ") "2107<< FM.getDefinition() << "\n";2108}21092110for (auto &part : parts)2111part << "\n";21122113// Now we've finished accumulating bits and pieces into the parts[] array.2114// Put it all together to write the final output file.21152116OS << "/*===---- arm_cde.h - ARM CDE intrinsics "2117"-----------------------------------===\n"2118<< LLVMLicenseHeader2119<< "#ifndef __ARM_CDE_H\n"2120"#define __ARM_CDE_H\n"2121"\n"2122"#if !__ARM_FEATURE_CDE\n"2123"#error \"CDE support not enabled\"\n"2124"#endif\n"2125"\n"2126"#include <stdint.h>\n"2127"\n"2128"#ifdef __cplusplus\n"2129"extern \"C\" {\n"2130"#endif\n"2131"\n";21322133for (size_t i = 0; i < NumParts; ++i) {2134std::string condition;2135if (i == MVEFloat)2136condition = "__ARM_FEATURE_MVE & 2";2137else if (i == MVE)2138condition = "__ARM_FEATURE_MVE";21392140if (!condition.empty())2141OS << "#if " << condition << "\n\n";2142OS << parts[i].str();2143if (!condition.empty())2144OS << "#endif /* " << condition << " */\n\n";2145}21462147OS << "#ifdef __cplusplus\n"2148"} /* extern \"C\" */\n"2149"#endif\n"2150"\n"2151"#endif /* __ARM_CDE_H */\n";2152}21532154void CdeEmitter::EmitBuiltinDef(raw_ostream &OS) {2155for (const auto &kv : ACLEIntrinsics) {2156if (kv.second->headerOnly())2157continue;2158const ACLEIntrinsic &Int = *kv.second;2159OS << "BUILTIN(__builtin_arm_cde_" << Int.fullName()2160<< ", \"\", \"ncU\")\n";2161}2162}21632164void CdeEmitter::EmitBuiltinSema(raw_ostream &OS) {2165std::map<std::string, std::set<std::string>> Checks;2166GroupSemaChecks(Checks);21672168for (const auto &kv : Checks) {2169for (StringRef Name : kv.second)2170OS << "case ARM::BI__builtin_arm_cde_" << Name << ":\n";2171OS << " Err = " << kv.first << " break;\n";2172}2173}21742175} // namespace21762177namespace clang {21782179// MVE21802181void EmitMveHeader(RecordKeeper &Records, raw_ostream &OS) {2182MveEmitter(Records).EmitHeader(OS);2183}21842185void EmitMveBuiltinDef(RecordKeeper &Records, raw_ostream &OS) {2186MveEmitter(Records).EmitBuiltinDef(OS);2187}21882189void EmitMveBuiltinSema(RecordKeeper &Records, raw_ostream &OS) {2190MveEmitter(Records).EmitBuiltinSema(OS);2191}21922193void EmitMveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {2194MveEmitter(Records).EmitBuiltinCG(OS);2195}21962197void EmitMveBuiltinAliases(RecordKeeper &Records, raw_ostream &OS) {2198MveEmitter(Records).EmitBuiltinAliases(OS);2199}22002201// CDE22022203void EmitCdeHeader(RecordKeeper &Records, raw_ostream &OS) {2204CdeEmitter(Records).EmitHeader(OS);2205}22062207void EmitCdeBuiltinDef(RecordKeeper &Records, raw_ostream &OS) {2208CdeEmitter(Records).EmitBuiltinDef(OS);2209}22102211void EmitCdeBuiltinSema(RecordKeeper &Records, raw_ostream &OS) {2212CdeEmitter(Records).EmitBuiltinSema(OS);2213}22142215void EmitCdeBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {2216CdeEmitter(Records).EmitBuiltinCG(OS);2217}22182219void EmitCdeBuiltinAliases(RecordKeeper &Records, raw_ostream &OS) {2220CdeEmitter(Records).EmitBuiltinAliases(OS);2221}22222223} // end namespace clang222422252226