2300 lines · cpp
1//===-- MveEmitter.cpp - Generate arm_mve.h for use with clang ------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This set of linked tablegen backends is responsible for emitting the bits10// and pieces that implement <arm_mve.h>, which is defined by the ACLE standard11// and provides a set of types and functions for (more or less) direct access12// to the MVE instruction set, including the scalar shifts as well as the13// vector instructions.14//15// MVE's standard intrinsic functions are unusual in that they have a system of16// polymorphism. For example, the function vaddq() can behave like vaddq_u16(),17// vaddq_f32(), vaddq_s8(), etc., depending on the types of the vector18// arguments you give it.19//20// This constrains the implementation strategies. The usual approach to making21// the user-facing functions polymorphic would be to either use22// __attribute__((overloadable)) to make a set of vaddq() functions that are23// all inline wrappers on the underlying clang builtins, or to define a single24// vaddq() macro which expands to an instance of _Generic.25//26// The inline-wrappers approach would work fine for most intrinsics, except for27// the ones that take an argument required to be a compile-time constant,28// because if you wrap an inline function around a call to a builtin, the29// constant nature of the argument is not passed through.30//31// The _Generic approach can be made to work with enough effort, but it takes a32// lot of machinery, because of the design feature of _Generic that even the33// untaken branches are required to pass all front-end validity checks such as34// type-correctness. You can work around that by nesting further _Generics all35// over the place to coerce things to the right type in untaken branches, but36// what you get out is complicated, hard to guarantee its correctness, and37// worst of all, gives _completely unreadable_ error messages if the user gets38// the types wrong for an intrinsic call.39//40// Therefore, my strategy is to introduce a new __attribute__ that allows a41// function to be mapped to a clang builtin even though it doesn't have the42// same name, and then declare all the user-facing MVE function names with that43// attribute, mapping each one directly to the clang builtin. And the44// polymorphic ones have __attribute__((overloadable)) as well. So once the45// compiler has resolved the overload, it knows the internal builtin ID of the46// selected function, and can check the immediate arguments against that; and47// if the user gets the types wrong in a call to a polymorphic intrinsic, they48// get a completely clear error message showing all the declarations of that49// function in the header file and explaining why each one doesn't fit their50// call.51//52// The downside of this is that if every clang builtin has to correspond53// exactly to a user-facing ACLE intrinsic, then you can't save work in the54// frontend by doing it in the header file: CGBuiltin.cpp has to do the entire55// job of converting an ACLE intrinsic call into LLVM IR. So the Tablegen56// description for an MVE intrinsic has to contain a full description of the57// sequence of IRBuilder calls that clang will need to make.58//59//===----------------------------------------------------------------------===//60 61#include "llvm/ADT/APInt.h"62#include "llvm/ADT/StringRef.h"63#include "llvm/ADT/StringSwitch.h"64#include "llvm/Support/Casting.h"65#include "llvm/Support/raw_ostream.h"66#include "llvm/TableGen/Error.h"67#include "llvm/TableGen/Record.h"68#include "llvm/TableGen/StringToOffsetTable.h"69#include <cassert>70#include <cstddef>71#include <cstdint>72#include <list>73#include <map>74#include <memory>75#include <set>76#include <string>77#include <vector>78 79using namespace llvm;80 81namespace {82 83class EmitterBase;84class Result;85 86// -----------------------------------------------------------------------------87// A system of classes to represent all the types we'll need to deal with in88// the prototypes of intrinsics.89//90// Query methods include finding out the C name of a type; the "LLVM name" in91// the sense of a C++ code snippet that can be used in the codegen function;92// the suffix that represents the type in the ACLE intrinsic naming scheme93// (e.g. 's32' represents int32_t in intrinsics such as vaddq_s32); whether the94// type is floating-point related (hence should be under #ifdef in the MVE95// header so that it isn't included in integer-only MVE mode); and the type's96// size in bits. Not all subtypes support all these queries.97 98class Type {99public:100 enum class TypeKind {101 // Void appears as a return type (for store intrinsics, which are pure102 // side-effect). It's also used as the parameter type in the Tablegen103 // when an intrinsic doesn't need to come in various suffixed forms like104 // vfooq_s8,vfooq_u16,vfooq_f32.105 Void,106 107 // Scalar is used for ordinary int and float types of all sizes.108 Scalar,109 110 // Vector is used for anything that occupies exactly one MVE vector111 // register, i.e. {uint,int,float}NxM_t.112 Vector,113 114 // MultiVector is used for the {uint,int,float}NxMxK_t types used by the115 // interleaving load/store intrinsics v{ld,st}{2,4}q.116 MultiVector,117 118 // Predicate is used by all the predicated intrinsics. Its C119 // representation is mve_pred16_t (which is just an alias for uint16_t).120 // But we give more detail here, by indicating that a given predicate121 // instruction is logically regarded as a vector of i1 containing the122 // same number of lanes as the input vector type. So our Predicate type123 // comes with a lane count, which we use to decide which kind of <n x i1>124 // we'll invoke the pred_i2v IR intrinsic to translate it into.125 Predicate,126 127 // Pointer is used for pointer types (obviously), and comes with a flag128 // indicating whether it's a pointer to a const or mutable instance of129 // the pointee type.130 Pointer,131 };132 133private:134 const TypeKind TKind;135 136protected:137 Type(TypeKind K) : TKind(K) {}138 139public:140 TypeKind typeKind() const { return TKind; }141 virtual ~Type() = default;142 virtual bool requiresFloat() const = 0;143 virtual bool requiresMVE() const = 0;144 virtual unsigned sizeInBits() const = 0;145 virtual std::string cName() const = 0;146 virtual std::string llvmName() const {147 PrintFatalError("no LLVM type name available for type " + cName());148 }149 virtual std::string acleSuffix(std::string) const {150 PrintFatalError("no ACLE suffix available for this type");151 }152};153 154enum class ScalarTypeKind { SignedInt, UnsignedInt, Float };155inline std::string toLetter(ScalarTypeKind kind) {156 switch (kind) {157 case ScalarTypeKind::SignedInt:158 return "s";159 case ScalarTypeKind::UnsignedInt:160 return "u";161 case ScalarTypeKind::Float:162 return "f";163 }164 llvm_unreachable("Unhandled ScalarTypeKind enum");165}166inline std::string toCPrefix(ScalarTypeKind kind) {167 switch (kind) {168 case ScalarTypeKind::SignedInt:169 return "int";170 case ScalarTypeKind::UnsignedInt:171 return "uint";172 case ScalarTypeKind::Float:173 return "float";174 }175 llvm_unreachable("Unhandled ScalarTypeKind enum");176}177 178class VoidType : public Type {179public:180 VoidType() : Type(TypeKind::Void) {}181 unsigned sizeInBits() const override { return 0; }182 bool requiresFloat() const override { return false; }183 bool requiresMVE() const override { return false; }184 std::string cName() const override { return "void"; }185 186 static bool classof(const Type *T) { return T->typeKind() == TypeKind::Void; }187 std::string acleSuffix(std::string) const override { return ""; }188};189 190class PointerType : public Type {191 const Type *Pointee;192 bool Const;193 194public:195 PointerType(const Type *Pointee, bool Const)196 : Type(TypeKind::Pointer), Pointee(Pointee), Const(Const) {}197 unsigned sizeInBits() const override { return 32; }198 bool requiresFloat() const override { return Pointee->requiresFloat(); }199 bool requiresMVE() const override { return Pointee->requiresMVE(); }200 std::string cName() const override {201 std::string Name = Pointee->cName();202 203 // The syntax for a pointer in C is different when the pointee is204 // itself a pointer. The MVE intrinsics don't contain any double205 // pointers, so we don't need to worry about that wrinkle.206 assert(!isa<PointerType>(Pointee) && "Pointer to pointer not supported");207 208 if (Const)209 Name = "const " + Name;210 return Name + " *";211 }212 std::string llvmName() const override { return "Builder.getPtrTy()"; }213 const Type *getPointeeType() const { return Pointee; }214 215 static bool classof(const Type *T) {216 return T->typeKind() == TypeKind::Pointer;217 }218};219 220// Base class for all the types that have a name of the form221// [prefix][numbers]_t, like int32_t, uint16x8_t, float32x4x2_t.222//223// For this sub-hierarchy we invent a cNameBase() method which returns the224// whole name except for the trailing "_t", so that Vector and MultiVector can225// append an extra "x2" or whatever to their element type's cNameBase(). Then226// the main cName() query method puts "_t" on the end for the final type name.227 228class CRegularNamedType : public Type {229 using Type::Type;230 virtual std::string cNameBase() const = 0;231 232public:233 std::string cName() const override { return cNameBase() + "_t"; }234};235 236class ScalarType : public CRegularNamedType {237 ScalarTypeKind Kind;238 unsigned Bits;239 std::string NameOverride;240 241public:242 ScalarType(const Record *Record) : CRegularNamedType(TypeKind::Scalar) {243 Kind = StringSwitch<ScalarTypeKind>(Record->getValueAsString("kind"))244 .Case("s", ScalarTypeKind::SignedInt)245 .Case("u", ScalarTypeKind::UnsignedInt)246 .Case("f", ScalarTypeKind::Float);247 Bits = Record->getValueAsInt("size");248 NameOverride = std::string(Record->getValueAsString("nameOverride"));249 }250 unsigned sizeInBits() const override { return Bits; }251 ScalarTypeKind kind() const { return Kind; }252 std::string suffix() const { return toLetter(Kind) + utostr(Bits); }253 std::string cNameBase() const override {254 return toCPrefix(Kind) + utostr(Bits);255 }256 std::string cName() const override {257 if (NameOverride.empty())258 return CRegularNamedType::cName();259 return NameOverride;260 }261 std::string llvmName() const override {262 if (Kind == ScalarTypeKind::Float) {263 if (Bits == 16)264 return "HalfTy";265 if (Bits == 32)266 return "FloatTy";267 if (Bits == 64)268 return "DoubleTy";269 PrintFatalError("bad size for floating type");270 }271 return "Int" + utostr(Bits) + "Ty";272 }273 std::string acleSuffix(std::string overrideLetter) const override {274 return "_" + (overrideLetter.size() ? overrideLetter : toLetter(Kind))275 + utostr(Bits);276 }277 bool isInteger() const { return Kind != ScalarTypeKind::Float; }278 bool requiresFloat() const override { return !isInteger(); }279 bool requiresMVE() const override { return false; }280 bool hasNonstandardName() const { return !NameOverride.empty(); }281 282 static bool classof(const Type *T) {283 return T->typeKind() == TypeKind::Scalar;284 }285};286 287class VectorType : public CRegularNamedType {288 const ScalarType *Element;289 unsigned Lanes;290 291public:292 VectorType(const ScalarType *Element, unsigned Lanes)293 : CRegularNamedType(TypeKind::Vector), Element(Element), Lanes(Lanes) {}294 unsigned sizeInBits() const override { return Lanes * Element->sizeInBits(); }295 unsigned lanes() const { return Lanes; }296 bool requiresFloat() const override { return Element->requiresFloat(); }297 bool requiresMVE() const override { return true; }298 std::string cNameBase() const override {299 return Element->cNameBase() + "x" + utostr(Lanes);300 }301 std::string llvmName() const override {302 return "llvm::FixedVectorType::get(" + Element->llvmName() + ", " +303 utostr(Lanes) + ")";304 }305 306 static bool classof(const Type *T) {307 return T->typeKind() == TypeKind::Vector;308 }309};310 311class MultiVectorType : public CRegularNamedType {312 const VectorType *Element;313 unsigned Registers;314 315public:316 MultiVectorType(unsigned Registers, const VectorType *Element)317 : CRegularNamedType(TypeKind::MultiVector), Element(Element),318 Registers(Registers) {}319 unsigned sizeInBits() const override {320 return Registers * Element->sizeInBits();321 }322 unsigned registers() const { return Registers; }323 bool requiresFloat() const override { return Element->requiresFloat(); }324 bool requiresMVE() const override { return true; }325 std::string cNameBase() const override {326 return Element->cNameBase() + "x" + utostr(Registers);327 }328 329 // MultiVectorType doesn't override llvmName, because we don't expect to do330 // automatic code generation for the MVE intrinsics that use it: the {vld2,331 // vld4, vst2, vst4} family are the only ones that use these types, so it was332 // easier to hand-write the codegen for dealing with these structs than to333 // build in lots of extra automatic machinery that would only be used once.334 335 static bool classof(const Type *T) {336 return T->typeKind() == TypeKind::MultiVector;337 }338};339 340class PredicateType : public CRegularNamedType {341 unsigned Lanes;342 343public:344 PredicateType(unsigned Lanes)345 : CRegularNamedType(TypeKind::Predicate), Lanes(Lanes) {}346 unsigned sizeInBits() const override { return 16; }347 std::string cNameBase() const override { return "mve_pred16"; }348 bool requiresFloat() const override { return false; };349 bool requiresMVE() const override { return true; }350 std::string llvmName() const override {351 return "llvm::FixedVectorType::get(Builder.getInt1Ty(), " + utostr(Lanes) +352 ")";353 }354 355 static bool classof(const Type *T) {356 return T->typeKind() == TypeKind::Predicate;357 }358};359 360// -----------------------------------------------------------------------------361// Class to facilitate merging together the code generation for many intrinsics362// by means of varying a few constant or type parameters.363//364// Most obviously, the intrinsics in a single parametrised family will have365// code generation sequences that only differ in a type or two, e.g. vaddq_s8366// and vaddq_u16 will look the same apart from putting a different vector type367// in the call to CGM.getIntrinsic(). But also, completely different intrinsics368// will often code-generate in the same way, with only a different choice of369// _which_ IR intrinsic they lower to (e.g. vaddq_m_s8 and vmulq_m_s8), but370// marshalling the arguments and return values of the IR intrinsic in exactly371// the same way. And others might differ only in some other kind of constant,372// such as a lane index.373//374// So, when we generate the IR-building code for all these intrinsics, we keep375// track of every value that could possibly be pulled out of the code and376// stored ahead of time in a local variable. Then we group together intrinsics377// by textual equivalence of the code that would result if _all_ those378// parameters were stored in local variables. That gives us maximal sets that379// can be implemented by a single piece of IR-building code by changing380// parameter values ahead of time.381//382// After we've done that, we do a second pass in which we only allocate _some_383// of the parameters into local variables, by tracking which ones have the same384// values as each other (so that a single variable can be reused) and which385// ones are the same across the whole set (so that no variable is needed at386// all).387//388// Hence the class below. Its allocParam method is invoked during code389// generation by every method of a Result subclass (see below) that wants to390// give it the opportunity to pull something out into a switchable parameter.391// It returns a variable name for the parameter, or (if it's being used in the392// second pass once we've decided that some parameters don't need to be stored393// in variables after all) it might just return the input expression unchanged.394 395struct CodeGenParamAllocator {396 // Accumulated during code generation397 std::vector<std::string> *ParamTypes = nullptr;398 std::vector<std::string> *ParamValues = nullptr;399 400 // Provided ahead of time in pass 2, to indicate which parameters are being401 // assigned to what. This vector contains an entry for each call to402 // allocParam expected during code gen (which we counted up in pass 1), and403 // indicates the number of the parameter variable that should be returned, or404 // -1 if this call shouldn't allocate a parameter variable at all.405 //406 // We rely on the recursive code generation working identically in passes 1407 // and 2, so that the same list of calls to allocParam happen in the same408 // order. That guarantees that the parameter numbers recorded in pass 1 will409 // match the entries in this vector that store what EmitterBase::EmitBuiltinCG410 // decided to do about each one in pass 2.411 std::vector<int> *ParamNumberMap = nullptr;412 413 // Internally track how many things we've allocated414 unsigned nparams = 0;415 416 std::string allocParam(StringRef Type, StringRef Value) {417 unsigned ParamNumber;418 419 if (!ParamNumberMap) {420 // In pass 1, unconditionally assign a new parameter variable to every421 // value we're asked to process.422 ParamNumber = nparams++;423 } else {424 // In pass 2, consult the map provided by the caller to find out which425 // variable we should be keeping things in.426 int MapValue = (*ParamNumberMap)[nparams++];427 if (MapValue < 0)428 return std::string(Value);429 ParamNumber = MapValue;430 }431 432 // If we've allocated a new parameter variable for the first time, store433 // its type and value to be retrieved after codegen.434 if (ParamTypes && ParamTypes->size() == ParamNumber)435 ParamTypes->push_back(std::string(Type));436 if (ParamValues && ParamValues->size() == ParamNumber)437 ParamValues->push_back(std::string(Value));438 439 // Unimaginative naming scheme for parameter variables.440 return "Param" + utostr(ParamNumber);441 }442};443 444// -----------------------------------------------------------------------------445// System of classes that represent all the intermediate values used during446// code-generation for an intrinsic.447//448// The base class 'Result' can represent a value of the LLVM type 'Value', or449// sometimes 'Address' (for loads/stores, including an alignment requirement).450//451// In the case where the Tablegen provides a value in the codegen dag as a452// plain integer literal, the Result object we construct here will be one that453// returns true from hasIntegerConstantValue(). This allows the generated C++454// code to use the constant directly in contexts which can take a literal455// integer, such as Builder.CreateExtractValue(thing, 1), without going to the456// effort of calling llvm::ConstantInt::get() and then pulling the constant457// back out of the resulting llvm:Value later.458 459class Result {460public:461 // Convenient shorthand for the pointer type we'll be using everywhere.462 using Ptr = std::shared_ptr<Result>;463 464private:465 Ptr Predecessor;466 std::string VarName;467 bool VarNameUsed = false;468 unsigned Visited = 0;469 470public:471 virtual ~Result() = default;472 using Scope = std::map<std::string, Ptr, std::less<>>;473 virtual void genCode(raw_ostream &OS, CodeGenParamAllocator &) const = 0;474 virtual bool hasIntegerConstantValue() const { return false; }475 virtual uint32_t integerConstantValue() const { return 0; }476 virtual bool hasIntegerValue() const { return false; }477 virtual std::string getIntegerValue(const std::string &) {478 llvm_unreachable("non-working Result::getIntegerValue called");479 }480 virtual std::string typeName() const { return "Value *"; }481 482 // Mostly, when a code-generation operation has a dependency on prior483 // operations, it's because it uses the output values of those operations as484 // inputs. But there's one exception, which is the use of 'seq' in Tablegen485 // to indicate that operations have to be performed in sequence regardless of486 // whether they use each others' output values.487 //488 // So, the actual generation of code is done by depth-first search, using the489 // prerequisites() method to get a list of all the other Results that have to490 // be computed before this one. That method divides into the 'predecessor',491 // set by setPredecessor() while processing a 'seq' dag node, and the list492 // returned by 'morePrerequisites', which each subclass implements to return493 // a list of the Results it uses as input to whatever its own computation is494 // doing.495 496 virtual void morePrerequisites(std::vector<Ptr> &output) const {}497 std::vector<Ptr> prerequisites() const {498 std::vector<Ptr> ToRet;499 if (Predecessor)500 ToRet.push_back(Predecessor);501 morePrerequisites(ToRet);502 return ToRet;503 }504 505 void setPredecessor(Ptr p) {506 // If the user has nested one 'seq' node inside another, and this507 // method is called on the return value of the inner 'seq' (i.e.508 // the final item inside it), then we can't link _this_ node to p,509 // because it already has a predecessor. Instead, walk the chain510 // until we find the first item in the inner seq, and link that to511 // p, so that nesting seqs has the obvious effect of linking512 // everything together into one long sequential chain.513 Result *r = this;514 while (r->Predecessor)515 r = r->Predecessor.get();516 r->Predecessor = p;517 }518 519 // Each Result will be assigned a variable name in the output code, but not520 // all those variable names will actually be used (e.g. the return value of521 // Builder.CreateStore has void type, so nobody will want to refer to it). To522 // prevent annoying compiler warnings, we track whether each Result's523 // variable name was ever actually mentioned in subsequent statements, so524 // that it can be left out of the final generated code.525 std::string varname() {526 VarNameUsed = true;527 return VarName;528 }529 void setVarname(const StringRef s) { VarName = std::string(s); }530 bool varnameUsed() const { return VarNameUsed; }531 532 // Emit code to generate this result as a Value *.533 virtual std::string asValue() {534 return varname();535 }536 537 // Code generation happens in multiple passes. This method tracks whether a538 // Result has yet been visited in a given pass, without the need for a539 // tedious loop in between passes that goes through and resets a 'visited'540 // flag back to false: you just set Pass=1 the first time round, and Pass=2541 // the second time.542 bool needsVisiting(unsigned Pass) {543 bool ToRet = Visited < Pass;544 Visited = Pass;545 return ToRet;546 }547};548 549// Result subclass that retrieves one of the arguments to the clang builtin550// function. In cases where the argument has pointer type, we call551// EmitPointerWithAlignment and store the result in a variable of type Address,552// so that load and store IR nodes can know the right alignment. Otherwise, we553// call EmitScalarExpr.554//555// There are aggregate parameters in the MVE intrinsics API, but we don't deal556// with them in this Tablegen back end: they only arise in the vld2q/vld4q and557// vst2q/vst4q family, which is few enough that we just write the code by hand558// for those in CGBuiltin.cpp.559class BuiltinArgResult : public Result {560public:561 unsigned ArgNum;562 bool AddressType;563 bool Immediate;564 BuiltinArgResult(unsigned ArgNum, bool AddressType, bool Immediate)565 : ArgNum(ArgNum), AddressType(AddressType), Immediate(Immediate) {}566 void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {567 OS << (AddressType ? "EmitPointerWithAlignment" : "EmitScalarExpr")568 << "(E->getArg(" << ArgNum << "))";569 }570 std::string typeName() const override {571 return AddressType ? "Address" : Result::typeName();572 }573 // Emit code to generate this result as a Value *.574 std::string asValue() override {575 if (AddressType)576 return "(" + varname() + ".emitRawPointer(*this))";577 return Result::asValue();578 }579 bool hasIntegerValue() const override { return Immediate; }580 std::string getIntegerValue(const std::string &IntType) override {581 return "GetIntegerConstantValue<" + IntType + ">(E->getArg(" +582 utostr(ArgNum) + "), getContext())";583 }584};585 586// Result subclass for an integer literal appearing in Tablegen. This may need587// to be turned into an llvm::Result by means of llvm::ConstantInt::get(), or588// it may be used directly as an integer, depending on which IRBuilder method589// it's being passed to.590class IntLiteralResult : public Result {591public:592 const ScalarType *IntegerType;593 uint32_t IntegerValue;594 IntLiteralResult(const ScalarType *IntegerType, uint32_t IntegerValue)595 : IntegerType(IntegerType), IntegerValue(IntegerValue) {}596 void genCode(raw_ostream &OS,597 CodeGenParamAllocator &ParamAlloc) const override {598 OS << "llvm::ConstantInt::get("599 << ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName())600 << ", ";601 OS << ParamAlloc.allocParam(IntegerType->cName(), utostr(IntegerValue))602 << ")";603 }604 bool hasIntegerConstantValue() const override { return true; }605 uint32_t integerConstantValue() const override { return IntegerValue; }606};607 608// Result subclass representing a cast between different integer types. We use609// our own ScalarType abstraction as the representation of the target type,610// which gives both size and signedness.611class IntCastResult : public Result {612public:613 const ScalarType *IntegerType;614 Ptr V;615 IntCastResult(const ScalarType *IntegerType, Ptr V)616 : IntegerType(IntegerType), V(V) {}617 void genCode(raw_ostream &OS,618 CodeGenParamAllocator &ParamAlloc) const override {619 OS << "Builder.CreateIntCast(" << V->varname() << ", "620 << ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName()) << ", "621 << ParamAlloc.allocParam("bool",622 IntegerType->kind() == ScalarTypeKind::SignedInt623 ? "true"624 : "false")625 << ")";626 }627 void morePrerequisites(std::vector<Ptr> &output) const override {628 output.push_back(V);629 }630};631 632// Result subclass representing a cast between different pointer types.633class PointerCastResult : public Result {634public:635 const PointerType *PtrType;636 Ptr V;637 PointerCastResult(const PointerType *PtrType, Ptr V)638 : PtrType(PtrType), V(V) {}639 void genCode(raw_ostream &OS,640 CodeGenParamAllocator &ParamAlloc) const override {641 OS << "Builder.CreatePointerCast(" << V->asValue() << ", "642 << ParamAlloc.allocParam("llvm::Type *", PtrType->llvmName()) << ")";643 }644 void morePrerequisites(std::vector<Ptr> &output) const override {645 output.push_back(V);646 }647};648 649// Result subclass representing a call to an IRBuilder method. Each IRBuilder650// method we want to use will have a Tablegen record giving the method name and651// describing any important details of how to call it, such as whether a652// particular argument should be an integer constant instead of an llvm::Value.653class IRBuilderResult : public Result {654public:655 StringRef CallPrefix;656 std::vector<Ptr> Args;657 std::set<unsigned> AddressArgs;658 std::map<unsigned, std::string> IntegerArgs;659 IRBuilderResult(StringRef CallPrefix, const std::vector<Ptr> &Args,660 const std::set<unsigned> &AddressArgs,661 const std::map<unsigned, std::string> &IntegerArgs)662 : CallPrefix(CallPrefix), Args(Args), AddressArgs(AddressArgs),663 IntegerArgs(IntegerArgs) {}664 void genCode(raw_ostream &OS,665 CodeGenParamAllocator &ParamAlloc) const override {666 OS << CallPrefix;667 const char *Sep = "";668 for (unsigned i = 0, e = Args.size(); i < e; ++i) {669 Ptr Arg = Args[i];670 auto it = IntegerArgs.find(i);671 672 OS << Sep;673 Sep = ", ";674 675 if (it != IntegerArgs.end()) {676 if (Arg->hasIntegerConstantValue())677 OS << "static_cast<" << it->second << ">("678 << ParamAlloc.allocParam(it->second,679 utostr(Arg->integerConstantValue()))680 << ")";681 else if (Arg->hasIntegerValue())682 OS << ParamAlloc.allocParam(it->second,683 Arg->getIntegerValue(it->second));684 } else {685 OS << Arg->varname();686 }687 }688 OS << ")";689 }690 void morePrerequisites(std::vector<Ptr> &output) const override {691 for (unsigned i = 0, e = Args.size(); i < e; ++i) {692 Ptr Arg = Args[i];693 if (IntegerArgs.find(i) != IntegerArgs.end())694 continue;695 output.push_back(Arg);696 }697 }698};699 700// Result subclass representing making an Address out of a Value.701class AddressResult : public Result {702public:703 Ptr Arg;704 const Type *Ty;705 unsigned Align;706 AddressResult(Ptr Arg, const Type *Ty, unsigned Align)707 : Arg(Arg), Ty(Ty), Align(Align) {}708 void genCode(raw_ostream &OS,709 CodeGenParamAllocator &ParamAlloc) const override {710 OS << "Address(" << Arg->varname() << ", " << Ty->llvmName()711 << ", CharUnits::fromQuantity(" << Align << "))";712 }713 std::string typeName() const override {714 return "Address";715 }716 void morePrerequisites(std::vector<Ptr> &output) const override {717 output.push_back(Arg);718 }719};720 721// Result subclass representing a call to an IR intrinsic, which we first have722// to look up using an Intrinsic::ID constant and an array of types.723class IRIntrinsicResult : public Result {724public:725 std::string IntrinsicID;726 std::vector<const Type *> ParamTypes;727 std::vector<Ptr> Args;728 IRIntrinsicResult(StringRef IntrinsicID,729 const std::vector<const Type *> &ParamTypes,730 const std::vector<Ptr> &Args)731 : IntrinsicID(std::string(IntrinsicID)), ParamTypes(ParamTypes),732 Args(Args) {}733 void genCode(raw_ostream &OS,734 CodeGenParamAllocator &ParamAlloc) const override {735 std::string IntNo = ParamAlloc.allocParam(736 "Intrinsic::ID", "Intrinsic::" + IntrinsicID);737 OS << "Builder.CreateCall(CGM.getIntrinsic(" << IntNo;738 if (!ParamTypes.empty()) {739 OS << ", {";740 const char *Sep = "";741 for (auto T : ParamTypes) {742 OS << Sep << ParamAlloc.allocParam("llvm::Type *", T->llvmName());743 Sep = ", ";744 }745 OS << "}";746 }747 OS << "), {";748 const char *Sep = "";749 for (auto Arg : Args) {750 OS << Sep << Arg->asValue();751 Sep = ", ";752 }753 OS << "})";754 }755 void morePrerequisites(std::vector<Ptr> &output) const override {756 llvm::append_range(output, Args);757 }758};759 760// Result subclass that generates761// Builder.getIsFPConstrained() ? <Standard> : <StrictFp>762class StrictFpAltResult : public Result {763public:764 Ptr Standard;765 Ptr StrictFp;766 StrictFpAltResult(Ptr Standard, Ptr StrictFp)767 : Standard(Standard), StrictFp(StrictFp) {}768 void genCode(raw_ostream &OS,769 CodeGenParamAllocator &ParamAlloc) const override {770 OS << "!Builder.getIsFPConstrained() ? ";771 Standard->genCode(OS, ParamAlloc);772 OS << " : ";773 StrictFp->genCode(OS, ParamAlloc);774 }775 void morePrerequisites(std::vector<Ptr> &output) const override {776 Standard->morePrerequisites(output);777 }778};779 780// Result subclass that specifies a type, for use in IRBuilder operations such781// as CreateBitCast that take a type argument.782class TypeResult : public Result {783public:784 const Type *T;785 TypeResult(const Type *T) : T(T) {}786 void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {787 OS << T->llvmName();788 }789 std::string typeName() const override {790 return "llvm::Type *";791 }792};793 794// -----------------------------------------------------------------------------795// Class that describes a single ACLE intrinsic.796//797// A Tablegen record will typically describe more than one ACLE intrinsic, by798// means of setting the 'list<Type> Params' field to a list of multiple799// parameter types, so as to define vaddq_{s8,u8,...,f16,f32} all in one go.800// We'll end up with one instance of ACLEIntrinsic for *each* parameter type,801// rather than a single one for all of them. Hence, the constructor takes both802// a Tablegen record and the current value of the parameter type.803 804class ACLEIntrinsic {805 // Structure documenting that one of the intrinsic's arguments is required to806 // be a compile-time constant integer, and what constraints there are on its807 // value. Used when generating Sema checking code.808 struct ImmediateArg {809 enum class BoundsType { ExplicitRange, UInt };810 BoundsType boundsType;811 int64_t i1, i2;812 StringRef ExtraCheckType, ExtraCheckArgs;813 const Type *ArgType;814 };815 816 // For polymorphic intrinsics, FullName is the explicit name that uniquely817 // identifies this variant of the intrinsic, and ShortName is the name it818 // shares with at least one other intrinsic.819 std::string ShortName, FullName;820 821 // Name of the architecture extension, used in the Clang builtin name822 StringRef BuiltinExtension;823 824 // A very small number of intrinsics _only_ have a polymorphic825 // variant (vuninitializedq taking an unevaluated argument).826 bool PolymorphicOnly;827 828 // Another rarely-used flag indicating that the builtin doesn't829 // evaluate its argument(s) at all.830 bool NonEvaluating;831 832 // True if the intrinsic needs only the C header part (no codegen, semantic833 // checks, etc). Used for redeclaring MVE intrinsics in the arm_cde.h header.834 bool HeaderOnly;835 836 const Type *ReturnType;837 std::vector<const Type *> ArgTypes;838 std::map<unsigned, ImmediateArg> ImmediateArgs;839 Result::Ptr Code;840 841 std::map<std::string, std::string> CustomCodeGenArgs;842 843 // Recursive function that does the internals of code generation.844 void genCodeDfs(Result::Ptr V, std::list<Result::Ptr> &Used,845 unsigned Pass) const {846 if (!V->needsVisiting(Pass))847 return;848 849 for (Result::Ptr W : V->prerequisites())850 genCodeDfs(W, Used, Pass);851 852 Used.push_back(V);853 }854 855public:856 const std::string &shortName() const { return ShortName; }857 const std::string &fullName() const { return FullName; }858 StringRef builtinExtension() const { return BuiltinExtension; }859 const Type *returnType() const { return ReturnType; }860 const std::vector<const Type *> &argTypes() const { return ArgTypes; }861 bool requiresFloat() const {862 if (ReturnType->requiresFloat())863 return true;864 for (const Type *T : ArgTypes)865 if (T->requiresFloat())866 return true;867 return false;868 }869 bool requiresMVE() const {870 return ReturnType->requiresMVE() ||871 any_of(ArgTypes, [](const Type *T) { return T->requiresMVE(); });872 }873 bool polymorphic() const { return ShortName != FullName; }874 bool polymorphicOnly() const { return PolymorphicOnly; }875 bool nonEvaluating() const { return NonEvaluating; }876 bool headerOnly() const { return HeaderOnly; }877 878 // External entry point for code generation, called from EmitterBase.879 void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc,880 unsigned Pass) const {881 assert(!headerOnly() && "Called genCode for header-only intrinsic");882 if (!hasCode()) {883 for (auto kv : CustomCodeGenArgs)884 OS << " " << kv.first << " = " << kv.second << ";\n";885 OS << " break; // custom code gen\n";886 return;887 }888 std::list<Result::Ptr> Used;889 genCodeDfs(Code, Used, Pass);890 891 unsigned varindex = 0;892 for (Result::Ptr V : Used)893 if (V->varnameUsed())894 V->setVarname("Val" + utostr(varindex++));895 896 for (Result::Ptr V : Used) {897 OS << " ";898 if (V == Used.back()) {899 assert(!V->varnameUsed());900 OS << "return "; // FIXME: what if the top-level thing is void?901 } else if (V->varnameUsed()) {902 std::string Type = V->typeName();903 OS << V->typeName();904 if (!StringRef(Type).ends_with("*"))905 OS << " ";906 OS << V->varname() << " = ";907 }908 V->genCode(OS, ParamAlloc);909 OS << ";\n";910 }911 }912 bool hasCode() const { return Code != nullptr; }913 914 static std::string signedHexLiteral(const APInt &iOrig) {915 APInt i = iOrig.trunc(64);916 SmallString<40> s;917 i.toString(s, 16, true, true);918 return std::string(s);919 }920 921 std::string genSema() const {922 assert(!headerOnly() && "Called genSema for header-only intrinsic");923 std::vector<std::string> SemaChecks;924 925 for (const auto &kv : ImmediateArgs) {926 const ImmediateArg &IA = kv.second;927 928 APInt lo(128, 0), hi(128, 0);929 switch (IA.boundsType) {930 case ImmediateArg::BoundsType::ExplicitRange:931 lo = IA.i1;932 hi = IA.i2;933 break;934 case ImmediateArg::BoundsType::UInt:935 lo = 0;936 hi = APInt::getMaxValue(IA.i1).zext(128);937 break;938 }939 940 std::string Index = utostr(kv.first);941 942 // Emit a range check if the legal range of values for the943 // immediate is smaller than the _possible_ range of values for944 // its type.945 unsigned ArgTypeBits = IA.ArgType->sizeInBits();946 APInt ArgTypeRange = APInt::getMaxValue(ArgTypeBits).zext(128);947 APInt ActualRange = (hi - lo).trunc(64).sext(128);948 if (ActualRange.ult(ArgTypeRange))949 SemaChecks.push_back("SemaRef.BuiltinConstantArgRange(TheCall, " +950 Index + ", " + signedHexLiteral(lo) + ", " +951 signedHexLiteral(hi) + ")");952 953 if (!IA.ExtraCheckType.empty()) {954 std::string Suffix;955 if (!IA.ExtraCheckArgs.empty()) {956 std::string tmp;957 StringRef Arg = IA.ExtraCheckArgs;958 if (Arg == "!lanesize") {959 tmp = utostr(IA.ArgType->sizeInBits());960 Arg = tmp;961 }962 Suffix = (Twine(", ") + Arg).str();963 }964 SemaChecks.push_back((Twine("SemaRef.BuiltinConstantArg") +965 IA.ExtraCheckType + "(TheCall, " + Index +966 Suffix + ")")967 .str());968 }969 970 assert(!SemaChecks.empty());971 }972 if (SemaChecks.empty())973 return "";974 return join(std::begin(SemaChecks), std::end(SemaChecks),975 " ||\n ") +976 ";\n";977 }978 979 ACLEIntrinsic(EmitterBase &ME, const Record *R, const Type *Param);980};981 982// -----------------------------------------------------------------------------983// The top-level class that holds all the state from analyzing the entire984// Tablegen input.985 986class EmitterBase {987protected:988 // EmitterBase holds a collection of all the types we've instantiated.989 VoidType Void;990 std::map<std::string, std::unique_ptr<ScalarType>> ScalarTypes;991 std::map<std::tuple<ScalarTypeKind, unsigned, unsigned>,992 std::unique_ptr<VectorType>>993 VectorTypes;994 std::map<std::pair<std::string, unsigned>, std::unique_ptr<MultiVectorType>>995 MultiVectorTypes;996 std::map<unsigned, std::unique_ptr<PredicateType>> PredicateTypes;997 std::map<std::string, std::unique_ptr<PointerType>> PointerTypes;998 999 // And all the ACLEIntrinsic instances we've created.1000 std::map<std::string, std::unique_ptr<ACLEIntrinsic>> ACLEIntrinsics;1001 1002public:1003 // Methods to create a Type object, or return the right existing one from the1004 // maps stored in this object.1005 const VoidType *getVoidType() { return &Void; }1006 const ScalarType *getScalarType(StringRef Name) {1007 return ScalarTypes[std::string(Name)].get();1008 }1009 const ScalarType *getScalarType(const Record *R) {1010 return getScalarType(R->getName());1011 }1012 const VectorType *getVectorType(const ScalarType *ST, unsigned Lanes) {1013 std::tuple<ScalarTypeKind, unsigned, unsigned> key(ST->kind(),1014 ST->sizeInBits(), Lanes);1015 auto [It, Inserted] = VectorTypes.try_emplace(key);1016 if (Inserted)1017 It->second = std::make_unique<VectorType>(ST, Lanes);1018 return It->second.get();1019 }1020 const VectorType *getVectorType(const ScalarType *ST) {1021 return getVectorType(ST, 128 / ST->sizeInBits());1022 }1023 const MultiVectorType *getMultiVectorType(unsigned Registers,1024 const VectorType *VT) {1025 std::pair<std::string, unsigned> key(VT->cNameBase(), Registers);1026 auto [It, Inserted] = MultiVectorTypes.try_emplace(key);1027 if (Inserted)1028 It->second = std::make_unique<MultiVectorType>(Registers, VT);1029 return It->second.get();1030 }1031 const PredicateType *getPredicateType(unsigned Lanes) {1032 unsigned key = Lanes;1033 auto [It, Inserted] = PredicateTypes.try_emplace(key);1034 if (Inserted)1035 It->second = std::make_unique<PredicateType>(Lanes);1036 return It->second.get();1037 }1038 const PointerType *getPointerType(const Type *T, bool Const) {1039 PointerType PT(T, Const);1040 std::string key = PT.cName();1041 auto [It, Inserted] = PointerTypes.try_emplace(key);1042 if (Inserted)1043 It->second = std::make_unique<PointerType>(PT);1044 return It->second.get();1045 }1046 1047 // Methods to construct a type from various pieces of Tablegen. These are1048 // always called in the context of setting up a particular ACLEIntrinsic, so1049 // there's always an ambient parameter type (because we're iterating through1050 // the Params list in the Tablegen record for the intrinsic), which is used1051 // to expand Tablegen classes like 'Vector' which mean something different in1052 // each member of a parametric family.1053 const Type *getType(const Record *R, const Type *Param);1054 const Type *getType(const DagInit *D, const Type *Param);1055 const Type *getType(const Init *I, const Type *Param);1056 1057 // Functions that translate the Tablegen representation of an intrinsic's1058 // code generation into a collection of Value objects (which will then be1059 // reprocessed to read out the actual C++ code included by CGBuiltin.cpp).1060 Result::Ptr getCodeForDag(const DagInit *D, const Result::Scope &Scope,1061 const Type *Param);1062 Result::Ptr getCodeForDagArg(const DagInit *D, unsigned ArgNum,1063 const Result::Scope &Scope, const Type *Param);1064 Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType, bool Promote,1065 bool Immediate);1066 1067 void GroupSemaChecks(std::map<std::string, std::set<std::string>> &Checks);1068 1069 // Constructor and top-level functions.1070 1071 EmitterBase(const RecordKeeper &Records);1072 virtual ~EmitterBase() = default;1073 1074 virtual void EmitHeader(raw_ostream &OS) = 0;1075 virtual void EmitBuiltinDef(raw_ostream &OS) = 0;1076 virtual void EmitBuiltinSema(raw_ostream &OS) = 0;1077 void EmitBuiltinCG(raw_ostream &OS);1078 void EmitBuiltinAliases(raw_ostream &OS);1079};1080 1081const Type *EmitterBase::getType(const Init *I, const Type *Param) {1082 if (const auto *Dag = dyn_cast<DagInit>(I))1083 return getType(Dag, Param);1084 if (const auto *Def = dyn_cast<DefInit>(I))1085 return getType(Def->getDef(), Param);1086 1087 PrintFatalError("Could not convert this value into a type");1088}1089 1090const Type *EmitterBase::getType(const Record *R, const Type *Param) {1091 // Pass to a subfield of any wrapper records. We don't expect more than one1092 // of these: immediate operands are used as plain numbers rather than as1093 // llvm::Value, so it's meaningless to promote their type anyway.1094 if (R->isSubClassOf("Immediate"))1095 R = R->getValueAsDef("type");1096 else if (R->isSubClassOf("unpromoted"))1097 R = R->getValueAsDef("underlying_type");1098 1099 if (R->getName() == "Void")1100 return getVoidType();1101 if (R->isSubClassOf("PrimitiveType"))1102 return getScalarType(R);1103 if (R->isSubClassOf("ComplexType"))1104 return getType(R->getValueAsDag("spec"), Param);1105 1106 PrintFatalError(R->getLoc(), "Could not convert this record into a type");1107}1108 1109const Type *EmitterBase::getType(const DagInit *D, const Type *Param) {1110 // The meat of the getType system: types in the Tablegen are represented by a1111 // dag whose operators select sub-cases of this function.1112 1113 const Record *Op = cast<DefInit>(D->getOperator())->getDef();1114 if (!Op->isSubClassOf("ComplexTypeOp"))1115 PrintFatalError(1116 "Expected ComplexTypeOp as dag operator in type expression");1117 1118 if (Op->getName() == "CTO_Parameter") {1119 if (isa<VoidType>(Param))1120 PrintFatalError("Parametric type in unparametrised context");1121 return Param;1122 }1123 1124 if (Op->getName() == "CTO_Vec") {1125 const Type *Element = getType(D->getArg(0), Param);1126 if (D->getNumArgs() == 1) {1127 return getVectorType(cast<ScalarType>(Element));1128 } else {1129 const Type *ExistingVector = getType(D->getArg(1), Param);1130 return getVectorType(cast<ScalarType>(Element),1131 cast<VectorType>(ExistingVector)->lanes());1132 }1133 }1134 1135 if (Op->getName() == "CTO_Pred") {1136 const Type *Element = getType(D->getArg(0), Param);1137 return getPredicateType(128 / Element->sizeInBits());1138 }1139 1140 if (Op->isSubClassOf("CTO_Tuple")) {1141 unsigned Registers = Op->getValueAsInt("n");1142 const Type *Element = getType(D->getArg(0), Param);1143 return getMultiVectorType(Registers, cast<VectorType>(Element));1144 }1145 1146 if (Op->isSubClassOf("CTO_Pointer")) {1147 const Type *Pointee = getType(D->getArg(0), Param);1148 return getPointerType(Pointee, Op->getValueAsBit("const"));1149 }1150 1151 if (Op->getName() == "CTO_CopyKind") {1152 const ScalarType *STSize = cast<ScalarType>(getType(D->getArg(0), Param));1153 const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(1), Param));1154 for (const auto &kv : ScalarTypes) {1155 const ScalarType *RT = kv.second.get();1156 if (RT->kind() == STKind->kind() && RT->sizeInBits() == STSize->sizeInBits())1157 return RT;1158 }1159 PrintFatalError("Cannot find a type to satisfy CopyKind");1160 }1161 1162 if (Op->isSubClassOf("CTO_ScaleSize")) {1163 const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(0), Param));1164 int Num = Op->getValueAsInt("num"), Denom = Op->getValueAsInt("denom");1165 unsigned DesiredSize = STKind->sizeInBits() * Num / Denom;1166 for (const auto &kv : ScalarTypes) {1167 const ScalarType *RT = kv.second.get();1168 if (RT->kind() == STKind->kind() && RT->sizeInBits() == DesiredSize)1169 return RT;1170 }1171 PrintFatalError("Cannot find a type to satisfy ScaleSize");1172 }1173 1174 PrintFatalError("Bad operator in type dag expression");1175}1176 1177Result::Ptr EmitterBase::getCodeForDag(const DagInit *D,1178 const Result::Scope &Scope,1179 const Type *Param) {1180 const Record *Op = cast<DefInit>(D->getOperator())->getDef();1181 1182 if (Op->getName() == "seq") {1183 Result::Scope SubScope = Scope;1184 Result::Ptr PrevV = nullptr;1185 for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i) {1186 // We don't use getCodeForDagArg here, because the argument name1187 // has different semantics in a seq1188 Result::Ptr V =1189 getCodeForDag(cast<DagInit>(D->getArg(i)), SubScope, Param);1190 StringRef ArgName = D->getArgNameStr(i);1191 if (!ArgName.empty())1192 SubScope[std::string(ArgName)] = V;1193 if (PrevV)1194 V->setPredecessor(PrevV);1195 PrevV = V;1196 }1197 return PrevV;1198 } else if (Op->isSubClassOf("Type")) {1199 if (D->getNumArgs() != 1)1200 PrintFatalError("Type casts should have exactly one argument");1201 const Type *CastType = getType(Op, Param);1202 Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);1203 if (const auto *ST = dyn_cast<ScalarType>(CastType)) {1204 if (!ST->requiresFloat()) {1205 if (Arg->hasIntegerConstantValue())1206 return std::make_shared<IntLiteralResult>(1207 ST, Arg->integerConstantValue());1208 else1209 return std::make_shared<IntCastResult>(ST, Arg);1210 }1211 } else if (const auto *PT = dyn_cast<PointerType>(CastType)) {1212 return std::make_shared<PointerCastResult>(PT, Arg);1213 }1214 PrintFatalError("Unsupported type cast");1215 } else if (Op->getName() == "address") {1216 if (D->getNumArgs() != 2)1217 PrintFatalError("'address' should have two arguments");1218 Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);1219 1220 const Type *Ty = nullptr;1221 if (const auto *DI = dyn_cast<DagInit>(D->getArg(0)))1222 if (auto *PTy = dyn_cast<PointerType>(getType(DI->getOperator(), Param)))1223 Ty = PTy->getPointeeType();1224 if (!Ty)1225 PrintFatalError("'address' pointer argument should be a pointer");1226 1227 unsigned Alignment;1228 if (const auto *II = dyn_cast<IntInit>(D->getArg(1))) {1229 Alignment = II->getValue();1230 } else {1231 PrintFatalError("'address' alignment argument should be an integer");1232 }1233 return std::make_shared<AddressResult>(Arg, Ty, Alignment);1234 } else if (Op->getName() == "unsignedflag") {1235 if (D->getNumArgs() != 1)1236 PrintFatalError("unsignedflag should have exactly one argument");1237 const Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef();1238 if (!TypeRec->isSubClassOf("Type"))1239 PrintFatalError("unsignedflag's argument should be a type");1240 if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) {1241 return std::make_shared<IntLiteralResult>(1242 getScalarType("u32"), ST->kind() == ScalarTypeKind::UnsignedInt);1243 } else {1244 PrintFatalError("unsignedflag's argument should be a scalar type");1245 }1246 } else if (Op->getName() == "bitsize") {1247 if (D->getNumArgs() != 1)1248 PrintFatalError("bitsize should have exactly one argument");1249 const Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef();1250 if (!TypeRec->isSubClassOf("Type"))1251 PrintFatalError("bitsize's argument should be a type");1252 if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) {1253 return std::make_shared<IntLiteralResult>(getScalarType("u32"),1254 ST->sizeInBits());1255 } else {1256 PrintFatalError("bitsize's argument should be a scalar type");1257 }1258 } else {1259 std::vector<Result::Ptr> Args;1260 for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i)1261 Args.push_back(getCodeForDagArg(D, i, Scope, Param));1262 1263 auto GenIRBuilderBase = [&](const Record *Op) -> Result::Ptr {1264 assert(Op->isSubClassOf("IRBuilderBase") &&1265 "Expected IRBuilderBase in GenIRBuilderBase\n");1266 std::set<unsigned> AddressArgs;1267 std::map<unsigned, std::string> IntegerArgs;1268 for (const Record *sp : Op->getValueAsListOfDefs("special_params")) {1269 unsigned Index = sp->getValueAsInt("index");1270 if (sp->isSubClassOf("IRBuilderAddrParam")) {1271 AddressArgs.insert(Index);1272 } else if (sp->isSubClassOf("IRBuilderIntParam")) {1273 IntegerArgs[Index] = std::string(sp->getValueAsString("type"));1274 }1275 }1276 return std::make_shared<IRBuilderResult>(Op->getValueAsString("prefix"),1277 Args, AddressArgs, IntegerArgs);1278 };1279 auto GenIRIntBase = [&](const Record *Op) -> Result::Ptr {1280 assert(Op->isSubClassOf("IRIntBase") &&1281 "Expected IRIntBase in GenIRIntBase\n");1282 std::vector<const Type *> ParamTypes;1283 for (const Record *RParam : Op->getValueAsListOfDefs("params"))1284 ParamTypes.push_back(getType(RParam, Param));1285 std::string IntName = std::string(Op->getValueAsString("intname"));1286 if (Op->getValueAsBit("appendKind"))1287 IntName += "_" + toLetter(cast<ScalarType>(Param)->kind());1288 return std::make_shared<IRIntrinsicResult>(IntName, ParamTypes, Args);1289 };1290 1291 if (Op->isSubClassOf("IRBuilderBase")) {1292 return GenIRBuilderBase(Op);1293 } else if (Op->isSubClassOf("IRIntBase")) {1294 return GenIRIntBase(Op);1295 } else if (Op->isSubClassOf("strictFPAlt")) {1296 auto StardardBuilder = Op->getValueAsDef("standard");1297 Result::Ptr Standard = StardardBuilder->isSubClassOf("IRBuilder")1298 ? GenIRBuilderBase(StardardBuilder)1299 : GenIRIntBase(StardardBuilder);1300 Result::Ptr StrictFp = GenIRIntBase(Op->getValueAsDef("strictfp"));1301 return std::make_shared<StrictFpAltResult>(Standard, StrictFp);1302 } else {1303 PrintFatalError("Unsupported dag node " + Op->getName());1304 }1305 }1306}1307 1308Result::Ptr EmitterBase::getCodeForDagArg(const DagInit *D, unsigned ArgNum,1309 const Result::Scope &Scope,1310 const Type *Param) {1311 const Init *Arg = D->getArg(ArgNum);1312 StringRef Name = D->getArgNameStr(ArgNum);1313 1314 if (!Name.empty()) {1315 if (!isa<UnsetInit>(Arg))1316 PrintFatalError(1317 "dag operator argument should not have both a value and a name");1318 auto it = Scope.find(Name);1319 if (it == Scope.end())1320 PrintFatalError("unrecognized variable name '" + Name + "'");1321 return it->second;1322 }1323 1324 // Sometimes the Arg is a bit. Prior to multiclass template argument1325 // checking, integers would sneak through the bit declaration,1326 // but now they really are bits.1327 if (const auto *BI = dyn_cast<BitInit>(Arg))1328 return std::make_shared<IntLiteralResult>(getScalarType("u32"),1329 BI->getValue());1330 1331 if (const auto *II = dyn_cast<IntInit>(Arg))1332 return std::make_shared<IntLiteralResult>(getScalarType("u32"),1333 II->getValue());1334 1335 if (const auto *DI = dyn_cast<DagInit>(Arg))1336 return getCodeForDag(DI, Scope, Param);1337 1338 if (const auto *DI = dyn_cast<DefInit>(Arg)) {1339 const Record *Rec = DI->getDef();1340 if (Rec->isSubClassOf("Type")) {1341 const Type *T = getType(Rec, Param);1342 return std::make_shared<TypeResult>(T);1343 }1344 }1345 1346 PrintError("bad DAG argument type for code generation");1347 PrintNote("DAG: " + D->getAsString());1348 if (const auto *Typed = dyn_cast<TypedInit>(Arg))1349 PrintNote("argument type: " + Typed->getType()->getAsString());1350 PrintFatalNote("argument number " + Twine(ArgNum) + ": " + Arg->getAsString());1351}1352 1353Result::Ptr EmitterBase::getCodeForArg(unsigned ArgNum, const Type *ArgType,1354 bool Promote, bool Immediate) {1355 Result::Ptr V = std::make_shared<BuiltinArgResult>(1356 ArgNum, isa<PointerType>(ArgType), Immediate);1357 1358 if (Promote) {1359 if (const auto *ST = dyn_cast<ScalarType>(ArgType)) {1360 if (ST->isInteger() && ST->sizeInBits() < 32)1361 V = std::make_shared<IntCastResult>(getScalarType("u32"), V);1362 } else if (const auto *PT = dyn_cast<PredicateType>(ArgType)) {1363 V = std::make_shared<IntCastResult>(getScalarType("u32"), V);1364 V = std::make_shared<IRIntrinsicResult>("arm_mve_pred_i2v",1365 std::vector<const Type *>{PT},1366 std::vector<Result::Ptr>{V});1367 }1368 }1369 1370 return V;1371}1372 1373ACLEIntrinsic::ACLEIntrinsic(EmitterBase &ME, const Record *R,1374 const Type *Param)1375 : ReturnType(ME.getType(R->getValueAsDef("ret"), Param)) {1376 // Derive the intrinsic's full name, by taking the name of the1377 // Tablegen record (or override) and appending the suffix from its1378 // parameter type. (If the intrinsic is unparametrised, its1379 // parameter type will be given as Void, which returns the empty1380 // string for acleSuffix.)1381 StringRef BaseName =1382 (R->isSubClassOf("NameOverride") ? R->getValueAsString("basename")1383 : R->getName());1384 StringRef overrideLetter = R->getValueAsString("overrideKindLetter");1385 FullName =1386 (Twine(BaseName) + Param->acleSuffix(std::string(overrideLetter))).str();1387 1388 // Derive the intrinsic's polymorphic name, by removing components from the1389 // full name as specified by its 'pnt' member ('polymorphic name type'),1390 // which indicates how many type suffixes to remove, and any other piece of1391 // the name that should be removed.1392 const Record *PolymorphicNameType = R->getValueAsDef("pnt");1393 SmallVector<StringRef, 8> NameParts;1394 StringRef(FullName).split(NameParts, '_');1395 for (unsigned i = 0, e = PolymorphicNameType->getValueAsInt(1396 "NumTypeSuffixesToDiscard");1397 i < e; ++i)1398 NameParts.pop_back();1399 if (!PolymorphicNameType->isValueUnset("ExtraSuffixToDiscard")) {1400 StringRef ExtraSuffix =1401 PolymorphicNameType->getValueAsString("ExtraSuffixToDiscard");1402 auto it = NameParts.end();1403 while (it != NameParts.begin()) {1404 --it;1405 if (*it == ExtraSuffix) {1406 NameParts.erase(it);1407 break;1408 }1409 }1410 }1411 ShortName = join(std::begin(NameParts), std::end(NameParts), "_");1412 1413 BuiltinExtension = R->getValueAsString("builtinExtension");1414 1415 PolymorphicOnly = R->getValueAsBit("polymorphicOnly");1416 NonEvaluating = R->getValueAsBit("nonEvaluating");1417 HeaderOnly = R->getValueAsBit("headerOnly");1418 1419 // Process the intrinsic's argument list.1420 const DagInit *ArgsDag = R->getValueAsDag("args");1421 Result::Scope Scope;1422 for (unsigned i = 0, e = ArgsDag->getNumArgs(); i < e; ++i) {1423 const Init *TypeInit = ArgsDag->getArg(i);1424 1425 bool Promote = true;1426 if (const auto *TypeDI = dyn_cast<DefInit>(TypeInit))1427 if (TypeDI->getDef()->isSubClassOf("unpromoted"))1428 Promote = false;1429 1430 // Work out the type of the argument, for use in the function prototype in1431 // the header file.1432 const Type *ArgType = ME.getType(TypeInit, Param);1433 ArgTypes.push_back(ArgType);1434 1435 // If the argument is a subclass of Immediate, record the details about1436 // what values it can take, for Sema checking.1437 bool Immediate = false;1438 if (const auto *TypeDI = dyn_cast<DefInit>(TypeInit)) {1439 const Record *TypeRec = TypeDI->getDef();1440 if (TypeRec->isSubClassOf("Immediate")) {1441 Immediate = true;1442 1443 const Record *Bounds = TypeRec->getValueAsDef("bounds");1444 ImmediateArg &IA = ImmediateArgs[i];1445 if (Bounds->isSubClassOf("IB_ConstRange")) {1446 IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1447 IA.i1 = Bounds->getValueAsInt("lo");1448 IA.i2 = Bounds->getValueAsInt("hi");1449 } else if (Bounds->getName() == "IB_UEltValue") {1450 IA.boundsType = ImmediateArg::BoundsType::UInt;1451 IA.i1 = Param->sizeInBits();1452 } else if (Bounds->getName() == "IB_LaneIndex") {1453 IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1454 IA.i1 = 0;1455 IA.i2 = 128 / Param->sizeInBits() - 1;1456 } else if (Bounds->isSubClassOf("IB_EltBit")) {1457 IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;1458 IA.i1 = Bounds->getValueAsInt("base");1459 const Type *T = ME.getType(Bounds->getValueAsDef("type"), Param);1460 IA.i2 = IA.i1 + T->sizeInBits() - 1;1461 } else {1462 PrintFatalError("unrecognised ImmediateBounds subclass");1463 }1464 1465 IA.ArgType = ArgType;1466 1467 if (!TypeRec->isValueUnset("extra")) {1468 IA.ExtraCheckType = TypeRec->getValueAsString("extra");1469 if (!TypeRec->isValueUnset("extraarg"))1470 IA.ExtraCheckArgs = TypeRec->getValueAsString("extraarg");1471 }1472 }1473 }1474 1475 // The argument will usually have a name in the arguments dag, which goes1476 // into the variable-name scope that the code gen will refer to.1477 StringRef ArgName = ArgsDag->getArgNameStr(i);1478 if (!ArgName.empty())1479 Scope[std::string(ArgName)] =1480 ME.getCodeForArg(i, ArgType, Promote, Immediate);1481 }1482 1483 // Finally, go through the codegen dag and translate it into a Result object1484 // (with an arbitrary DAG of depended-on Results hanging off it).1485 const DagInit *CodeDag = R->getValueAsDag("codegen");1486 const Record *MainOp = cast<DefInit>(CodeDag->getOperator())->getDef();1487 if (MainOp->isSubClassOf("CustomCodegen")) {1488 // Or, if it's the special case of CustomCodegen, just accumulate1489 // a list of parameters we're going to assign to variables before1490 // breaking from the loop.1491 CustomCodeGenArgs["CustomCodeGenType"] =1492 (Twine("CustomCodeGen::") + MainOp->getValueAsString("type")).str();1493 for (unsigned i = 0, e = CodeDag->getNumArgs(); i < e; ++i) {1494 StringRef Name = CodeDag->getArgNameStr(i);1495 if (Name.empty()) {1496 PrintFatalError("Operands to CustomCodegen should have names");1497 } else if (const auto *II = dyn_cast<IntInit>(CodeDag->getArg(i))) {1498 CustomCodeGenArgs[std::string(Name)] = itostr(II->getValue());1499 } else if (const auto *SI = dyn_cast<StringInit>(CodeDag->getArg(i))) {1500 CustomCodeGenArgs[std::string(Name)] = std::string(SI->getValue());1501 } else {1502 PrintFatalError("Operands to CustomCodegen should be integers");1503 }1504 }1505 } else {1506 Code = ME.getCodeForDag(CodeDag, Scope, Param);1507 }1508}1509 1510EmitterBase::EmitterBase(const RecordKeeper &Records) {1511 // Construct the whole EmitterBase.1512 1513 // First, look up all the instances of PrimitiveType. This gives us the list1514 // of vector typedefs we have to put in arm_mve.h, and also allows us to1515 // collect all the useful ScalarType instances into a big list so that we can1516 // use it for operations such as 'find the unsigned version of this signed1517 // integer type'.1518 for (const Record *R : Records.getAllDerivedDefinitions("PrimitiveType"))1519 ScalarTypes[std::string(R->getName())] = std::make_unique<ScalarType>(R);1520 1521 // Now go through the instances of Intrinsic, and for each one, iterate1522 // through its list of type parameters making an ACLEIntrinsic for each one.1523 for (const Record *R : Records.getAllDerivedDefinitions("Intrinsic")) {1524 for (const Record *RParam : R->getValueAsListOfDefs("params")) {1525 const Type *Param = getType(RParam, getVoidType());1526 auto Intrinsic = std::make_unique<ACLEIntrinsic>(*this, R, Param);1527 ACLEIntrinsics[Intrinsic->fullName()] = std::move(Intrinsic);1528 }1529 }1530}1531 1532/// A wrapper on raw_string_ostream that contains its own buffer rather than1533/// having to point it at one elsewhere. (In other words, it works just like1534/// std::ostringstream; also, this makes it convenient to declare a whole array1535/// of them at once.)1536///1537/// We have to set this up using multiple inheritance, to ensure that the1538/// string member has been constructed before raw_string_ostream's constructor1539/// is given a pointer to it.1540class string_holder {1541protected:1542 std::string S;1543};1544class raw_self_contained_string_ostream : private string_holder,1545 public raw_string_ostream {1546public:1547 raw_self_contained_string_ostream() : raw_string_ostream(S) {}1548};1549 1550const char LLVMLicenseHeader[] =1551 " *\n"1552 " *\n"1553 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM"1554 " Exceptions.\n"1555 " * See https://llvm.org/LICENSE.txt for license information.\n"1556 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"1557 " *\n"1558 " *===-----------------------------------------------------------------"1559 "------===\n"1560 " */\n"1561 "\n";1562 1563// Machinery for the grouping of intrinsics by similar codegen.1564//1565// The general setup is that 'MergeableGroup' stores the things that a set of1566// similarly shaped intrinsics have in common: the text of their code1567// generation, and the number and type of their parameter variables.1568// MergeableGroup is the key in a std::map whose value is a set of1569// OutputIntrinsic, which stores the ways in which a particular intrinsic1570// specializes the MergeableGroup's generic description: the function name and1571// the _values_ of the parameter variables.1572 1573struct ComparableStringVector : std::vector<std::string> {1574 // Infrastructure: a derived class of vector<string> which comes with an1575 // ordering, so that it can be used as a key in maps and an element in sets.1576 // There's no requirement on the ordering beyond being deterministic.1577 bool operator<(const ComparableStringVector &rhs) const {1578 if (size() != rhs.size())1579 return size() < rhs.size();1580 for (size_t i = 0, e = size(); i < e; ++i)1581 if ((*this)[i] != rhs[i])1582 return (*this)[i] < rhs[i];1583 return false;1584 }1585};1586 1587struct OutputIntrinsic {1588 const ACLEIntrinsic *Int;1589 std::string Name;1590 ComparableStringVector ParamValues;1591 bool operator<(const OutputIntrinsic &rhs) const {1592 return std::tie(Name, ParamValues) < std::tie(rhs.Name, rhs.ParamValues);1593 }1594};1595struct MergeableGroup {1596 std::string Code;1597 ComparableStringVector ParamTypes;1598 bool operator<(const MergeableGroup &rhs) const {1599 return std::tie(Code, ParamTypes) < std::tie(rhs.Code, rhs.ParamTypes);1600 }1601};1602 1603void EmitterBase::EmitBuiltinCG(raw_ostream &OS) {1604 // Pass 1: generate code for all the intrinsics as if every type or constant1605 // that can possibly be abstracted out into a parameter variable will be.1606 // This identifies the sets of intrinsics we'll group together into a single1607 // piece of code generation.1608 1609 std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroupsPrelim;1610 1611 for (const auto &kv : ACLEIntrinsics) {1612 const ACLEIntrinsic &Int = *kv.second;1613 if (Int.headerOnly())1614 continue;1615 1616 MergeableGroup MG;1617 OutputIntrinsic OI;1618 1619 OI.Int = ∬1620 OI.Name = Int.fullName();1621 CodeGenParamAllocator ParamAllocPrelim{&MG.ParamTypes, &OI.ParamValues};1622 raw_string_ostream OS(MG.Code);1623 Int.genCode(OS, ParamAllocPrelim, 1);1624 1625 MergeableGroupsPrelim[MG].insert(OI);1626 }1627 1628 // Pass 2: for each of those groups, optimize the parameter variable set by1629 // eliminating 'parameters' that are the same for all intrinsics in the1630 // group, and merging together pairs of parameter variables that take the1631 // same values as each other for all intrinsics in the group.1632 1633 std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroups;1634 1635 for (const auto &kv : MergeableGroupsPrelim) {1636 const MergeableGroup &MG = kv.first;1637 std::vector<int> ParamNumbers;1638 std::map<ComparableStringVector, int> ParamNumberMap;1639 1640 // Loop over the parameters for this group.1641 for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {1642 // Is this parameter the same for all intrinsics in the group?1643 const OutputIntrinsic &OI_first = *kv.second.begin();1644 bool Constant = all_of(kv.second, [&](const OutputIntrinsic &OI) {1645 return OI.ParamValues[i] == OI_first.ParamValues[i];1646 });1647 1648 // If so, record it as -1, meaning 'no parameter variable needed'. Then1649 // the corresponding call to allocParam in pass 2 will not generate a1650 // variable at all, and just use the value inline.1651 if (Constant) {1652 ParamNumbers.push_back(-1);1653 continue;1654 }1655 1656 // Otherwise, make a list of the values this parameter takes for each1657 // intrinsic, and see if that value vector matches anything we already1658 // have. We also record the parameter type, so that we don't accidentally1659 // match up two parameter variables with different types. (Not that1660 // there's much chance of them having textually equivalent values, but in1661 // _principle_ it could happen.)1662 ComparableStringVector key;1663 key.push_back(MG.ParamTypes[i]);1664 for (const auto &OI : kv.second)1665 key.push_back(OI.ParamValues[i]);1666 1667 // Obtain a new parameter variable if we don't have one.1668 int ParamNum =1669 ParamNumberMap.try_emplace(key, ParamNumberMap.size()).first->second;1670 ParamNumbers.push_back(ParamNum);1671 }1672 1673 // Now we're ready to do the pass 2 code generation, which will emit the1674 // reduced set of parameter variables we've just worked out.1675 1676 for (const auto &OI_prelim : kv.second) {1677 const ACLEIntrinsic *Int = OI_prelim.Int;1678 1679 MergeableGroup MG;1680 OutputIntrinsic OI;1681 1682 OI.Int = OI_prelim.Int;1683 OI.Name = OI_prelim.Name;1684 CodeGenParamAllocator ParamAlloc{&MG.ParamTypes, &OI.ParamValues,1685 &ParamNumbers};1686 raw_string_ostream OS(MG.Code);1687 Int->genCode(OS, ParamAlloc, 2);1688 1689 MergeableGroups[MG].insert(OI);1690 }1691 }1692 1693 // Output the actual C++ code.1694 1695 for (const auto &kv : MergeableGroups) {1696 const MergeableGroup &MG = kv.first;1697 1698 // List of case statements in the main switch on BuiltinID, and an open1699 // brace.1700 const char *prefix = "";1701 for (const auto &OI : kv.second) {1702 OS << prefix << "case ARM::BI__builtin_arm_" << OI.Int->builtinExtension()1703 << "_" << OI.Name << ":";1704 1705 prefix = "\n";1706 }1707 OS << " {\n";1708 1709 if (!MG.ParamTypes.empty()) {1710 // If we've got some parameter variables, then emit their declarations...1711 for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {1712 StringRef Type = MG.ParamTypes[i];1713 OS << " " << Type;1714 if (!Type.ends_with("*"))1715 OS << " ";1716 OS << " Param" << utostr(i) << ";\n";1717 }1718 1719 // ... and an inner switch on BuiltinID that will fill them in with each1720 // individual intrinsic's values.1721 OS << " switch (BuiltinID) {\n";1722 for (const auto &OI : kv.second) {1723 OS << " case ARM::BI__builtin_arm_" << OI.Int->builtinExtension()1724 << "_" << OI.Name << ":\n";1725 for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i)1726 OS << " Param" << utostr(i) << " = static_cast<"1727 << MG.ParamTypes[i] << ">(" << OI.ParamValues[i] << ");\n";1728 OS << " break;\n";1729 }1730 OS << " }\n";1731 }1732 1733 // And finally, output the code, and close the outer pair of braces. (The1734 // code will always end with a 'return' statement, so we need not insert a1735 // 'break' here.)1736 OS << MG.Code << "}\n";1737 }1738}1739 1740void EmitterBase::EmitBuiltinAliases(raw_ostream &OS) {1741 // Build a sorted table of:1742 // - intrinsic id number1743 // - full name1744 // - polymorphic name or -11745 StringToOffsetTable StringTable;1746 OS << "static const IntrinToName MapData[] = {\n";1747 for (const auto &kv : ACLEIntrinsics) {1748 const ACLEIntrinsic &Int = *kv.second;1749 if (Int.headerOnly())1750 continue;1751 int32_t ShortNameOffset =1752 Int.polymorphic() ? StringTable.GetOrAddStringOffset(Int.shortName())1753 : -1;1754 OS << " { ARM::BI__builtin_arm_" << Int.builtinExtension() << "_"1755 << Int.fullName() << ", "1756 << StringTable.GetOrAddStringOffset(Int.fullName()) << ", "1757 << ShortNameOffset << "},\n";1758 }1759 OS << "};\n\n";1760 1761 OS << "ArrayRef<IntrinToName> Map(MapData);\n\n";1762 1763 OS << "static const char IntrinNames[] = {\n";1764 StringTable.EmitString(OS);1765 OS << "};\n\n";1766}1767 1768void EmitterBase::GroupSemaChecks(1769 std::map<std::string, std::set<std::string>> &Checks) {1770 for (const auto &kv : ACLEIntrinsics) {1771 const ACLEIntrinsic &Int = *kv.second;1772 if (Int.headerOnly())1773 continue;1774 std::string Check = Int.genSema();1775 if (!Check.empty())1776 Checks[Check].insert(Int.fullName());1777 }1778}1779 1780// -----------------------------------------------------------------------------1781// The class used for generating arm_mve.h and related Clang bits1782//1783 1784class MveEmitter : public EmitterBase {1785public:1786 MveEmitter(const RecordKeeper &Records) : EmitterBase(Records) {}1787 void EmitHeader(raw_ostream &OS) override;1788 void EmitBuiltinDef(raw_ostream &OS) override;1789 void EmitBuiltinSema(raw_ostream &OS) override;1790};1791 1792void MveEmitter::EmitHeader(raw_ostream &OS) {1793 // Accumulate pieces of the header file that will be enabled under various1794 // different combinations of #ifdef. The index into parts[] is made up of1795 // the following bit flags.1796 constexpr unsigned Float = 1;1797 constexpr unsigned UseUserNamespace = 2;1798 1799 constexpr unsigned NumParts = 4;1800 raw_self_contained_string_ostream parts[NumParts];1801 1802 // Write typedefs for all the required vector types, and a few scalar1803 // types that don't already have the name we want them to have.1804 1805 parts[0] << "typedef uint16_t mve_pred16_t;\n";1806 parts[Float] << "typedef __fp16 float16_t;\n"1807 "typedef float float32_t;\n";1808 for (const auto &kv : ScalarTypes) {1809 const ScalarType *ST = kv.second.get();1810 if (ST->hasNonstandardName())1811 continue;1812 raw_ostream &OS = parts[ST->requiresFloat() ? Float : 0];1813 const VectorType *VT = getVectorType(ST);1814 1815 OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes()1816 << "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " "1817 << VT->cName() << ";\n";1818 1819 // Every vector type also comes with a pair of multi-vector types for1820 // the VLD2 and VLD4 instructions.1821 for (unsigned n = 2; n <= 4; n += 2) {1822 const MultiVectorType *MT = getMultiVectorType(n, VT);1823 OS << "typedef struct { " << VT->cName() << " val[" << n << "]; } "1824 << MT->cName() << ";\n";1825 }1826 }1827 parts[0] << "\n";1828 parts[Float] << "\n";1829 1830 // Write declarations for all the intrinsics.1831 1832 for (const auto &kv : ACLEIntrinsics) {1833 const ACLEIntrinsic &Int = *kv.second;1834 1835 // We generate each intrinsic twice, under its full unambiguous1836 // name and its shorter polymorphic name (if the latter exists).1837 for (bool Polymorphic : {false, true}) {1838 if (Polymorphic && !Int.polymorphic())1839 continue;1840 if (!Polymorphic && Int.polymorphicOnly())1841 continue;1842 1843 // We also generate each intrinsic under a name like __arm_vfooq1844 // (which is in C language implementation namespace, so it's1845 // safe to define in any conforming user program) and a shorter1846 // one like vfooq (which is in user namespace, so a user might1847 // reasonably have used it for something already). If so, they1848 // can #define __ARM_MVE_PRESERVE_USER_NAMESPACE before1849 // including the header, which will suppress the shorter names1850 // and leave only the implementation-namespace ones. Then they1851 // have to write __arm_vfooq everywhere, of course.1852 1853 for (bool UserNamespace : {false, true}) {1854 raw_ostream &OS = parts[(Int.requiresFloat() ? Float : 0) |1855 (UserNamespace ? UseUserNamespace : 0)];1856 1857 // Make the name of the function in this declaration.1858 1859 std::string FunctionName =1860 Polymorphic ? Int.shortName() : Int.fullName();1861 if (!UserNamespace)1862 FunctionName = "__arm_" + FunctionName;1863 1864 // Make strings for the types involved in the function's1865 // prototype.1866 1867 std::string RetTypeName = Int.returnType()->cName();1868 if (!StringRef(RetTypeName).ends_with("*"))1869 RetTypeName += " ";1870 1871 std::vector<std::string> ArgTypeNames;1872 for (const Type *ArgTypePtr : Int.argTypes())1873 ArgTypeNames.push_back(ArgTypePtr->cName());1874 std::string ArgTypesString =1875 join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", ");1876 1877 // Emit the actual declaration. All these functions are1878 // declared 'static inline' without a body, which is fine1879 // provided clang recognizes them as builtins, and has the1880 // effect that this type signature is used in place of the one1881 // that Builtins.td didn't provide. That's how we can get1882 // structure types that weren't defined until this header was1883 // included to be part of the type signature of a builtin that1884 // was known to clang already.1885 //1886 // The declarations use __attribute__(__clang_arm_builtin_alias),1887 // so that each function declared will be recognized as the1888 // appropriate MVE builtin in spite of its user-facing name.1889 //1890 // (That's better than making them all wrapper functions,1891 // partly because it avoids any compiler error message citing1892 // the wrapper function definition instead of the user's code,1893 // and mostly because some MVE intrinsics have arguments1894 // required to be compile-time constants, and that property1895 // can't be propagated through a wrapper function. It can be1896 // propagated through a macro, but macros can't be overloaded1897 // on argument types very easily - you have to use _Generic,1898 // which makes error messages very confusing when the user1899 // gets it wrong.)1900 //1901 // Finally, the polymorphic versions of the intrinsics are1902 // also defined with __attribute__(overloadable), so that when1903 // the same name is defined with several type signatures, the1904 // right thing happens. Each one of the overloaded1905 // declarations is given a different builtin id, which1906 // has exactly the effect we want: first clang resolves the1907 // overload to the right function, then it knows which builtin1908 // it's referring to, and then the Sema checking for that1909 // builtin can check further things like the constant1910 // arguments.1911 //1912 // One more subtlety is the newline just before the return1913 // type name. That's a cosmetic tweak to make the error1914 // messages legible if the user gets the types wrong in a call1915 // to a polymorphic function: this way, clang will print just1916 // the _final_ line of each declaration in the header, to show1917 // the type signatures that would have been legal. So all the1918 // confusing machinery with __attribute__ is left out of the1919 // error message, and the user sees something that's more or1920 // less self-documenting: "here's a list of actually readable1921 // type signatures for vfooq(), and here's why each one didn't1922 // match your call".1923 1924 OS << "static __inline__ __attribute__(("1925 << (Polymorphic ? "__overloadable__, " : "")1926 << "__clang_arm_builtin_alias(__builtin_arm_mve_" << Int.fullName()1927 << ")))\n"1928 << RetTypeName << FunctionName << "(" << ArgTypesString << ");\n";1929 }1930 }1931 }1932 for (auto &part : parts)1933 part << "\n";1934 1935 // Now we've finished accumulating bits and pieces into the parts[] array.1936 // Put it all together to write the final output file.1937 1938 OS << "/*===---- arm_mve.h - ARM MVE intrinsics "1939 "-----------------------------------===\n"1940 << LLVMLicenseHeader1941 << "#ifndef __ARM_MVE_H\n"1942 "#define __ARM_MVE_H\n"1943 "\n"1944 "#if !__ARM_FEATURE_MVE\n"1945 "#error \"MVE support not enabled\"\n"1946 "#endif\n"1947 "\n"1948 "#include <stdint.h>\n"1949 "\n"1950 "#ifdef __cplusplus\n"1951 "extern \"C\" {\n"1952 "#endif\n"1953 "\n";1954 1955 for (size_t i = 0; i < NumParts; ++i) {1956 std::vector<std::string> conditions;1957 if (i & Float)1958 conditions.push_back("(__ARM_FEATURE_MVE & 2)");1959 if (i & UseUserNamespace)1960 conditions.push_back("(!defined __ARM_MVE_PRESERVE_USER_NAMESPACE)");1961 1962 std::string condition =1963 join(std::begin(conditions), std::end(conditions), " && ");1964 if (!condition.empty())1965 OS << "#if " << condition << "\n\n";1966 OS << parts[i].str();1967 if (!condition.empty())1968 OS << "#endif /* " << condition << " */\n\n";1969 }1970 1971 OS << "#ifdef __cplusplus\n"1972 "} /* extern \"C\" */\n"1973 "#endif\n"1974 "\n"1975 "#endif /* __ARM_MVE_H */\n";1976}1977 1978void MveEmitter::EmitBuiltinDef(raw_ostream &OS) {1979 llvm::StringToOffsetTable Table;1980 Table.GetOrAddStringOffset("n");1981 Table.GetOrAddStringOffset("nt");1982 Table.GetOrAddStringOffset("ntu");1983 Table.GetOrAddStringOffset("vi.");1984 1985 for (const auto &[_, Int] : ACLEIntrinsics)1986 Table.GetOrAddStringOffset(Int->fullName());1987 1988 std::map<std::string, ACLEIntrinsic *> ShortNameIntrinsics;1989 for (const auto &[_, Int] : ACLEIntrinsics) {1990 if (!Int->polymorphic())1991 continue;1992 1993 StringRef Name = Int->shortName();1994 if (ShortNameIntrinsics.insert({Name.str(), Int.get()}).second)1995 Table.GetOrAddStringOffset(Name);1996 }1997 1998 OS << "#ifdef GET_MVE_BUILTIN_ENUMERATORS\n";1999 for (const auto &[_, Int] : ACLEIntrinsics) {2000 OS << " BI__builtin_arm_mve_" << Int->fullName() << ",\n";2001 }2002 for (const auto &[Name, _] : ShortNameIntrinsics) {2003 OS << " BI__builtin_arm_mve_" << Name << ",\n";2004 }2005 OS << "#endif // GET_MVE_BUILTIN_ENUMERATORS\n\n";2006 2007 OS << "#ifdef GET_MVE_BUILTIN_STR_TABLE\n";2008 Table.EmitStringTableDef(OS, "BuiltinStrings");2009 OS << "#endif // GET_MVE_BUILTIN_STR_TABLE\n\n";2010 2011 OS << "#ifdef GET_MVE_BUILTIN_INFOS\n";2012 for (const auto &[_, Int] : ACLEIntrinsics) {2013 OS << " Builtin::Info{Builtin::Info::StrOffsets{"2014 << Table.GetStringOffset(Int->fullName()) << " /* " << Int->fullName()2015 << " */, " << Table.GetStringOffset("") << ", "2016 << Table.GetStringOffset("n") << " /* n */}},\n";2017 }2018 for (const auto &[Name, Int] : ShortNameIntrinsics) {2019 StringRef Attrs = Int->nonEvaluating() ? "ntu" : "nt";2020 OS << " Builtin::Info{Builtin::Info::StrOffsets{"2021 << Table.GetStringOffset(Name) << " /* " << Name << " */, "2022 << Table.GetStringOffset("vi.") << " /* vi. */, "2023 << Table.GetStringOffset(Attrs) << " /* " << Attrs << " */}},\n";2024 }2025 OS << "#endif // GET_MVE_BUILTIN_INFOS\n\n";2026}2027 2028void MveEmitter::EmitBuiltinSema(raw_ostream &OS) {2029 std::map<std::string, std::set<std::string>> Checks;2030 GroupSemaChecks(Checks);2031 2032 for (const auto &kv : Checks) {2033 for (StringRef Name : kv.second)2034 OS << "case ARM::BI__builtin_arm_mve_" << Name << ":\n";2035 OS << " return " << kv.first;2036 }2037}2038 2039// -----------------------------------------------------------------------------2040// Class that describes an ACLE intrinsic implemented as a macro.2041//2042// This class is used when the intrinsic is polymorphic in 2 or 3 types, but we2043// want to avoid a combinatorial explosion by reinterpreting the arguments to2044// fixed types.2045 2046class FunctionMacro {2047 std::vector<StringRef> Params;2048 StringRef Definition;2049 2050public:2051 FunctionMacro(const Record &R);2052 2053 const std::vector<StringRef> &getParams() const { return Params; }2054 StringRef getDefinition() const { return Definition; }2055};2056 2057FunctionMacro::FunctionMacro(const Record &R) {2058 Params = R.getValueAsListOfStrings("params");2059 Definition = R.getValueAsString("definition");2060}2061 2062// -----------------------------------------------------------------------------2063// The class used for generating arm_cde.h and related Clang bits2064//2065 2066class CdeEmitter : public EmitterBase {2067 std::map<StringRef, FunctionMacro> FunctionMacros;2068 2069public:2070 CdeEmitter(const RecordKeeper &Records);2071 void EmitHeader(raw_ostream &OS) override;2072 void EmitBuiltinDef(raw_ostream &OS) override;2073 void EmitBuiltinSema(raw_ostream &OS) override;2074};2075 2076CdeEmitter::CdeEmitter(const RecordKeeper &Records) : EmitterBase(Records) {2077 for (const Record *R : Records.getAllDerivedDefinitions("FunctionMacro"))2078 FunctionMacros.emplace(R->getName(), FunctionMacro(*R));2079}2080 2081void CdeEmitter::EmitHeader(raw_ostream &OS) {2082 // Accumulate pieces of the header file that will be enabled under various2083 // different combinations of #ifdef. The index into parts[] is one of the2084 // following:2085 constexpr unsigned None = 0;2086 constexpr unsigned MVE = 1;2087 constexpr unsigned MVEFloat = 2;2088 2089 constexpr unsigned NumParts = 3;2090 raw_self_contained_string_ostream parts[NumParts];2091 2092 // Write typedefs for all the required vector types, and a few scalar2093 // types that don't already have the name we want them to have.2094 2095 parts[MVE] << "typedef uint16_t mve_pred16_t;\n";2096 parts[MVEFloat] << "typedef __fp16 float16_t;\n"2097 "typedef float float32_t;\n";2098 for (const auto &kv : ScalarTypes) {2099 const ScalarType *ST = kv.second.get();2100 if (ST->hasNonstandardName())2101 continue;2102 // We don't have float64x2_t2103 if (ST->kind() == ScalarTypeKind::Float && ST->sizeInBits() == 64)2104 continue;2105 raw_ostream &OS = parts[ST->requiresFloat() ? MVEFloat : MVE];2106 const VectorType *VT = getVectorType(ST);2107 2108 OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes()2109 << "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " "2110 << VT->cName() << ";\n";2111 }2112 parts[MVE] << "\n";2113 parts[MVEFloat] << "\n";2114 2115 // Write declarations for all the intrinsics.2116 2117 for (const auto &kv : ACLEIntrinsics) {2118 const ACLEIntrinsic &Int = *kv.second;2119 2120 // We generate each intrinsic twice, under its full unambiguous2121 // name and its shorter polymorphic name (if the latter exists).2122 for (bool Polymorphic : {false, true}) {2123 if (Polymorphic && !Int.polymorphic())2124 continue;2125 if (!Polymorphic && Int.polymorphicOnly())2126 continue;2127 2128 raw_ostream &OS =2129 parts[Int.requiresFloat() ? MVEFloat2130 : Int.requiresMVE() ? MVE : None];2131 2132 // Make the name of the function in this declaration.2133 std::string FunctionName =2134 "__arm_" + (Polymorphic ? Int.shortName() : Int.fullName());2135 2136 // Make strings for the types involved in the function's2137 // prototype.2138 std::string RetTypeName = Int.returnType()->cName();2139 if (!StringRef(RetTypeName).ends_with("*"))2140 RetTypeName += " ";2141 2142 std::vector<std::string> ArgTypeNames;2143 for (const Type *ArgTypePtr : Int.argTypes())2144 ArgTypeNames.push_back(ArgTypePtr->cName());2145 std::string ArgTypesString =2146 join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", ");2147 2148 // Emit the actual declaration. See MveEmitter::EmitHeader for detailed2149 // comments2150 OS << "static __inline__ __attribute__(("2151 << (Polymorphic ? "__overloadable__, " : "")2152 << "__clang_arm_builtin_alias(__builtin_arm_" << Int.builtinExtension()2153 << "_" << Int.fullName() << ")))\n"2154 << RetTypeName << FunctionName << "(" << ArgTypesString << ");\n";2155 }2156 }2157 2158 for (const auto &kv : FunctionMacros) {2159 StringRef Name = kv.first;2160 const FunctionMacro &FM = kv.second;2161 2162 raw_ostream &OS = parts[MVE];2163 OS << "#define "2164 << "__arm_" << Name << "(" << join(FM.getParams(), ", ") << ") "2165 << FM.getDefinition() << "\n";2166 }2167 2168 for (auto &part : parts)2169 part << "\n";2170 2171 // Now we've finished accumulating bits and pieces into the parts[] array.2172 // Put it all together to write the final output file.2173 2174 OS << "/*===---- arm_cde.h - ARM CDE intrinsics "2175 "-----------------------------------===\n"2176 << LLVMLicenseHeader2177 << "#ifndef __ARM_CDE_H\n"2178 "#define __ARM_CDE_H\n"2179 "\n"2180 "#if !__ARM_FEATURE_CDE\n"2181 "#error \"CDE support not enabled\"\n"2182 "#endif\n"2183 "\n"2184 "#include <stdint.h>\n"2185 "\n"2186 "#ifdef __cplusplus\n"2187 "extern \"C\" {\n"2188 "#endif\n"2189 "\n";2190 2191 for (size_t i = 0; i < NumParts; ++i) {2192 std::string condition;2193 if (i == MVEFloat)2194 condition = "__ARM_FEATURE_MVE & 2";2195 else if (i == MVE)2196 condition = "__ARM_FEATURE_MVE";2197 2198 if (!condition.empty())2199 OS << "#if " << condition << "\n\n";2200 OS << parts[i].str();2201 if (!condition.empty())2202 OS << "#endif /* " << condition << " */\n\n";2203 }2204 2205 OS << "#ifdef __cplusplus\n"2206 "} /* extern \"C\" */\n"2207 "#endif\n"2208 "\n"2209 "#endif /* __ARM_CDE_H */\n";2210}2211 2212void CdeEmitter::EmitBuiltinDef(raw_ostream &OS) {2213 llvm::StringToOffsetTable Table;2214 Table.GetOrAddStringOffset("ncU");2215 2216 for (const auto &[_, Int] : ACLEIntrinsics)2217 if (!Int->headerOnly())2218 Table.GetOrAddStringOffset(Int->fullName());2219 2220 OS << "#ifdef GET_CDE_BUILTIN_ENUMERATORS\n";2221 for (const auto &[_, Int] : ACLEIntrinsics)2222 if (!Int->headerOnly())2223 OS << " BI__builtin_arm_cde_" << Int->fullName() << ",\n";2224 OS << "#endif // GET_CDE_BUILTIN_ENUMERATORS\n\n";2225 2226 OS << "#ifdef GET_CDE_BUILTIN_STR_TABLE\n";2227 Table.EmitStringTableDef(OS, "BuiltinStrings");2228 OS << "#endif // GET_CDE_BUILTIN_STR_TABLE\n\n";2229 2230 OS << "#ifdef GET_CDE_BUILTIN_INFOS\n";2231 for (const auto &[_, Int] : ACLEIntrinsics)2232 if (!Int->headerOnly())2233 OS << " Builtin::Info{Builtin::Info::StrOffsets{"2234 << Table.GetStringOffset(Int->fullName()) << " /* " << Int->fullName()2235 << " */, " << Table.GetStringOffset("") << ", "2236 << Table.GetStringOffset("ncU") << " /* ncU */}},\n";2237 OS << "#endif // GET_CDE_BUILTIN_INFOS\n\n";2238}2239 2240void CdeEmitter::EmitBuiltinSema(raw_ostream &OS) {2241 std::map<std::string, std::set<std::string>> Checks;2242 GroupSemaChecks(Checks);2243 2244 for (const auto &kv : Checks) {2245 for (StringRef Name : kv.second)2246 OS << "case ARM::BI__builtin_arm_cde_" << Name << ":\n";2247 OS << " Err = " << kv.first << " break;\n";2248 }2249}2250 2251} // namespace2252 2253namespace clang {2254 2255// MVE2256 2257void EmitMveHeader(const RecordKeeper &Records, raw_ostream &OS) {2258 MveEmitter(Records).EmitHeader(OS);2259}2260 2261void EmitMveBuiltinDef(const RecordKeeper &Records, raw_ostream &OS) {2262 MveEmitter(Records).EmitBuiltinDef(OS);2263}2264 2265void EmitMveBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) {2266 MveEmitter(Records).EmitBuiltinSema(OS);2267}2268 2269void EmitMveBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) {2270 MveEmitter(Records).EmitBuiltinCG(OS);2271}2272 2273void EmitMveBuiltinAliases(const RecordKeeper &Records, raw_ostream &OS) {2274 MveEmitter(Records).EmitBuiltinAliases(OS);2275}2276 2277// CDE2278 2279void EmitCdeHeader(const RecordKeeper &Records, raw_ostream &OS) {2280 CdeEmitter(Records).EmitHeader(OS);2281}2282 2283void EmitCdeBuiltinDef(const RecordKeeper &Records, raw_ostream &OS) {2284 CdeEmitter(Records).EmitBuiltinDef(OS);2285}2286 2287void EmitCdeBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) {2288 CdeEmitter(Records).EmitBuiltinSema(OS);2289}2290 2291void EmitCdeBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) {2292 CdeEmitter(Records).EmitBuiltinCG(OS);2293}2294 2295void EmitCdeBuiltinAliases(const RecordKeeper &Records, raw_ostream &OS) {2296 CdeEmitter(Records).EmitBuiltinAliases(OS);2297}2298 2299} // end namespace clang2300