diff options
Diffstat (limited to 'clang/utils/TableGen/MveEmitter.cpp')
-rw-r--r-- | clang/utils/TableGen/MveEmitter.cpp | 835 |
1 files changed, 580 insertions, 255 deletions
diff --git a/clang/utils/TableGen/MveEmitter.cpp b/clang/utils/TableGen/MveEmitter.cpp index 431e5c477c2b..e9ae08ac4c05 100644 --- a/clang/utils/TableGen/MveEmitter.cpp +++ b/clang/utils/TableGen/MveEmitter.cpp @@ -60,10 +60,12 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringToOffsetTable.h" #include <cassert> #include <cstddef> #include <cstdint> @@ -78,7 +80,7 @@ using namespace llvm; namespace { -class MveEmitter; +class EmitterBase; class Result; // ----------------------------------------------------------------------------- @@ -138,6 +140,7 @@ public: TypeKind typeKind() const { return TKind; } virtual ~Type() = default; virtual bool requiresFloat() const = 0; + virtual bool requiresMVE() const = 0; virtual unsigned sizeInBits() const = 0; virtual std::string cName() const = 0; virtual std::string llvmName() const { @@ -177,6 +180,7 @@ public: VoidType() : Type(TypeKind::Void) {} unsigned sizeInBits() const override { return 0; } bool requiresFloat() const override { return false; } + bool requiresMVE() const override { return false; } std::string cName() const override { return "void"; } static bool classof(const Type *T) { return T->typeKind() == TypeKind::Void; } @@ -192,6 +196,7 @@ public: : Type(TypeKind::Pointer), Pointee(Pointee), Const(Const) {} unsigned sizeInBits() const override { return 32; } bool requiresFloat() const override { return Pointee->requiresFloat(); } + bool requiresMVE() const override { return Pointee->requiresMVE(); } std::string cName() const override { std::string Name = Pointee->cName(); @@ -241,7 +246,7 @@ public: .Case("u", ScalarTypeKind::UnsignedInt) .Case("f", ScalarTypeKind::Float); Bits = Record->getValueAsInt("size"); - NameOverride = Record->getValueAsString("nameOverride"); + NameOverride = std::string(Record->getValueAsString("nameOverride")); } unsigned sizeInBits() const override { return Bits; } ScalarTypeKind kind() const { return Kind; } @@ -272,6 +277,7 @@ public: } bool isInteger() const { return Kind != ScalarTypeKind::Float; } bool requiresFloat() const override { return !isInteger(); } + bool requiresMVE() const override { return false; } bool hasNonstandardName() const { return !NameOverride.empty(); } static bool classof(const Type *T) { @@ -289,11 +295,12 @@ public: unsigned sizeInBits() const override { return Lanes * Element->sizeInBits(); } unsigned lanes() const { return Lanes; } bool requiresFloat() const override { return Element->requiresFloat(); } + bool requiresMVE() const override { return true; } std::string cNameBase() const override { return Element->cNameBase() + "x" + utostr(Lanes); } std::string llvmName() const override { - return "llvm::VectorType::get(" + Element->llvmName() + ", " + + return "llvm::FixedVectorType::get(" + Element->llvmName() + ", " + utostr(Lanes) + ")"; } @@ -315,6 +322,7 @@ public: } unsigned registers() const { return Registers; } bool requiresFloat() const override { return Element->requiresFloat(); } + bool requiresMVE() const override { return true; } std::string cNameBase() const override { return Element->cNameBase() + "x" + utostr(Registers); } @@ -339,13 +347,14 @@ public: unsigned sizeInBits() const override { return 16; } std::string cNameBase() const override { return "mve_pred16"; } bool requiresFloat() const override { return false; }; + bool requiresMVE() const override { return true; } std::string llvmName() const override { // Use <4 x i1> instead of <2 x i1> for two-lane vector types. See // the comment in llvm/lib/Target/ARM/ARMInstrMVE.td for further // explanation. unsigned ModifiedLanes = (Lanes == 2 ? 4 : Lanes); - return "llvm::VectorType::get(Builder.getInt1Ty(), " + + return "llvm::FixedVectorType::get(Builder.getInt1Ty(), " + utostr(ModifiedLanes) + ")"; } @@ -403,7 +412,7 @@ struct CodeGenParamAllocator { // We rely on the recursive code generation working identically in passes 1 // and 2, so that the same list of calls to allocParam happen in the same // order. That guarantees that the parameter numbers recorded in pass 1 will - // match the entries in this vector that store what MveEmitter::EmitBuiltinCG + // match the entries in this vector that store what EmitterBase::EmitBuiltinCG // decided to do about each one in pass 2. std::vector<int> *ParamNumberMap = nullptr; @@ -422,16 +431,16 @@ struct CodeGenParamAllocator { // variable we should be keeping things in. int MapValue = (*ParamNumberMap)[nparams++]; if (MapValue < 0) - return Value; + return std::string(Value); ParamNumber = MapValue; } // If we've allocated a new parameter variable for the first time, store // its type and value to be retrieved after codegen. if (ParamTypes && ParamTypes->size() == ParamNumber) - ParamTypes->push_back(Type); + ParamTypes->push_back(std::string(Type)); if (ParamValues && ParamValues->size() == ParamNumber) - ParamValues->push_back(Value); + ParamValues->push_back(std::string(Value)); // Unimaginative naming scheme for parameter variables. return "Param" + utostr(ParamNumber); @@ -500,8 +509,17 @@ public: } void setPredecessor(Ptr p) { - assert(!Predecessor); - Predecessor = p; + // If the user has nested one 'seq' node inside another, and this + // method is called on the return value of the inner 'seq' (i.e. + // the final item inside it), then we can't link _this_ node to p, + // because it already has a predecessor. Instead, walk the chain + // until we find the first item in the inner seq, and link that to + // p, so that nesting seqs has the obvious effect of linking + // everything together into one long sequential chain. + Result *r = this; + while (r->Predecessor) + r = r->Predecessor.get(); + r->Predecessor = p; } // Each Result will be assigned a variable name in the output code, but not @@ -514,7 +532,7 @@ public: VarNameUsed = true; return VarName; } - void setVarname(const StringRef s) { VarName = s; } + void setVarname(const StringRef s) { VarName = std::string(s); } bool varnameUsed() const { return VarNameUsed; } // Emit code to generate this result as a Value *. @@ -713,14 +731,15 @@ public: std::vector<Ptr> Args; IRIntrinsicResult(StringRef IntrinsicID, std::vector<const Type *> ParamTypes, std::vector<Ptr> Args) - : IntrinsicID(IntrinsicID), ParamTypes(ParamTypes), Args(Args) {} + : IntrinsicID(std::string(IntrinsicID)), ParamTypes(ParamTypes), + Args(Args) {} void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc) const override { std::string IntNo = ParamAlloc.allocParam( "Intrinsic::ID", "Intrinsic::" + IntrinsicID); OS << "Builder.CreateCall(CGM.getIntrinsic(" << IntNo; if (!ParamTypes.empty()) { - OS << ", llvm::SmallVector<llvm::Type *, " << ParamTypes.size() << "> {"; + OS << ", {"; const char *Sep = ""; for (auto T : ParamTypes) { OS << Sep << ParamAlloc.allocParam("llvm::Type *", T->llvmName()); @@ -728,7 +747,7 @@ public: } OS << "}"; } - OS << "), llvm::SmallVector<Value *, " << Args.size() << "> {"; + OS << "), {"; const char *Sep = ""; for (auto Arg : Args) { OS << Sep << Arg->asValue(); @@ -782,6 +801,9 @@ class ACLEIntrinsic { // shares with at least one other intrinsic. std::string ShortName, FullName; + // Name of the architecture extension, used in the Clang builtin name + StringRef BuiltinExtension; + // A very small number of intrinsics _only_ have a polymorphic // variant (vuninitializedq taking an unevaluated argument). bool PolymorphicOnly; @@ -790,6 +812,10 @@ class ACLEIntrinsic { // evaluate its argument(s) at all. bool NonEvaluating; + // True if the intrinsic needs only the C header part (no codegen, semantic + // checks, etc). Used for redeclaring MVE intrinsics in the arm_cde.h header. + bool HeaderOnly; + const Type *ReturnType; std::vector<const Type *> ArgTypes; std::map<unsigned, ImmediateArg> ImmediateArgs; @@ -812,6 +838,7 @@ class ACLEIntrinsic { public: const std::string &shortName() const { return ShortName; } const std::string &fullName() const { return FullName; } + StringRef builtinExtension() const { return BuiltinExtension; } const Type *returnType() const { return ReturnType; } const std::vector<const Type *> &argTypes() const { return ArgTypes; } bool requiresFloat() const { @@ -822,13 +849,19 @@ public: return true; return false; } + bool requiresMVE() const { + return ReturnType->requiresMVE() || + any_of(ArgTypes, [](const Type *T) { return T->requiresMVE(); }); + } bool polymorphic() const { return ShortName != FullName; } bool polymorphicOnly() const { return PolymorphicOnly; } bool nonEvaluating() const { return NonEvaluating; } + bool headerOnly() const { return HeaderOnly; } - // External entry point for code generation, called from MveEmitter. + // External entry point for code generation, called from EmitterBase. void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc, unsigned Pass) const { + assert(!headerOnly() && "Called genCode for header-only intrinsic"); if (!hasCode()) { for (auto kv : CustomCodeGenArgs) OS << " " << kv.first << " = " << kv.second << ";\n"; @@ -865,10 +898,11 @@ public: llvm::APInt i = iOrig.trunc(64); SmallString<40> s; i.toString(s, 16, true, true); - return s.str(); + return std::string(s.str()); } std::string genSema() const { + assert(!headerOnly() && "Called genSema for header-only intrinsic"); std::vector<std::string> SemaChecks; for (const auto &kv : ImmediateArgs) { @@ -882,57 +916,59 @@ public: break; case ImmediateArg::BoundsType::UInt: lo = 0; - hi = IA.i1; + hi = llvm::APInt::getMaxValue(IA.i1).zext(128); break; } - llvm::APInt typelo, typehi; - unsigned Bits = IA.ArgType->sizeInBits(); - if (cast<ScalarType>(IA.ArgType)->kind() == ScalarTypeKind::SignedInt) { - typelo = llvm::APInt::getSignedMinValue(Bits).sext(128); - typehi = llvm::APInt::getSignedMaxValue(Bits).sext(128); - } else { - typelo = llvm::APInt::getMinValue(Bits).zext(128); - typehi = llvm::APInt::getMaxValue(Bits).zext(128); - } - std::string Index = utostr(kv.first); - if (lo.sle(typelo) && hi.sge(typehi)) - SemaChecks.push_back("SemaBuiltinConstantArg(TheCall, " + Index + ")"); - else + // Emit a range check if the legal range of values for the + // immediate is smaller than the _possible_ range of values for + // its type. + unsigned ArgTypeBits = IA.ArgType->sizeInBits(); + llvm::APInt ArgTypeRange = llvm::APInt::getMaxValue(ArgTypeBits).zext(128); + llvm::APInt ActualRange = (hi-lo).trunc(64).sext(128); + if (ActualRange.ult(ArgTypeRange)) SemaChecks.push_back("SemaBuiltinConstantArgRange(TheCall, " + Index + ", " + signedHexLiteral(lo) + ", " + signedHexLiteral(hi) + ")"); if (!IA.ExtraCheckType.empty()) { std::string Suffix; - if (!IA.ExtraCheckArgs.empty()) - Suffix = (Twine(", ") + IA.ExtraCheckArgs).str(); + if (!IA.ExtraCheckArgs.empty()) { + std::string tmp; + StringRef Arg = IA.ExtraCheckArgs; + if (Arg == "!lanesize") { + tmp = utostr(IA.ArgType->sizeInBits()); + Arg = tmp; + } + Suffix = (Twine(", ") + Arg).str(); + } SemaChecks.push_back((Twine("SemaBuiltinConstantArg") + IA.ExtraCheckType + "(TheCall, " + Index + Suffix + ")") .str()); } + + assert(!SemaChecks.empty()); } if (SemaChecks.empty()) return ""; - return (Twine(" return ") + - join(std::begin(SemaChecks), std::end(SemaChecks), - " ||\n ") + - ";\n") - .str(); + return join(std::begin(SemaChecks), std::end(SemaChecks), + " ||\n ") + + ";\n"; } - ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param); + ACLEIntrinsic(EmitterBase &ME, Record *R, const Type *Param); }; // ----------------------------------------------------------------------------- // The top-level class that holds all the state from analyzing the entire // Tablegen input. -class MveEmitter { - // MveEmitter holds a collection of all the types we've instantiated. +class EmitterBase { +protected: + // EmitterBase holds a collection of all the types we've instantiated. VoidType Void; std::map<std::string, std::unique_ptr<ScalarType>> ScalarTypes; std::map<std::tuple<ScalarTypeKind, unsigned, unsigned>, @@ -951,7 +987,7 @@ public: // maps stored in this object. const VoidType *getVoidType() { return &Void; } const ScalarType *getScalarType(StringRef Name) { - return ScalarTypes[Name].get(); + return ScalarTypes[std::string(Name)].get(); } const ScalarType *getScalarType(Record *R) { return getScalarType(R->getName()); @@ -1007,18 +1043,21 @@ public: Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType, bool Promote, bool Immediate); + void GroupSemaChecks(std::map<std::string, std::set<std::string>> &Checks); + // Constructor and top-level functions. - MveEmitter(RecordKeeper &Records); + EmitterBase(RecordKeeper &Records); + virtual ~EmitterBase() = default; - void EmitHeader(raw_ostream &OS); - void EmitBuiltinDef(raw_ostream &OS); - void EmitBuiltinSema(raw_ostream &OS); + virtual void EmitHeader(raw_ostream &OS) = 0; + virtual void EmitBuiltinDef(raw_ostream &OS) = 0; + virtual void EmitBuiltinSema(raw_ostream &OS) = 0; void EmitBuiltinCG(raw_ostream &OS); void EmitBuiltinAliases(raw_ostream &OS); }; -const Type *MveEmitter::getType(Init *I, const Type *Param) { +const Type *EmitterBase::getType(Init *I, const Type *Param) { if (auto Dag = dyn_cast<DagInit>(I)) return getType(Dag, Param); if (auto Def = dyn_cast<DefInit>(I)) @@ -1027,7 +1066,7 @@ const Type *MveEmitter::getType(Init *I, const Type *Param) { PrintFatalError("Could not convert this value into a type"); } -const Type *MveEmitter::getType(Record *R, const Type *Param) { +const Type *EmitterBase::getType(Record *R, const Type *Param) { // Pass to a subfield of any wrapper records. We don't expect more than one // of these: immediate operands are used as plain numbers rather than as // llvm::Value, so it's meaningless to promote their type anyway. @@ -1046,7 +1085,7 @@ const Type *MveEmitter::getType(Record *R, const Type *Param) { PrintFatalError(R->getLoc(), "Could not convert this record into a type"); } -const Type *MveEmitter::getType(DagInit *D, const Type *Param) { +const Type *EmitterBase::getType(DagInit *D, const Type *Param) { // The meat of the getType system: types in the Tablegen are represented by a // dag whose operators select sub-cases of this function. @@ -1114,8 +1153,8 @@ const Type *MveEmitter::getType(DagInit *D, const Type *Param) { PrintFatalError("Bad operator in type dag expression"); } -Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, - const Type *Param) { +Result::Ptr EmitterBase::getCodeForDag(DagInit *D, const Result::Scope &Scope, + const Type *Param) { Record *Op = cast<DefInit>(D->getOperator())->getDef(); if (Op->getName() == "seq") { @@ -1128,7 +1167,7 @@ Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, getCodeForDag(cast<DagInit>(D->getArg(i)), SubScope, Param); StringRef ArgName = D->getArgNameStr(i); if (!ArgName.empty()) - SubScope[ArgName] = V; + SubScope[std::string(ArgName)] = V; if (PrevV) V->setPredecessor(PrevV); PrevV = V; @@ -1174,6 +1213,18 @@ Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, } else { PrintFatalError("unsignedflag's argument should be a scalar type"); } + } else if (Op->getName() == "bitsize") { + if (D->getNumArgs() != 1) + PrintFatalError("bitsize should have exactly one argument"); + Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef(); + if (!TypeRec->isSubClassOf("Type")) + PrintFatalError("bitsize's argument should be a type"); + if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) { + return std::make_shared<IntLiteralResult>(getScalarType("u32"), + ST->sizeInBits()); + } else { + PrintFatalError("bitsize's argument should be a scalar type"); + } } else { std::vector<Result::Ptr> Args; for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i) @@ -1186,7 +1237,7 @@ Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, if (sp->isSubClassOf("IRBuilderAddrParam")) { AddressArgs.insert(Index); } else if (sp->isSubClassOf("IRBuilderIntParam")) { - IntegerArgs[Index] = sp->getValueAsString("type"); + IntegerArgs[Index] = std::string(sp->getValueAsString("type")); } } return std::make_shared<IRBuilderResult>(Op->getValueAsString("prefix"), @@ -1195,7 +1246,7 @@ Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, std::vector<const Type *> ParamTypes; for (Record *RParam : Op->getValueAsListOfDefs("params")) ParamTypes.push_back(getType(RParam, Param)); - std::string IntName = Op->getValueAsString("intname"); + std::string IntName = std::string(Op->getValueAsString("intname")); if (Op->getValueAsBit("appendKind")) IntName += "_" + toLetter(cast<ScalarType>(Param)->kind()); return std::make_shared<IRIntrinsicResult>(IntName, ParamTypes, Args); @@ -1205,9 +1256,9 @@ Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope, } } -Result::Ptr MveEmitter::getCodeForDagArg(DagInit *D, unsigned ArgNum, - const Result::Scope &Scope, - const Type *Param) { +Result::Ptr EmitterBase::getCodeForDagArg(DagInit *D, unsigned ArgNum, + const Result::Scope &Scope, + const Type *Param) { Init *Arg = D->getArg(ArgNum); StringRef Name = D->getArgNameStr(ArgNum); @@ -1215,7 +1266,7 @@ Result::Ptr MveEmitter::getCodeForDagArg(DagInit *D, unsigned ArgNum, if (!isa<UnsetInit>(Arg)) PrintFatalError( "dag operator argument should not have both a value and a name"); - auto it = Scope.find(Name); + auto it = Scope.find(std::string(Name)); if (it == Scope.end()) PrintFatalError("unrecognized variable name '" + Name + "'"); return it->second; @@ -1239,8 +1290,8 @@ Result::Ptr MveEmitter::getCodeForDagArg(DagInit *D, unsigned ArgNum, PrintFatalError("bad dag argument type for code generation"); } -Result::Ptr MveEmitter::getCodeForArg(unsigned ArgNum, const Type *ArgType, - bool Promote, bool Immediate) { +Result::Ptr EmitterBase::getCodeForArg(unsigned ArgNum, const Type *ArgType, + bool Promote, bool Immediate) { Result::Ptr V = std::make_shared<BuiltinArgResult>( ArgNum, isa<PointerType>(ArgType), Immediate); @@ -1259,7 +1310,7 @@ Result::Ptr MveEmitter::getCodeForArg(unsigned ArgNum, const Type *ArgType, return V; } -ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) +ACLEIntrinsic::ACLEIntrinsic(EmitterBase &ME, Record *R, const Type *Param) : ReturnType(ME.getType(R->getValueAsDef("ret"), Param)) { // Derive the intrinsic's full name, by taking the name of the // Tablegen record (or override) and appending the suffix from its @@ -1270,7 +1321,8 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) (R->isSubClassOf("NameOverride") ? R->getValueAsString("basename") : R->getName()); StringRef overrideLetter = R->getValueAsString("overrideKindLetter"); - FullName = (Twine(BaseName) + Param->acleSuffix(overrideLetter)).str(); + FullName = + (Twine(BaseName) + Param->acleSuffix(std::string(overrideLetter))).str(); // Derive the intrinsic's polymorphic name, by removing components from the // full name as specified by its 'pnt' member ('polymorphic name type'), @@ -1297,8 +1349,11 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) } ShortName = join(std::begin(NameParts), std::end(NameParts), "_"); + BuiltinExtension = R->getValueAsString("builtinExtension"); + PolymorphicOnly = R->getValueAsBit("polymorphicOnly"); NonEvaluating = R->getValueAsBit("nonEvaluating"); + HeaderOnly = R->getValueAsBit("headerOnly"); // Process the intrinsic's argument list. DagInit *ArgsDag = R->getValueAsDag("args"); @@ -1360,7 +1415,8 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) // into the variable-name scope that the code gen will refer to. StringRef ArgName = ArgsDag->getArgNameStr(i); if (!ArgName.empty()) - Scope[ArgName] = ME.getCodeForArg(i, ArgType, Promote, Immediate); + Scope[std::string(ArgName)] = + ME.getCodeForArg(i, ArgType, Promote, Immediate); } // Finally, go through the codegen dag and translate it into a Result object @@ -1378,9 +1434,9 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) if (Name.empty()) { PrintFatalError("Operands to CustomCodegen should have names"); } else if (auto *II = dyn_cast<IntInit>(CodeDag->getArg(i))) { - CustomCodeGenArgs[Name] = itostr(II->getValue()); + CustomCodeGenArgs[std::string(Name)] = itostr(II->getValue()); } else if (auto *SI = dyn_cast<StringInit>(CodeDag->getArg(i))) { - CustomCodeGenArgs[Name] = SI->getValue(); + CustomCodeGenArgs[std::string(Name)] = std::string(SI->getValue()); } else { PrintFatalError("Operands to CustomCodegen should be integers"); } @@ -1390,8 +1446,8 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param) } } -MveEmitter::MveEmitter(RecordKeeper &Records) { - // Construct the whole MveEmitter. +EmitterBase::EmitterBase(RecordKeeper &Records) { + // Construct the whole EmitterBase. // First, look up all the instances of PrimitiveType. This gives us the list // of vector typedefs we have to put in arm_mve.h, and also allows us to @@ -1399,7 +1455,7 @@ MveEmitter::MveEmitter(RecordKeeper &Records) { // use it for operations such as 'find the unsigned version of this signed // integer type'. for (Record *R : Records.getAllDerivedDefinitions("PrimitiveType")) - ScalarTypes[R->getName()] = std::make_unique<ScalarType>(R); + ScalarTypes[std::string(R->getName())] = std::make_unique<ScalarType>(R); // Now go through the instances of Intrinsic, and for each one, iterate // through its list of type parameters making an ACLEIntrinsic for each one. @@ -1431,6 +1487,260 @@ public: : string_holder(), raw_string_ostream(S) {} }; +const char LLVMLicenseHeader[] = + " *\n" + " *\n" + " * Part of the LLVM Project, under the Apache License v2.0 with LLVM" + " Exceptions.\n" + " * See https://llvm.org/LICENSE.txt for license information.\n" + " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" + " *\n" + " *===-----------------------------------------------------------------" + "------===\n" + " */\n" + "\n"; + +// Machinery for the grouping of intrinsics by similar codegen. +// +// The general setup is that 'MergeableGroup' stores the things that a set of +// similarly shaped intrinsics have in common: the text of their code +// generation, and the number and type of their parameter variables. +// MergeableGroup is the key in a std::map whose value is a set of +// OutputIntrinsic, which stores the ways in which a particular intrinsic +// specializes the MergeableGroup's generic description: the function name and +// the _values_ of the parameter variables. + +struct ComparableStringVector : std::vector<std::string> { + // Infrastructure: a derived class of vector<string> which comes with an + // ordering, so that it can be used as a key in maps and an element in sets. + // There's no requirement on the ordering beyond being deterministic. + bool operator<(const ComparableStringVector &rhs) const { + if (size() != rhs.size()) + return size() < rhs.size(); + for (size_t i = 0, e = size(); i < e; ++i) + if ((*this)[i] != rhs[i]) + return (*this)[i] < rhs[i]; + return false; + } +}; + +struct OutputIntrinsic { + const ACLEIntrinsic *Int; + std::string Name; + ComparableStringVector ParamValues; + bool operator<(const OutputIntrinsic &rhs) const { + if (Name != rhs.Name) + return Name < rhs.Name; + return ParamValues < rhs.ParamValues; + } +}; +struct MergeableGroup { + std::string Code; + ComparableStringVector ParamTypes; + bool operator<(const MergeableGroup &rhs) const { + if (Code != rhs.Code) + return Code < rhs.Code; + return ParamTypes < rhs.ParamTypes; + } +}; + +void EmitterBase::EmitBuiltinCG(raw_ostream &OS) { + // Pass 1: generate code for all the intrinsics as if every type or constant + // that can possibly be abstracted out into a parameter variable will be. + // This identifies the sets of intrinsics we'll group together into a single + // piece of code generation. + + std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroupsPrelim; + + for (const auto &kv : ACLEIntrinsics) { + const ACLEIntrinsic &Int = *kv.second; + if (Int.headerOnly()) + continue; + + MergeableGroup MG; + OutputIntrinsic OI; + + OI.Int = ∬ + OI.Name = Int.fullName(); + CodeGenParamAllocator ParamAllocPrelim{&MG.ParamTypes, &OI.ParamValues}; + raw_string_ostream OS(MG.Code); + Int.genCode(OS, ParamAllocPrelim, 1); + OS.flush(); + + MergeableGroupsPrelim[MG].insert(OI); + } + + // Pass 2: for each of those groups, optimize the parameter variable set by + // eliminating 'parameters' that are the same for all intrinsics in the + // group, and merging together pairs of parameter variables that take the + // same values as each other for all intrinsics in the group. + + std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroups; + + for (const auto &kv : MergeableGroupsPrelim) { + const MergeableGroup &MG = kv.first; + std::vector<int> ParamNumbers; + std::map<ComparableStringVector, int> ParamNumberMap; + + // Loop over the parameters for this group. + for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) { + // Is this parameter the same for all intrinsics in the group? + const OutputIntrinsic &OI_first = *kv.second.begin(); + bool Constant = all_of(kv.second, [&](const OutputIntrinsic &OI) { + return OI.ParamValues[i] == OI_first.ParamValues[i]; + }); + + // If so, record it as -1, meaning 'no parameter variable needed'. Then + // the corresponding call to allocParam in pass 2 will not generate a + // variable at all, and just use the value inline. + if (Constant) { + ParamNumbers.push_back(-1); + continue; + } + + // Otherwise, make a list of the values this parameter takes for each + // intrinsic, and see if that value vector matches anything we already + // have. We also record the parameter type, so that we don't accidentally + // match up two parameter variables with different types. (Not that + // there's much chance of them having textually equivalent values, but in + // _principle_ it could happen.) + ComparableStringVector key; + key.push_back(MG.ParamTypes[i]); + for (const auto &OI : kv.second) + key.push_back(OI.ParamValues[i]); + + auto Found = ParamNumberMap.find(key); + if (Found != ParamNumberMap.end()) { + // Yes, an existing parameter variable can be reused for this. + ParamNumbers.push_back(Found->second); + continue; + } + + // No, we need a new parameter variable. + int ExistingIndex = ParamNumberMap.size(); + ParamNumberMap[key] = ExistingIndex; + ParamNumbers.push_back(ExistingIndex); + } + + // Now we're ready to do the pass 2 code generation, which will emit the + // reduced set of parameter variables we've just worked out. + + for (const auto &OI_prelim : kv.second) { + const ACLEIntrinsic *Int = OI_prelim.Int; + + MergeableGroup MG; + OutputIntrinsic OI; + + OI.Int = OI_prelim.Int; + OI.Name = OI_prelim.Name; + CodeGenParamAllocator ParamAlloc{&MG.ParamTypes, &OI.ParamValues, + &ParamNumbers}; + raw_string_ostream OS(MG.Code); + Int->genCode(OS, ParamAlloc, 2); + OS.flush(); + + MergeableGroups[MG].insert(OI); + } + } + + // Output the actual C++ code. + + for (const auto &kv : MergeableGroups) { + const MergeableGroup &MG = kv.first; + + // List of case statements in the main switch on BuiltinID, and an open + // brace. + const char *prefix = ""; + for (const auto &OI : kv.second) { + OS << prefix << "case ARM::BI__builtin_arm_" << OI.Int->builtinExtension() + << "_" << OI.Name << ":"; + + prefix = "\n"; + } + OS << " {\n"; + + if (!MG.ParamTypes.empty()) { + // If we've got some parameter variables, then emit their declarations... + for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) { + StringRef Type = MG.ParamTypes[i]; + OS << " " << Type; + if (!Type.endswith("*")) + OS << " "; + OS << " Param" << utostr(i) << ";\n"; + } + + // ... and an inner switch on BuiltinID that will fill them in with each + // individual intrinsic's values. + OS << " switch (BuiltinID) {\n"; + for (const auto &OI : kv.second) { + OS << " case ARM::BI__builtin_arm_" << OI.Int->builtinExtension() + << "_" << OI.Name << ":\n"; + for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) + OS << " Param" << utostr(i) << " = " << OI.ParamValues[i] << ";\n"; + OS << " break;\n"; + } + OS << " }\n"; + } + + // And finally, output the code, and close the outer pair of braces. (The + // code will always end with a 'return' statement, so we need not insert a + // 'break' here.) + OS << MG.Code << "}\n"; + } +} + +void EmitterBase::EmitBuiltinAliases(raw_ostream &OS) { + // Build a sorted table of: + // - intrinsic id number + // - full name + // - polymorphic name or -1 + StringToOffsetTable StringTable; + OS << "static const IntrinToName MapData[] = {\n"; + for (const auto &kv : ACLEIntrinsics) { + const ACLEIntrinsic &Int = *kv.second; + if (Int.headerOnly()) + continue; + int32_t ShortNameOffset = + Int.polymorphic() ? StringTable.GetOrAddStringOffset(Int.shortName()) + : -1; + OS << " { ARM::BI__builtin_arm_" << Int.builtinExtension() << "_" + << Int.fullName() << ", " + << StringTable.GetOrAddStringOffset(Int.fullName()) << ", " + << ShortNameOffset << "},\n"; + } + OS << "};\n\n"; + + OS << "ArrayRef<IntrinToName> Map(MapData);\n\n"; + + OS << "static const char IntrinNames[] = {\n"; + StringTable.EmitString(OS); + OS << "};\n\n"; +} + +void EmitterBase::GroupSemaChecks( + std::map<std::string, std::set<std::string>> &Checks) { + for (const auto &kv : ACLEIntrinsics) { + const ACLEIntrinsic &Int = *kv.second; + if (Int.headerOnly()) + continue; + std::string Check = Int.genSema(); + if (!Check.empty()) + Checks[Check].insert(Int.fullName()); + } +} + +// ----------------------------------------------------------------------------- +// The class used for generating arm_mve.h and related Clang bits +// + +class MveEmitter : public EmitterBase { +public: + MveEmitter(RecordKeeper &Records) : EmitterBase(Records){}; + void EmitHeader(raw_ostream &OS) override; + void EmitBuiltinDef(raw_ostream &OS) override; + void EmitBuiltinSema(raw_ostream &OS) override; +}; + void MveEmitter::EmitHeader(raw_ostream &OS) { // Accumulate pieces of the header file that will be enabled under various // different combinations of #ifdef. The index into parts[] is made up of @@ -1454,8 +1764,9 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { raw_ostream &OS = parts[ST->requiresFloat() ? Float : 0]; const VectorType *VT = getVectorType(ST); - OS << "typedef __attribute__((neon_vector_type(" << VT->lanes() << "))) " - << ST->cName() << " " << VT->cName() << ";\n"; + OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes() + << "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " " + << VT->cName() << ";\n"; // Every vector type also comes with a pair of multi-vector types for // the VLD2 and VLD4 instructions. @@ -1524,7 +1835,7 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { // included to be part of the type signature of a builtin that // was known to clang already. // - // The declarations use __attribute__(__clang_arm_mve_alias), + // The declarations use __attribute__(__clang_arm_builtin_alias), // so that each function declared will be recognized as the // appropriate MVE builtin in spite of its user-facing name. // @@ -1563,8 +1874,8 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { // match your call". OS << "static __inline__ __attribute__((" - << (Polymorphic ? "overloadable, " : "") - << "__clang_arm_mve_alias(__builtin_arm_mve_" << Int.fullName() + << (Polymorphic ? "__overloadable__, " : "") + << "__clang_arm_builtin_alias(__builtin_arm_mve_" << Int.fullName() << ")))\n" << RetTypeName << FunctionName << "(" << ArgTypesString << ");\n"; } @@ -1578,19 +1889,8 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { OS << "/*===---- arm_mve.h - ARM MVE intrinsics " "-----------------------------------===\n" - " *\n" - " *\n" - " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " - "Exceptions.\n" - " * See https://llvm.org/LICENSE.txt for license information.\n" - " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" - " *\n" - " *===-------------------------------------------------------------" - "----" - "------===\n" - " */\n" - "\n" - "#ifndef __ARM_MVE_H\n" + << LLVMLicenseHeader + << "#ifndef __ARM_MVE_H\n" "#define __ARM_MVE_H\n" "\n" "#if !__ARM_FEATURE_MVE\n" @@ -1598,6 +1898,10 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { "#endif\n" "\n" "#include <stdint.h>\n" + "\n" + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n" "\n"; for (size_t i = 0; i < NumParts; ++i) { @@ -1616,7 +1920,11 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { OS << "#endif /* " << condition << " */\n\n"; } - OS << "#endif /* __ARM_MVE_H */\n"; + OS << "#ifdef __cplusplus\n" + "} /* extern \"C\" */\n" + "#endif\n" + "\n" + "#endif /* __ARM_MVE_H */\n"; } void MveEmitter::EmitBuiltinDef(raw_ostream &OS) { @@ -1632,12 +1940,12 @@ void MveEmitter::EmitBuiltinDef(raw_ostream &OS) { const ACLEIntrinsic &Int = *kv.second; if (Int.polymorphic()) { StringRef Name = Int.shortName(); - if (ShortNamesSeen.find(Name) == ShortNamesSeen.end()) { + if (ShortNamesSeen.find(std::string(Name)) == ShortNamesSeen.end()) { OS << "BUILTIN(__builtin_arm_mve_" << Name << ", \"vi.\", \"nt"; if (Int.nonEvaluating()) OS << "u"; // indicate that this builtin doesn't evaluate its args OS << "\")\n"; - ShortNamesSeen.insert(Name); + ShortNamesSeen.insert(std::string(Name)); } } } @@ -1645,213 +1953,206 @@ void MveEmitter::EmitBuiltinDef(raw_ostream &OS) { void MveEmitter::EmitBuiltinSema(raw_ostream &OS) { std::map<std::string, std::set<std::string>> Checks; - - for (const auto &kv : ACLEIntrinsics) { - const ACLEIntrinsic &Int = *kv.second; - std::string Check = Int.genSema(); - if (!Check.empty()) - Checks[Check].insert(Int.fullName()); - } + GroupSemaChecks(Checks); for (const auto &kv : Checks) { for (StringRef Name : kv.second) OS << "case ARM::BI__builtin_arm_mve_" << Name << ":\n"; - OS << kv.first; + OS << " return " << kv.first; } } -// Machinery for the grouping of intrinsics by similar codegen. +// ----------------------------------------------------------------------------- +// Class that describes an ACLE intrinsic implemented as a macro. // -// The general setup is that 'MergeableGroup' stores the things that a set of -// similarly shaped intrinsics have in common: the text of their code -// generation, and the number and type of their parameter variables. -// MergeableGroup is the key in a std::map whose value is a set of -// OutputIntrinsic, which stores the ways in which a particular intrinsic -// specializes the MergeableGroup's generic description: the function name and -// the _values_ of the parameter variables. +// This class is used when the intrinsic is polymorphic in 2 or 3 types, but we +// want to avoid a combinatorial explosion by reinterpreting the arguments to +// fixed types. -struct ComparableStringVector : std::vector<std::string> { - // Infrastructure: a derived class of vector<string> which comes with an - // ordering, so that it can be used as a key in maps and an element in sets. - // There's no requirement on the ordering beyond being deterministic. - bool operator<(const ComparableStringVector &rhs) const { - if (size() != rhs.size()) - return size() < rhs.size(); - for (size_t i = 0, e = size(); i < e; ++i) - if ((*this)[i] != rhs[i]) - return (*this)[i] < rhs[i]; - return false; - } -}; +class FunctionMacro { + std::vector<StringRef> Params; + StringRef Definition; -struct OutputIntrinsic { - const ACLEIntrinsic *Int; - std::string Name; - ComparableStringVector ParamValues; - bool operator<(const OutputIntrinsic &rhs) const { - if (Name != rhs.Name) - return Name < rhs.Name; - return ParamValues < rhs.ParamValues; - } -}; -struct MergeableGroup { - std::string Code; - ComparableStringVector ParamTypes; - bool operator<(const MergeableGroup &rhs) const { - if (Code != rhs.Code) - return Code < rhs.Code; - return ParamTypes < rhs.ParamTypes; - } +public: + FunctionMacro(const Record &R); + + const std::vector<StringRef> &getParams() const { return Params; } + StringRef getDefinition() const { return Definition; } }; -void MveEmitter::EmitBuiltinCG(raw_ostream &OS) { - // Pass 1: generate code for all the intrinsics as if every type or constant - // that can possibly be abstracted out into a parameter variable will be. - // This identifies the sets of intrinsics we'll group together into a single - // piece of code generation. +FunctionMacro::FunctionMacro(const Record &R) { + Params = R.getValueAsListOfStrings("params"); + Definition = R.getValueAsString("definition"); +} - std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroupsPrelim; +// ----------------------------------------------------------------------------- +// The class used for generating arm_cde.h and related Clang bits +// - for (const auto &kv : ACLEIntrinsics) { - const ACLEIntrinsic &Int = *kv.second; +class CdeEmitter : public EmitterBase { + std::map<StringRef, FunctionMacro> FunctionMacros; - MergeableGroup MG; - OutputIntrinsic OI; +public: + CdeEmitter(RecordKeeper &Records); + void EmitHeader(raw_ostream &OS) override; + void EmitBuiltinDef(raw_ostream &OS) override; + void EmitBuiltinSema(raw_ostream &OS) override; +}; - OI.Int = ∬ - OI.Name = Int.fullName(); - CodeGenParamAllocator ParamAllocPrelim{&MG.ParamTypes, &OI.ParamValues}; - raw_string_ostream OS(MG.Code); - Int.genCode(OS, ParamAllocPrelim, 1); - OS.flush(); +CdeEmitter::CdeEmitter(RecordKeeper &Records) : EmitterBase(Records) { + for (Record *R : Records.getAllDerivedDefinitions("FunctionMacro")) + FunctionMacros.emplace(R->getName(), FunctionMacro(*R)); +} - MergeableGroupsPrelim[MG].insert(OI); - } +void CdeEmitter::EmitHeader(raw_ostream &OS) { + // Accumulate pieces of the header file that will be enabled under various + // different combinations of #ifdef. The index into parts[] is one of the + // following: + constexpr unsigned None = 0; + constexpr unsigned MVE = 1; + constexpr unsigned MVEFloat = 2; - // Pass 2: for each of those groups, optimize the parameter variable set by - // eliminating 'parameters' that are the same for all intrinsics in the - // group, and merging together pairs of parameter variables that take the - // same values as each other for all intrinsics in the group. + constexpr unsigned NumParts = 3; + raw_self_contained_string_ostream parts[NumParts]; - std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroups; + // Write typedefs for all the required vector types, and a few scalar + // types that don't already have the name we want them to have. - for (const auto &kv : MergeableGroupsPrelim) { - const MergeableGroup &MG = kv.first; - std::vector<int> ParamNumbers; - std::map<ComparableStringVector, int> ParamNumberMap; + parts[MVE] << "typedef uint16_t mve_pred16_t;\n"; + parts[MVEFloat] << "typedef __fp16 float16_t;\n" + "typedef float float32_t;\n"; + for (const auto &kv : ScalarTypes) { + const ScalarType *ST = kv.second.get(); + if (ST->hasNonstandardName()) + continue; + // We don't have float64x2_t + if (ST->kind() == ScalarTypeKind::Float && ST->sizeInBits() == 64) + continue; + raw_ostream &OS = parts[ST->requiresFloat() ? MVEFloat : MVE]; + const VectorType *VT = getVectorType(ST); - // Loop over the parameters for this group. - for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) { - // Is this parameter the same for all intrinsics in the group? - const OutputIntrinsic &OI_first = *kv.second.begin(); - bool Constant = all_of(kv.second, [&](const OutputIntrinsic &OI) { - return OI.ParamValues[i] == OI_first.ParamValues[i]; - }); + OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes() + << "), __clang_arm_mve_strict_polymorphism)) " << ST->cName() << " " + << VT->cName() << ";\n"; + } + parts[MVE] << "\n"; + parts[MVEFloat] << "\n"; - // If so, record it as -1, meaning 'no parameter variable needed'. Then - // the corresponding call to allocParam in pass 2 will not generate a - // variable at all, and just use the value inline. - if (Constant) { - ParamNumbers.push_back(-1); - continue; - } + // Write declarations for all the intrinsics. - // Otherwise, make a list of the values this parameter takes for each - // intrinsic, and see if that value vector matches anything we already - // have. We also record the parameter type, so that we don't accidentally - // match up two parameter variables with different types. (Not that - // there's much chance of them having textually equivalent values, but in - // _principle_ it could happen.) - ComparableStringVector key; - key.push_back(MG.ParamTypes[i]); - for (const auto &OI : kv.second) - key.push_back(OI.ParamValues[i]); + for (const auto &kv : ACLEIntrinsics) { + const ACLEIntrinsic &Int = *kv.second; - auto Found = ParamNumberMap.find(key); - if (Found != ParamNumberMap.end()) { - // Yes, an existing parameter variable can be reused for this. - ParamNumbers.push_back(Found->second); + // We generate each intrinsic twice, under its full unambiguous + // name and its shorter polymorphic name (if the latter exists). + for (bool Polymorphic : {false, true}) { + if (Polymorphic && !Int.polymorphic()) + continue; + if (!Polymorphic && Int.polymorphicOnly()) continue; - } - // No, we need a new parameter variable. - int ExistingIndex = ParamNumberMap.size(); - ParamNumberMap[key] = ExistingIndex; - ParamNumbers.push_back(ExistingIndex); + raw_ostream &OS = + parts[Int.requiresFloat() ? MVEFloat + : Int.requiresMVE() ? MVE : None]; + + // Make the name of the function in this declaration. + std::string FunctionName = + "__arm_" + (Polymorphic ? Int.shortName() : Int.fullName()); + + // Make strings for the types involved in the function's + // prototype. + std::string RetTypeName = Int.returnType()->cName(); + if (!StringRef(RetTypeName).endswith("*")) + RetTypeName += " "; + + std::vector<std::string> ArgTypeNames; + for (const Type *ArgTypePtr : Int.argTypes()) + ArgTypeNames.push_back(ArgTypePtr->cName()); + std::string ArgTypesString = + join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", "); + + // Emit the actual declaration. See MveEmitter::EmitHeader for detailed + // comments + OS << "static __inline__ __attribute__((" + << (Polymorphic ? "__overloadable__, " : "") + << "__clang_arm_builtin_alias(__builtin_arm_" << Int.builtinExtension() + << "_" << Int.fullName() << ")))\n" + << RetTypeName << FunctionName << "(" << ArgTypesString << ");\n"; } + } - // Now we're ready to do the pass 2 code generation, which will emit the - // reduced set of parameter variables we've just worked out. - - for (const auto &OI_prelim : kv.second) { - const ACLEIntrinsic *Int = OI_prelim.Int; - - MergeableGroup MG; - OutputIntrinsic OI; - - OI.Int = OI_prelim.Int; - OI.Name = OI_prelim.Name; - CodeGenParamAllocator ParamAlloc{&MG.ParamTypes, &OI.ParamValues, - &ParamNumbers}; - raw_string_ostream OS(MG.Code); - Int->genCode(OS, ParamAlloc, 2); - OS.flush(); + for (const auto &kv : FunctionMacros) { + StringRef Name = kv.first; + const FunctionMacro &FM = kv.second; - MergeableGroups[MG].insert(OI); - } + raw_ostream &OS = parts[MVE]; + OS << "#define " + << "__arm_" << Name << "(" << join(FM.getParams(), ", ") << ") " + << FM.getDefinition() << "\n"; } - // Output the actual C++ code. - - for (const auto &kv : MergeableGroups) { - const MergeableGroup &MG = kv.first; + for (auto &part : parts) + part << "\n"; - // List of case statements in the main switch on BuiltinID, and an open - // brace. - const char *prefix = ""; - for (const auto &OI : kv.second) { - OS << prefix << "case ARM::BI__builtin_arm_mve_" << OI.Name << ":"; - prefix = "\n"; - } - OS << " {\n"; + // Now we've finished accumulating bits and pieces into the parts[] array. + // Put it all together to write the final output file. - if (!MG.ParamTypes.empty()) { - // If we've got some parameter variables, then emit their declarations... - for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) { - StringRef Type = MG.ParamTypes[i]; - OS << " " << Type; - if (!Type.endswith("*")) - OS << " "; - OS << " Param" << utostr(i) << ";\n"; - } + OS << "/*===---- arm_cde.h - ARM CDE intrinsics " + "-----------------------------------===\n" + << LLVMLicenseHeader + << "#ifndef __ARM_CDE_H\n" + "#define __ARM_CDE_H\n" + "\n" + "#if !__ARM_FEATURE_CDE\n" + "#error \"CDE support not enabled\"\n" + "#endif\n" + "\n" + "#include <stdint.h>\n" + "\n" + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n" + "\n"; - // ... and an inner switch on BuiltinID that will fill them in with each - // individual intrinsic's values. - OS << " switch (BuiltinID) {\n"; - for (const auto &OI : kv.second) { - OS << " case ARM::BI__builtin_arm_mve_" << OI.Name << ":\n"; - for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) - OS << " Param" << utostr(i) << " = " << OI.ParamValues[i] << ";\n"; - OS << " break;\n"; - } - OS << " }\n"; - } + for (size_t i = 0; i < NumParts; ++i) { + std::string condition; + if (i == MVEFloat) + condition = "__ARM_FEATURE_MVE & 2"; + else if (i == MVE) + condition = "__ARM_FEATURE_MVE"; - // And finally, output the code, and close the outer pair of braces. (The - // code will always end with a 'return' statement, so we need not insert a - // 'break' here.) - OS << MG.Code << "}\n"; + if (!condition.empty()) + OS << "#if " << condition << "\n\n"; + OS << parts[i].str(); + if (!condition.empty()) + OS << "#endif /* " << condition << " */\n\n"; } + + OS << "#ifdef __cplusplus\n" + "} /* extern \"C\" */\n" + "#endif\n" + "\n" + "#endif /* __ARM_CDE_H */\n"; } -void MveEmitter::EmitBuiltinAliases(raw_ostream &OS) { +void CdeEmitter::EmitBuiltinDef(raw_ostream &OS) { for (const auto &kv : ACLEIntrinsics) { + if (kv.second->headerOnly()) + continue; const ACLEIntrinsic &Int = *kv.second; - OS << "case ARM::BI__builtin_arm_mve_" << Int.fullName() << ":\n" - << " return AliasName == \"" << Int.fullName() << "\""; - if (Int.polymorphic()) - OS << " || AliasName == \"" << Int.shortName() << "\""; - OS << ";\n"; + OS << "TARGET_HEADER_BUILTIN(__builtin_arm_cde_" << Int.fullName() + << ", \"\", \"ncU\", \"arm_cde.h\", ALL_LANGUAGES, \"\")\n"; + } +} + +void CdeEmitter::EmitBuiltinSema(raw_ostream &OS) { + std::map<std::string, std::set<std::string>> Checks; + GroupSemaChecks(Checks); + + for (const auto &kv : Checks) { + for (StringRef Name : kv.second) + OS << "case ARM::BI__builtin_arm_cde_" << Name << ":\n"; + OS << " Err = " << kv.first << " break;\n"; } } @@ -1859,6 +2160,8 @@ void MveEmitter::EmitBuiltinAliases(raw_ostream &OS) { namespace clang { +// MVE + void EmitMveHeader(RecordKeeper &Records, raw_ostream &OS) { MveEmitter(Records).EmitHeader(OS); } @@ -1879,4 +2182,26 @@ void EmitMveBuiltinAliases(RecordKeeper &Records, raw_ostream &OS) { MveEmitter(Records).EmitBuiltinAliases(OS); } +// CDE + +void EmitCdeHeader(RecordKeeper &Records, raw_ostream &OS) { + CdeEmitter(Records).EmitHeader(OS); +} + +void EmitCdeBuiltinDef(RecordKeeper &Records, raw_ostream &OS) { + CdeEmitter(Records).EmitBuiltinDef(OS); +} + +void EmitCdeBuiltinSema(RecordKeeper &Records, raw_ostream &OS) { + CdeEmitter(Records).EmitBuiltinSema(OS); +} + +void EmitCdeBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { + CdeEmitter(Records).EmitBuiltinCG(OS); +} + +void EmitCdeBuiltinAliases(RecordKeeper &Records, raw_ostream &OS) { + CdeEmitter(Records).EmitBuiltinAliases(OS); +} + } // end namespace clang |