summaryrefslogtreecommitdiff
path: root/clang/utils/TableGen/MveEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/utils/TableGen/MveEmitter.cpp')
-rw-r--r--clang/utils/TableGen/MveEmitter.cpp835
1 files changed, 580 insertions, 255 deletions
diff --git a/clang/utils/TableGen/MveEmitter.cpp b/clang/utils/TableGen/MveEmitter.cpp
index 431e5c477c2b7..e9ae08ac4c051 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 = &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 = &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