summaryrefslogtreecommitdiff
path: root/clang/utils/TableGen
diff options
context:
space:
mode:
Diffstat (limited to 'clang/utils/TableGen')
-rw-r--r--clang/utils/TableGen/ClangASTNodesEmitter.cpp6
-rw-r--r--clang/utils/TableGen/ClangAttrEmitter.cpp853
-rw-r--r--clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp2
-rw-r--r--clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp4
-rw-r--r--clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp5
-rw-r--r--clang/utils/TableGen/ClangDiagnosticsEmitter.cpp69
-rw-r--r--clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp28
-rw-r--r--clang/utils/TableGen/ClangOptionDocEmitter.cpp12
-rw-r--r--clang/utils/TableGen/ClangSACheckersEmitter.cpp27
-rw-r--r--clang/utils/TableGen/MveEmitter.cpp835
-rw-r--r--clang/utils/TableGen/NeonEmitter.cpp411
-rw-r--r--clang/utils/TableGen/SveEmitter.cpp1436
-rw-r--r--clang/utils/TableGen/TableGen.cpp65
-rw-r--r--clang/utils/TableGen/TableGenBackends.h13
14 files changed, 2943 insertions, 823 deletions
diff --git a/clang/utils/TableGen/ClangASTNodesEmitter.cpp b/clang/utils/TableGen/ClangASTNodesEmitter.cpp
index 1cc46cb06570..2b8d7a9efdf1 100644
--- a/clang/utils/TableGen/ClangASTNodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangASTNodesEmitter.cpp
@@ -51,7 +51,7 @@ class ClangASTNodesEmitter {
const std::string &macroHierarchyName() {
assert(Root && "root node not yet derived!");
if (MacroHierarchyName.empty())
- MacroHierarchyName = macroName(Root.getName());
+ MacroHierarchyName = macroName(std::string(Root.getName()));
return MacroHierarchyName;
}
@@ -86,7 +86,7 @@ public:
// Called recursively to ensure that nodes remain contiguous
std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
ASTNode Base) {
- std::string BaseName = macroName(Base.getName());
+ std::string BaseName = macroName(std::string(Base.getName()));
ChildIterator i = Tree.lower_bound(Base), e = Tree.upper_bound(Base);
bool HasChildren = (i != e);
@@ -98,7 +98,7 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
for (; i != e; ++i) {
ASTNode Child = i->second;
bool Abstract = Child.isAbstract();
- std::string NodeName = macroName(Child.getName());
+ std::string NodeName = macroName(std::string(Child.getName()));
OS << "#ifndef " << NodeName << "\n";
OS << "# define " << NodeName << "(Type, Base) "
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 4c3742c8e339..bd20e447a950 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -48,22 +48,19 @@ namespace {
class FlattenedSpelling {
std::string V, N, NS;
- bool K;
+ bool K = false;
public:
FlattenedSpelling(const std::string &Variety, const std::string &Name,
const std::string &Namespace, bool KnownToGCC) :
V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {}
- explicit FlattenedSpelling(const Record &Spelling) :
- V(Spelling.getValueAsString("Variety")),
- N(Spelling.getValueAsString("Name")) {
-
+ explicit FlattenedSpelling(const Record &Spelling)
+ : V(std::string(Spelling.getValueAsString("Variety"))),
+ N(std::string(Spelling.getValueAsString("Name"))) {
assert(V != "GCC" && V != "Clang" &&
"Given a GCC spelling, which means this hasn't been flattened!");
if (V == "CXX11" || V == "C2x" || V == "Pragma")
- NS = Spelling.getValueAsString("Namespace");
- bool Unset;
- K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset);
+ NS = std::string(Spelling.getValueAsString("Namespace"));
}
const std::string &variety() const { return V; }
@@ -83,14 +80,15 @@ GetFlattenedSpellings(const Record &Attr) {
StringRef Variety = Spelling->getValueAsString("Variety");
StringRef Name = Spelling->getValueAsString("Name");
if (Variety == "GCC") {
- // Gin up two new spelling objects to add into the list.
- Ret.emplace_back("GNU", Name, "", true);
- Ret.emplace_back("CXX11", Name, "gnu", true);
+ Ret.emplace_back("GNU", std::string(Name), "", true);
+ Ret.emplace_back("CXX11", std::string(Name), "gnu", true);
+ if (Spelling->getValueAsBit("AllowInC"))
+ Ret.emplace_back("C2x", std::string(Name), "gnu", true);
} else if (Variety == "Clang") {
- Ret.emplace_back("GNU", Name, "", false);
- Ret.emplace_back("CXX11", Name, "clang", false);
+ Ret.emplace_back("GNU", std::string(Name), "", false);
+ Ret.emplace_back("CXX11", std::string(Name), "clang", false);
if (Spelling->getValueAsBit("AllowInC"))
- Ret.emplace_back("C2x", Name, "clang", false);
+ Ret.emplace_back("C2x", std::string(Name), "clang", false);
} else
Ret.push_back(FlattenedSpelling(*Spelling));
}
@@ -100,14 +98,16 @@ GetFlattenedSpellings(const Record &Attr) {
static std::string ReadPCHRecord(StringRef type) {
return StringSwitch<std::string>(type)
- .EndsWith("Decl *", "Record.GetLocalDeclAs<"
- + std::string(type, 0, type.size()-1) + ">(Record.readInt())")
- .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()")
- .Case("Expr *", "Record.readExpr()")
- .Case("IdentifierInfo *", "Record.readIdentifier()")
- .Case("StringRef", "Record.readString()")
- .Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())")
- .Default("Record.readInt()");
+ .EndsWith("Decl *", "Record.GetLocalDeclAs<" +
+ std::string(type.data(), 0, type.size() - 1) +
+ ">(Record.readInt())")
+ .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()")
+ .Case("Expr *", "Record.readExpr()")
+ .Case("IdentifierInfo *", "Record.readIdentifier()")
+ .Case("StringRef", "Record.readString()")
+ .Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())")
+ .Case("OMPTraitInfo *", "Record.readOMPTraitInfo()")
+ .Default("Record.readInt()");
}
// Get a type that is suitable for storing an object of the specified type.
@@ -119,14 +119,20 @@ static StringRef getStorageType(StringRef type) {
// Assumes that the way to get the value is SA->getname()
static std::string WritePCHRecord(StringRef type, StringRef name) {
- return "Record." + StringSwitch<std::string>(type)
- .EndsWith("Decl *", "AddDeclRef(" + std::string(name) + ");\n")
- .Case("TypeSourceInfo *", "AddTypeSourceInfo(" + std::string(name) + ");\n")
- .Case("Expr *", "AddStmt(" + std::string(name) + ");\n")
- .Case("IdentifierInfo *", "AddIdentifierRef(" + std::string(name) + ");\n")
- .Case("StringRef", "AddString(" + std::string(name) + ");\n")
- .Case("ParamIdx", "push_back(" + std::string(name) + ".serialize());\n")
- .Default("push_back(" + std::string(name) + ");\n");
+ return "Record." +
+ StringSwitch<std::string>(type)
+ .EndsWith("Decl *", "AddDeclRef(" + std::string(name) + ");\n")
+ .Case("TypeSourceInfo *",
+ "AddTypeSourceInfo(" + std::string(name) + ");\n")
+ .Case("Expr *", "AddStmt(" + std::string(name) + ");\n")
+ .Case("IdentifierInfo *",
+ "AddIdentifierRef(" + std::string(name) + ");\n")
+ .Case("StringRef", "AddString(" + std::string(name) + ");\n")
+ .Case("ParamIdx",
+ "push_back(" + std::string(name) + ".serialize());\n")
+ .Case("OMPTraitInfo *",
+ "writeOMPTraitInfo(" + std::string(name) + ");\n")
+ .Default("push_back(" + std::string(name) + ");\n");
}
// Normalize attribute name by removing leading and trailing
@@ -167,7 +173,7 @@ static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records,
std::string AN;
if (Attr->isSubClassOf("TargetSpecificAttr") &&
!Attr->isValueUnset("ParseKind")) {
- AN = Attr->getValueAsString("ParseKind");
+ AN = std::string(Attr->getValueAsString("ParseKind"));
// If this attribute has already been handled, it does not need to be
// handled again.
@@ -196,8 +202,8 @@ namespace {
public:
Argument(const Record &Arg, StringRef Attr)
- : lowerName(Arg.getValueAsString("Name")), upperName(lowerName),
- attrName(Attr), isOpt(false), Fake(false) {
+ : lowerName(std::string(Arg.getValueAsString("Name"))),
+ upperName(lowerName), attrName(Attr), isOpt(false), Fake(false) {
if (!lowerName.empty()) {
lowerName[0] = std::tolower(lowerName[0]);
upperName[0] = std::toupper(upperName[0]);
@@ -299,8 +305,9 @@ namespace {
}
void writePCHWrite(raw_ostream &OS) const override {
- OS << " " << WritePCHRecord(type, "SA->get" +
- std::string(getUpperName()) + "()");
+ OS << " "
+ << WritePCHRecord(type,
+ "SA->get" + std::string(getUpperName()) + "()");
}
std::string getIsOmitted() const override {
@@ -331,9 +338,9 @@ namespace {
}
void writeDump(raw_ostream &OS) const override {
- if (type == "FunctionDecl *" || type == "NamedDecl *") {
+ if (StringRef(type).endswith("Decl *")) {
OS << " OS << \" \";\n";
- OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n";
+ OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n";
} else if (type == "IdentifierInfo *") {
// Some non-optional (comma required) identifier arguments can be the
// empty string but are then recorded as a nullptr.
@@ -355,6 +362,8 @@ namespace {
OS << " if (SA->get" << getUpperName() << "().isValid())\n ";
OS << " OS << \" \" << SA->get" << getUpperName()
<< "().getSourceIndex();\n";
+ } else if (type == "OMPTraitInfo *") {
+ OS << " OS << \" \" << SA->get" << getUpperName() << "();\n";
} else {
llvm_unreachable("Unknown SimpleArgument type!");
}
@@ -416,8 +425,8 @@ namespace {
}
void writeCtorBody(raw_ostream &OS) const override {
- OS << " if (!" << getUpperName() << ".empty())\n";
- OS << " std::memcpy(" << getLowerName() << ", " << getUpperName()
+ OS << " if (!" << getUpperName() << ".empty())\n";
+ OS << " std::memcpy(" << getLowerName() << ", " << getUpperName()
<< ".data(), " << getLowerName() << "Length);\n";
}
@@ -471,6 +480,7 @@ namespace {
void writeAccessors(raw_ostream &OS) const override {
OS << " bool is" << getUpperName() << "Dependent() const;\n";
+ OS << " bool is" << getUpperName() << "ErrorDependent() const;\n";
OS << " unsigned get" << getUpperName() << "(ASTContext &Ctx) const;\n";
@@ -495,12 +505,21 @@ namespace {
OS << " if (is" << getLowerName() << "Expr)\n";
OS << " return " << getLowerName() << "Expr && (" << getLowerName()
<< "Expr->isValueDependent() || " << getLowerName()
- << "Expr->isTypeDependent());\n";
+ << "Expr->isTypeDependent());\n";
OS << " else\n";
OS << " return " << getLowerName()
<< "Type->getType()->isDependentType();\n";
OS << "}\n";
+ OS << "bool " << getAttrName() << "Attr::is" << getUpperName()
+ << "ErrorDependent() const {\n";
+ OS << " if (is" << getLowerName() << "Expr)\n";
+ OS << " return " << getLowerName() << "Expr && " << getLowerName()
+ << "Expr->containsErrors();\n";
+ OS << " return " << getLowerName()
+ << "Type->getType()->containsErrors();\n";
+ OS << "}\n";
+
// FIXME: Do not do the calculation here
// FIXME: Handle types correctly
// A null pointer means maximum alignment
@@ -520,11 +539,11 @@ namespace {
void writeASTVisitorTraversal(raw_ostream &OS) const override {
StringRef Name = getUpperName();
OS << " if (A->is" << Name << "Expr()) {\n"
- << " if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n"
- << " return false;\n"
+ << " if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n"
+ << " return false;\n"
<< " } else if (auto *TSI = A->get" << Name << "Type()) {\n"
<< " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n"
- << " return false;\n"
+ << " return false;\n"
<< " }\n";
}
@@ -642,7 +661,7 @@ namespace {
VariadicArgument(const Record &Arg, StringRef Attr, std::string T)
: Argument(Arg, Attr), Type(std::move(T)),
ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"),
- RangeName(getLowerName()) {}
+ RangeName(std::string(getLowerName())) {}
const std::string &getType() const { return Type; }
const std::string &getArgName() const { return ArgName; }
@@ -653,7 +672,7 @@ namespace {
std::string IteratorType = getLowerName().str() + "_iterator";
std::string BeginFn = getLowerName().str() + "_begin()";
std::string EndFn = getLowerName().str() + "_end()";
-
+
OS << " typedef " << Type << "* " << IteratorType << ";\n";
OS << " " << IteratorType << " " << BeginFn << " const {"
<< " return " << ArgName << "; }\n";
@@ -681,8 +700,8 @@ namespace {
}
void writeCtorBody(raw_ostream &OS) const override {
- OS << " std::copy(" << getUpperName() << ", " << getUpperName()
- << " + " << ArgSizeName << ", " << ArgName << ");\n";
+ OS << " std::copy(" << getUpperName() << ", " << getUpperName() << " + "
+ << ArgSizeName << ", " << ArgName << ");\n";
}
void writeCtorInitializers(raw_ostream &OS) const override {
@@ -719,8 +738,8 @@ namespace {
// If we can't store the values in the current type (if it's something
// like StringRef), store them in a different type and convert the
// container afterwards.
- std::string StorageType = getStorageType(getType());
- std::string StorageName = getLowerName();
+ std::string StorageType = std::string(getStorageType(getType()));
+ std::string StorageName = std::string(getLowerName());
if (StorageType != getType()) {
StorageName += "Storage";
OS << " SmallVector<" << StorageType << ", 4> "
@@ -805,11 +824,10 @@ namespace {
public:
EnumArgument(const Record &Arg, StringRef Attr)
- : Argument(Arg, Attr), type(Arg.getValueAsString("Type")),
- values(Arg.getValueAsListOfStrings("Values")),
- enums(Arg.getValueAsListOfStrings("Enums")),
- uniques(uniqueEnumsInOrder(enums))
- {
+ : Argument(Arg, Attr), type(std::string(Arg.getValueAsString("Type"))),
+ values(Arg.getValueAsListOfStrings("Values")),
+ enums(Arg.getValueAsListOfStrings("Enums")),
+ uniques(uniqueEnumsInOrder(enums)) {
// FIXME: Emit a proper error
assert(!uniques.empty());
}
@@ -885,40 +903,48 @@ namespace {
OS << " }\n";
}
- void writeConversion(raw_ostream &OS) const {
- OS << " static bool ConvertStrTo" << type << "(StringRef Val, ";
- OS << type << " &Out) {\n";
- OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
+ void writeConversion(raw_ostream &OS, bool Header) const {
+ if (Header) {
+ OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
+ << " &Out);\n";
+ OS << " static const char *Convert" << type << "ToStr(" << type
+ << " Val);\n";
+ return;
+ }
+
+ OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
+ << "(StringRef Val, " << type << " &Out) {\n";
+ OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
OS << type << ">>(Val)\n";
for (size_t I = 0; I < enums.size(); ++I) {
- OS << " .Case(\"" << values[I] << "\", ";
+ OS << " .Case(\"" << values[I] << "\", ";
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
}
- OS << " .Default(Optional<" << type << ">());\n";
- OS << " if (R) {\n";
- OS << " Out = *R;\n return true;\n }\n";
- OS << " return false;\n";
- OS << " }\n\n";
+ OS << " .Default(Optional<" << type << ">());\n";
+ OS << " if (R) {\n";
+ OS << " Out = *R;\n return true;\n }\n";
+ OS << " return false;\n";
+ OS << "}\n\n";
// Mapping from enumeration values back to enumeration strings isn't
// trivial because some enumeration values have multiple named
// enumerators, such as type_visibility(internal) and
// type_visibility(hidden) both mapping to TypeVisibilityAttr::Hidden.
- OS << " static const char *Convert" << type << "ToStr("
- << type << " Val) {\n"
- << " switch(Val) {\n";
+ OS << "const char *" << getAttrName() << "Attr::Convert" << type
+ << "ToStr(" << type << " Val) {\n"
+ << " switch(Val) {\n";
SmallDenseSet<StringRef, 8> Uniques;
for (size_t I = 0; I < enums.size(); ++I) {
if (Uniques.insert(enums[I]).second)
- OS << " case " << getAttrName() << "Attr::" << enums[I]
- << ": return \"" << values[I] << "\";\n";
+ OS << " case " << getAttrName() << "Attr::" << enums[I]
+ << ": return \"" << values[I] << "\";\n";
}
- OS << " }\n"
- << " llvm_unreachable(\"No enumerator with that value\");\n"
- << " }\n";
+ OS << " }\n"
+ << " llvm_unreachable(\"No enumerator with that value\");\n"
+ << "}\n";
}
};
-
+
class VariadicEnumArgument: public VariadicArgument {
std::string type, QualifiedTypeName;
std::vector<StringRef> values, enums, uniques;
@@ -934,20 +960,20 @@ namespace {
public:
VariadicEnumArgument(const Record &Arg, StringRef Attr)
- : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")),
- type(Arg.getValueAsString("Type")),
- values(Arg.getValueAsListOfStrings("Values")),
- enums(Arg.getValueAsListOfStrings("Enums")),
- uniques(uniqueEnumsInOrder(enums))
- {
+ : VariadicArgument(Arg, Attr,
+ std::string(Arg.getValueAsString("Type"))),
+ type(std::string(Arg.getValueAsString("Type"))),
+ values(Arg.getValueAsListOfStrings("Values")),
+ enums(Arg.getValueAsListOfStrings("Enums")),
+ uniques(uniqueEnumsInOrder(enums)) {
QualifiedTypeName = getAttrName().str() + "Attr::" + type;
-
+
// FIXME: Emit a proper error
assert(!uniques.empty());
}
bool isVariadicEnumArg() const override { return true; }
-
+
void writeDeclarations(raw_ostream &OS) const override {
auto i = uniques.cbegin(), e = uniques.cend();
// The last one needs to not have a comma.
@@ -960,7 +986,7 @@ namespace {
OS << " " << *e << "\n";
OS << " };\n";
OS << "private:\n";
-
+
VariadicArgument::writeDeclarations(OS);
}
@@ -997,33 +1023,42 @@ namespace {
OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)");
}
- void writeConversion(raw_ostream &OS) const {
- OS << " static bool ConvertStrTo" << type << "(StringRef Val, ";
+ void writeConversion(raw_ostream &OS, bool Header) const {
+ if (Header) {
+ OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
+ << " &Out);\n";
+ OS << " static const char *Convert" << type << "ToStr(" << type
+ << " Val);\n";
+ return;
+ }
+
+ OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
+ << "(StringRef Val, ";
OS << type << " &Out) {\n";
- OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
+ OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
OS << type << ">>(Val)\n";
for (size_t I = 0; I < enums.size(); ++I) {
- OS << " .Case(\"" << values[I] << "\", ";
+ OS << " .Case(\"" << values[I] << "\", ";
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
}
- OS << " .Default(Optional<" << type << ">());\n";
- OS << " if (R) {\n";
- OS << " Out = *R;\n return true;\n }\n";
- OS << " return false;\n";
- OS << " }\n\n";
-
- OS << " static const char *Convert" << type << "ToStr("
- << type << " Val) {\n"
- << " switch(Val) {\n";
+ OS << " .Default(Optional<" << type << ">());\n";
+ OS << " if (R) {\n";
+ OS << " Out = *R;\n return true;\n }\n";
+ OS << " return false;\n";
+ OS << "}\n\n";
+
+ OS << "const char *" << getAttrName() << "Attr::Convert" << type
+ << "ToStr(" << type << " Val) {\n"
+ << " switch(Val) {\n";
SmallDenseSet<StringRef, 8> Uniques;
for (size_t I = 0; I < enums.size(); ++I) {
if (Uniques.insert(enums[I]).second)
- OS << " case " << getAttrName() << "Attr::" << enums[I]
- << ": return \"" << values[I] << "\";\n";
+ OS << " case " << getAttrName() << "Attr::" << enums[I]
+ << ": return \"" << values[I] << "\";\n";
}
- OS << " }\n"
- << " llvm_unreachable(\"No enumerator with that value\");\n"
- << " }\n";
+ OS << " }\n"
+ << " llvm_unreachable(\"No enumerator with that value\");\n"
+ << "}\n";
}
};
@@ -1037,7 +1072,7 @@ namespace {
OS << " VersionTuple get" << getUpperName() << "() const {\n";
OS << " return " << getLowerName() << ";\n";
OS << " }\n";
- OS << " void set" << getUpperName()
+ OS << " void set" << getUpperName()
<< "(ASTContext &C, VersionTuple V) {\n";
OS << " " << getLowerName() << " = V;\n";
OS << " }";
@@ -1199,15 +1234,15 @@ namespace {
{}
void writeCtorBody(raw_ostream &OS) const override {
- OS << " for (size_t I = 0, E = " << getArgSizeName() << "; I != E;\n"
- " ++I) {\n"
- " StringRef Ref = " << getUpperName() << "[I];\n"
- " if (!Ref.empty()) {\n"
- " char *Mem = new (Ctx, 1) char[Ref.size()];\n"
- " std::memcpy(Mem, Ref.data(), Ref.size());\n"
- " " << getArgName() << "[I] = StringRef(Mem, Ref.size());\n"
- " }\n"
- " }\n";
+ OS << " for (size_t I = 0, E = " << getArgSizeName() << "; I != E;\n"
+ " ++I) {\n"
+ " StringRef Ref = " << getUpperName() << "[I];\n"
+ " if (!Ref.empty()) {\n"
+ " char *Mem = new (Ctx, 1) char[Ref.size()];\n"
+ " std::memcpy(Mem, Ref.data(), Ref.size());\n"
+ " " << getArgName() << "[I] = StringRef(Mem, Ref.size());\n"
+ " }\n"
+ " }\n";
}
void writeValueImpl(raw_ostream &OS) const override {
@@ -1241,8 +1276,9 @@ namespace {
}
void writePCHWrite(raw_ostream &OS) const override {
- OS << " " << WritePCHRecord(
- getType(), "SA->get" + std::string(getUpperName()) + "Loc()");
+ OS << " "
+ << WritePCHRecord(getType(),
+ "SA->get" + std::string(getUpperName()) + "Loc()");
}
};
@@ -1263,10 +1299,9 @@ createArgument(const Record &Arg, StringRef Attr,
Ptr = std::make_unique<EnumArgument>(Arg, Attr);
else if (ArgName == "ExprArgument")
Ptr = std::make_unique<ExprArgument>(Arg, Attr);
- else if (ArgName == "FunctionArgument")
- Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "FunctionDecl *");
- else if (ArgName == "NamedArgument")
- Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "NamedDecl *");
+ else if (ArgName == "DeclArgument")
+ Ptr = std::make_unique<SimpleArgument>(
+ Arg, Attr, (Arg.getValueAsDef("Kind")->getName() + "Decl *").str());
else if (ArgName == "IdentifierArgument")
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "IdentifierInfo *");
else if (ArgName == "DefaultBoolArgument")
@@ -1303,6 +1338,8 @@ createArgument(const Record &Arg, StringRef Attr,
Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr);
else if (ArgName == "VersionArgument")
Ptr = std::make_unique<VersionArgument>(Arg, Attr);
+ else if (ArgName == "OMPTraitInfoArgument")
+ Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *");
if (!Ptr) {
// Search in reverse order so that the most-derived type is handled first.
@@ -1341,7 +1378,7 @@ static void writeDeprecatedAttrValue(raw_ostream &OS, std::string &Variety) {
OS << " OS << \"";
}
-static void writeGetSpellingFunction(Record &R, raw_ostream &OS) {
+static void writeGetSpellingFunction(const Record &R, raw_ostream &OS) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
OS << "const char *" << R.getName() << "Attr::getSpelling() const {\n";
@@ -1365,7 +1402,7 @@ static void writeGetSpellingFunction(Record &R, raw_ostream &OS) {
}
static void
-writePrettyPrintFunction(Record &R,
+writePrettyPrintFunction(const Record &R,
const std::vector<std::unique_ptr<Argument>> &Args,
raw_ostream &OS) {
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
@@ -1577,11 +1614,12 @@ static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) {
static bool
SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) {
assert(!Spellings.empty() && "An empty list of spellings was provided");
- std::string FirstName = NormalizeNameForSpellingComparison(
- Spellings.front().name());
+ std::string FirstName =
+ std::string(NormalizeNameForSpellingComparison(Spellings.front().name()));
for (const auto &Spelling :
llvm::make_range(std::next(Spellings.begin()), Spellings.end())) {
- std::string Name = NormalizeNameForSpellingComparison(Spelling.name());
+ std::string Name =
+ std::string(NormalizeNameForSpellingComparison(Spelling.name()));
if (Name != FirstName)
return false;
}
@@ -1727,7 +1765,7 @@ struct AttributeSubjectMatchRule {
}
std::string getSpelling() const {
- std::string Result = MetaSubject->getValueAsString("Name");
+ std::string Result = std::string(MetaSubject->getValueAsString("Name"));
if (isSubRule()) {
Result += '(';
if (isNegatedSubRule())
@@ -1752,7 +1790,7 @@ struct AttributeSubjectMatchRule {
}
if (isAbstractRule())
Result += "_abstract";
- return Result.str();
+ return std::string(Result.str());
}
std::string getEnumValue() const { return "attr::" + getEnumValueName(); }
@@ -1801,7 +1839,7 @@ struct PragmaClangAttributeSupport {
void emitMatchRuleList(raw_ostream &OS);
- std::string generateStrictConformsTo(const Record &Attr, raw_ostream &OS);
+ void generateStrictConformsTo(const Record &Attr, raw_ostream &OS);
void generateParsingHelpers(raw_ostream &OS);
};
@@ -1950,6 +1988,11 @@ static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) {
Test += "(";
Test += Code;
Test += ")";
+ if (!E->getValueAsString("Name").empty()) {
+ PrintWarning(
+ E->getLoc(),
+ "non-empty 'Name' field ignored because 'CustomCode' was supplied");
+ }
} else {
Test += "LangOpts.";
Test += E->getValueAsString("Name");
@@ -1962,21 +2005,17 @@ static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) {
return Test;
}
-std::string
+void
PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr,
raw_ostream &OS) {
- if (!isAttributedSupported(Attr))
- return "nullptr";
+ if (!isAttributedSupported(Attr) || Attr.isValueUnset("Subjects"))
+ return;
// Generate a function that constructs a set of matching rules that describe
// to which declarations the attribute should apply to.
- std::string FnName = "matchRulesFor" + Attr.getName().str();
- OS << "static void " << FnName << "(llvm::SmallVectorImpl<std::pair<"
+ OS << "void getPragmaAttributeMatchRules("
+ << "llvm::SmallVectorImpl<std::pair<"
<< AttributeSubjectMatchRule::EnumName
- << ", bool>> &MatchRules, const LangOptions &LangOpts) {\n";
- if (Attr.isValueUnset("Subjects")) {
- OS << "}\n\n";
- return FnName;
- }
+ << ", bool>> &MatchRules, const LangOptions &LangOpts) const override {\n";
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
for (const auto *Subject : Subjects) {
@@ -1993,7 +2032,6 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr,
}
}
OS << "}\n\n";
- return FnName;
}
void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) {
@@ -2223,13 +2261,8 @@ static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records,
OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n";
}
-// Emits the class definitions for attributes.
-void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
- emitSourceFileHeader("Attribute classes' definitions", OS);
-
- OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n";
- OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n";
-
+static void emitAttributes(RecordKeeper &Records, raw_ostream &OS,
+ bool Header) {
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
ParsedAttrMap AttrMap = getParsedAttrList(Records);
@@ -2246,10 +2279,10 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
// When attribute documentation can be generated as part of the build
// itself, this code can be removed.
(void)R.getValueAsListOfDefs("Documentation");
-
+
if (!R.getValueAsBit("ASTNode"))
continue;
-
+
ArrayRef<std::pair<Record *, SMRange>> Supers = R.getSuperClasses();
assert(!Supers.empty() && "Forgot to specify a superclass for the attr");
std::string SuperName;
@@ -2258,12 +2291,15 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
const Record *R = Super.first;
if (R->getName() != "TargetSpecificAttr" &&
R->getName() != "DeclOrTypeAttr" && SuperName.empty())
- SuperName = R->getName();
+ SuperName = std::string(R->getName());
if (R->getName() == "InheritableAttr")
Inheritable = true;
}
- OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n";
+ if (Header)
+ OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n";
+ else
+ OS << "\n// " << R.getName() << "Attr implementation\n\n";
std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
std::vector<std::unique_ptr<Argument>> Args;
@@ -2273,8 +2309,10 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
bool HasFakeArg = false;
for (const auto *ArgRecord : ArgRecords) {
Args.emplace_back(createArgument(*ArgRecord, R.getName()));
- Args.back()->writeDeclarations(OS);
- OS << "\n\n";
+ if (Header) {
+ Args.back()->writeDeclarations(OS);
+ OS << "\n\n";
+ }
// For these purposes, fake takes priority over optional.
if (Args.back()->isFake()) {
@@ -2284,7 +2322,8 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
}
}
- OS << "public:\n";
+ if (Header)
+ OS << "public:\n";
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
@@ -2297,8 +2336,11 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
// This maps spelling index values to semantic Spelling enumerants.
SemanticSpellingMap SemanticToSyntacticMap;
- if (!ElideSpelling)
- OS << CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
+ std::string SpellingEnum;
+ if (Spellings.size() > 1)
+ SpellingEnum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
+ if (Header)
+ OS << SpellingEnum;
const auto &ParsedAttrSpellingItr = llvm::find_if(
AttrMap, [R](const std::pair<std::string, const Record *> &P) {
@@ -2307,9 +2349,14 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
// Emit CreateImplicit factory methods.
auto emitCreate = [&](bool Implicit, bool emitFake) {
- OS << " static " << R.getName() << "Attr *Create";
- if (Implicit)
- OS << "Implicit";
+ if (Header)
+ OS << " static ";
+ OS << R.getName() << "Attr *";
+ if (!Header)
+ OS << R.getName() << "Attr::";
+ OS << "Create";
+ if (Implicit)
+ OS << "Implicit";
OS << "(";
OS << "ASTContext &Ctx";
for (auto const &ai : Args) {
@@ -2317,8 +2364,17 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
OS << ", ";
ai->writeCtorParameters(OS);
}
- OS << ", const AttributeCommonInfo &CommonInfo = {SourceRange{}}) {\n";
- OS << " auto *A = new (Ctx) " << R.getName();
+ OS << ", const AttributeCommonInfo &CommonInfo";
+ if (Header)
+ OS << " = {SourceRange{}}";
+ OS << ")";
+ if (Header) {
+ OS << ";\n";
+ return;
+ }
+
+ OS << " {\n";
+ OS << " auto *A = new (Ctx) " << R.getName();
OS << "Attr(Ctx, CommonInfo";
for (auto const &ai : Args) {
if (ai->isFake() && !emitFake) continue;
@@ -2327,18 +2383,23 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
}
OS << ");\n";
if (Implicit) {
- OS << " A->setImplicit(true);\n";
+ OS << " A->setImplicit(true);\n";
}
if (Implicit || ElideSpelling) {
- OS << " if (!A->isAttributeSpellingListCalculated() && "
+ OS << " if (!A->isAttributeSpellingListCalculated() && "
"!A->getAttrName())\n";
- OS << " A->setAttributeSpellingListIndex(0);\n";
+ OS << " A->setAttributeSpellingListIndex(0);\n";
}
- OS << " return A;\n }\n\n";
+ OS << " return A;\n}\n\n";
};
auto emitCreateNoCI = [&](bool Implicit, bool emitFake) {
- OS <<" static " << R.getName() << "Attr *Create";
+ if (Header)
+ OS << " static ";
+ OS << R.getName() << "Attr *";
+ if (!Header)
+ OS << R.getName() << "Attr::";
+ OS << "Create";
if (Implicit)
OS << "Implicit";
OS << "(";
@@ -2349,12 +2410,19 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
ai->writeCtorParameters(OS);
}
OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax";
- if (!ElideSpelling)
- OS << ", " << R.getName()
- << "Attr::Spelling S = "
- "static_cast<Spelling>(SpellingNotCalculated)";
- OS << ") {\n";
- OS << " AttributeCommonInfo I(Range, ";
+ if (!ElideSpelling) {
+ OS << ", " << R.getName() << "Attr::Spelling S";
+ if (Header)
+ OS << " = static_cast<Spelling>(SpellingNotCalculated)";
+ }
+ OS << ")";
+ if (Header) {
+ OS << ";\n";
+ return;
+ }
+
+ OS << " {\n";
+ OS << " AttributeCommonInfo I(Range, ";
if (ParsedAttrSpellingItr != std::end(AttrMap))
OS << "AT_" << ParsedAttrSpellingItr->first;
@@ -2365,7 +2433,7 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
if (!ElideSpelling)
OS << ", S";
OS << ");\n";
- OS << " return Create";
+ OS << " return Create";
if (Implicit)
OS << "Implicit";
OS << "(Ctx";
@@ -2375,7 +2443,7 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
ai->writeImplicitCtorArgs(OS);
}
OS << ", I);\n";
- OS << " }\n";
+ OS << "}\n\n";
};
auto emitCreates = [&](bool emitFake) {
@@ -2385,6 +2453,9 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
emitCreateNoCI(false, emitFake);
};
+ if (Header)
+ OS << " // Factory methods\n";
+
// Emit a CreateImplicit that takes all the arguments.
emitCreates(true);
@@ -2399,7 +2470,11 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
if (arg->isOptional()) return emitOpt;
return true;
};
- OS << " " << R.getName()
+ if (Header)
+ OS << " ";
+ else
+ OS << R.getName() << "Attr::";
+ OS << R.getName()
<< "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo";
OS << '\n';
for (auto const &ai : Args) {
@@ -2409,8 +2484,12 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
OS << "\n";
}
- OS << " )\n";
- OS << " : " << SuperName << "(Ctx, CommonInfo, ";
+ OS << " )";
+ if (Header) {
+ OS << ";\n";
+ return;
+ }
+ OS << "\n : " << SuperName << "(Ctx, CommonInfo, ";
OS << "attr::" << R.getName() << ", "
<< (R.getValueAsBit("LateParsed") ? "true" : "false");
if (Inheritable) {
@@ -2431,14 +2510,17 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
}
OS << " {\n";
-
+
for (auto const &ai : Args) {
if (!shouldEmitArg(ai)) continue;
ai->writeCtorBody(OS);
}
- OS << " }\n\n";
+ OS << "}\n\n";
};
+ if (Header)
+ OS << "\n // Constructors\n";
+
// Emit a constructor that includes all the arguments.
// This is necessary for cloning.
emitCtor(true, true);
@@ -2446,48 +2528,89 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
// Emit a constructor that takes all the non-fake arguments.
if (HasFakeArg)
emitCtor(true, false);
-
+
// Emit a constructor that takes all the non-fake, non-optional arguments.
if (HasOptArg)
emitCtor(false, false);
- OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n";
- OS << " void printPretty(raw_ostream &OS,\n"
- << " const PrintingPolicy &Policy) const;\n";
- OS << " const char *getSpelling() const;\n";
-
+ if (Header) {
+ OS << '\n';
+ OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n";
+ OS << " void printPretty(raw_ostream &OS,\n"
+ << " const PrintingPolicy &Policy) const;\n";
+ OS << " const char *getSpelling() const;\n";
+ }
+
if (!ElideSpelling) {
assert(!SemanticToSyntacticMap.empty() && "Empty semantic mapping list");
- OS << " Spelling getSemanticSpelling() const {\n";
- WriteSemanticSpellingSwitch("getAttributeSpellingListIndex()",
- SemanticToSyntacticMap, OS);
- OS << " }\n";
+ if (Header)
+ OS << " Spelling getSemanticSpelling() const;\n";
+ else {
+ OS << R.getName() << "Attr::Spelling " << R.getName()
+ << "Attr::getSemanticSpelling() const {\n";
+ WriteSemanticSpellingSwitch("getAttributeSpellingListIndex()",
+ SemanticToSyntacticMap, OS);
+ OS << "}\n";
+ }
}
- writeAttrAccessorDefinition(R, OS);
+ if (Header)
+ writeAttrAccessorDefinition(R, OS);
for (auto const &ai : Args) {
- ai->writeAccessors(OS);
+ if (Header) {
+ ai->writeAccessors(OS);
+ } else {
+ ai->writeAccessorDefinitions(OS);
+ }
OS << "\n\n";
// Don't write conversion routines for fake arguments.
if (ai->isFake()) continue;
if (ai->isEnumArg())
- static_cast<const EnumArgument *>(ai.get())->writeConversion(OS);
+ static_cast<const EnumArgument *>(ai.get())->writeConversion(OS,
+ Header);
else if (ai->isVariadicEnumArg())
- static_cast<const VariadicEnumArgument *>(ai.get())
- ->writeConversion(OS);
+ static_cast<const VariadicEnumArgument *>(ai.get())->writeConversion(
+ OS, Header);
}
- OS << R.getValueAsString("AdditionalMembers");
- OS << "\n\n";
+ if (Header) {
+ OS << R.getValueAsString("AdditionalMembers");
+ OS << "\n\n";
- OS << " static bool classof(const Attr *A) { return A->getKind() == "
- << "attr::" << R.getName() << "; }\n";
+ OS << " static bool classof(const Attr *A) { return A->getKind() == "
+ << "attr::" << R.getName() << "; }\n";
- OS << "};\n\n";
+ OS << "};\n\n";
+ } else {
+ OS << R.getName() << "Attr *" << R.getName()
+ << "Attr::clone(ASTContext &C) const {\n";
+ OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this";
+ for (auto const &ai : Args) {
+ OS << ", ";
+ ai->writeCloneArgs(OS);
+ }
+ OS << ");\n";
+ OS << " A->Inherited = Inherited;\n";
+ OS << " A->IsPackExpansion = IsPackExpansion;\n";
+ OS << " A->setImplicit(Implicit);\n";
+ OS << " return A;\n}\n\n";
+
+ writePrettyPrintFunction(R, Args, OS);
+ writeGetSpellingFunction(R, OS);
+ }
}
+}
+// Emits the class definitions for attributes.
+void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
+ emitSourceFileHeader("Attribute classes' definitions", OS);
+
+ OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n";
+ OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n";
+
+ emitAttributes(Records, OS, true);
OS << "#endif // LLVM_CLANG_ATTR_CLASSES_INC\n";
}
@@ -2496,38 +2619,9 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("Attribute classes' member function definitions", OS);
- std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
-
- for (auto *Attr : Attrs) {
- Record &R = *Attr;
-
- if (!R.getValueAsBit("ASTNode"))
- continue;
-
- std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
- std::vector<std::unique_ptr<Argument>> Args;
- for (const auto *Arg : ArgRecords)
- Args.emplace_back(createArgument(*Arg, R.getName()));
-
- for (auto const &ai : Args)
- ai->writeAccessorDefinitions(OS);
-
- OS << R.getName() << "Attr *" << R.getName()
- << "Attr::clone(ASTContext &C) const {\n";
- OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this";
- for (auto const &ai : Args) {
- OS << ", ";
- ai->writeCloneArgs(OS);
- }
- OS << ");\n";
- OS << " A->Inherited = Inherited;\n";
- OS << " A->IsPackExpansion = IsPackExpansion;\n";
- OS << " A->setImplicit(Implicit);\n";
- OS << " return A;\n}\n\n";
+ emitAttributes(Records, OS, false);
- writePrettyPrintFunction(R, Args, OS);
- writeGetSpellingFunction(R, OS);
- }
+ std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
// Instead of relying on virtual dispatch we just create a huge dispatch
// switch. This is both smaller and faster than virtual functions.
@@ -2825,6 +2919,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
if (R.isSubClassOf(InhClass))
OS << " bool isInherited = Record.readInt();\n";
OS << " bool isImplicit = Record.readInt();\n";
+ OS << " bool isPackExpansion = Record.readInt();\n";
ArgRecords = R.getValueAsListOfDefs("Args");
Args.clear();
for (const auto *Arg : ArgRecords) {
@@ -2840,6 +2935,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
if (R.isSubClassOf(InhClass))
OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n";
OS << " New->setImplicit(isImplicit);\n";
+ OS << " New->setPackExpansion(isPackExpansion);\n";
OS << " break;\n";
OS << " }\n";
}
@@ -2866,6 +2962,7 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
if (R.isSubClassOf(InhClass))
OS << " Record.push_back(SA->isInherited());\n";
OS << " Record.push_back(A->isImplicit());\n";
+ OS << " Record.push_back(A->isPackExpansion());\n";
for (const auto *Arg : Args)
createArgument(*Arg, R.getName())->writePCHWrite(OS);
@@ -2965,7 +3062,7 @@ static void GenerateHasAttrSpellingStringSwitch(
// them. If the attribute has no scope, the version information must not
// have the default value (1), as that's incorrect. Instead, the unscoped
// attribute version information should be taken from the SD-6 standing
- // document, which can be found at:
+ // document, which can be found at:
// https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
int Version = 1;
@@ -3257,7 +3354,7 @@ void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) {
OS << "#ifndef PARSED_ATTR\n";
OS << "#define PARSED_ATTR(NAME) NAME\n";
OS << "#endif\n\n";
-
+
ParsedAttrMap Names = getParsedAttrList(Records);
for (const auto &I : Names) {
OS << "PARSED_ATTR(" << I.first << ")\n";
@@ -3287,18 +3384,12 @@ static void emitArgInfo(const Record &R, raw_ostream &OS) {
// If there is a variadic argument, we will set the optional argument count
// to its largest value. Since it's currently a 4-bit number, we set it to 15.
- OS << ArgCount << ", " << (HasVariadic ? 15 : OptCount);
-}
-
-static void GenerateDefaultAppertainsTo(raw_ostream &OS) {
- OS << "static bool defaultAppertainsTo(Sema &, const ParsedAttr &,";
- OS << "const Decl *) {\n";
- OS << " return true;\n";
- OS << "}\n\n";
+ OS << " NumArgs = " << ArgCount << ";\n";
+ OS << " OptArgs = " << (HasVariadic ? 15 : OptCount) << ";\n";
}
static std::string GetDiagnosticSpelling(const Record &R) {
- std::string Ret = R.getValueAsString("DiagSpelling");
+ std::string Ret = std::string(R.getValueAsString("DiagSpelling"));
if (!Ret.empty())
return Ret;
@@ -3334,7 +3425,7 @@ static std::string CalculateDiagnostic(const Record &S) {
SmallVector<StringRef, 2> Frags;
llvm::SplitString(V, Frags, ",");
for (auto Str : Frags) {
- DiagList.push_back(Str.trim());
+ DiagList.push_back(std::string(Str.trim()));
}
}
}
@@ -3365,7 +3456,7 @@ static std::string CalculateDiagnostic(const Record &S) {
}
static std::string GetSubjectWithSuffix(const Record *R) {
- const std::string &B = R->getName();
+ const std::string &B = std::string(R->getName());
if (B == "DeclBase")
return "Decl";
return B + "Decl";
@@ -3375,16 +3466,14 @@ static std::string functionNameForCustomAppertainsTo(const Record &Subject) {
return "is" + Subject.getName().str();
}
-static std::string GenerateCustomAppertainsTo(const Record &Subject,
- raw_ostream &OS) {
+static void GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) {
std::string FnName = functionNameForCustomAppertainsTo(Subject);
- // If this code has already been generated, simply return the previous
- // instance of it.
+ // If this code has already been generated, we don't need to do anything.
static std::set<std::string> CustomSubjectSet;
auto I = CustomSubjectSet.find(FnName);
if (I != CustomSubjectSet.end())
- return *I;
+ return;
// This only works with non-root Decls.
Record *Base = Subject.getValueAsDef(BaseFieldName);
@@ -3393,7 +3482,7 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject,
if (Base->isSubClassOf("SubsetSubject")) {
PrintFatalError(Subject.getLoc(),
"SubsetSubjects within SubsetSubjects is not supported");
- return "";
+ return;
}
OS << "static bool " << FnName << "(const Decl *D) {\n";
@@ -3405,14 +3494,13 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject,
OS << "}\n\n";
CustomSubjectSet.insert(FnName);
- return FnName;
}
-static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
+static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
// If the attribute does not contain a Subjects definition, then use the
// default appertainsTo logic.
if (Attr.isValueUnset("Subjects"))
- return "defaultAppertainsTo";
+ return;
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
std::vector<Record*> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
@@ -3420,52 +3508,46 @@ static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
// If the list of subjects is empty, it is assumed that the attribute
// appertains to everything.
if (Subjects.empty())
- return "defaultAppertainsTo";
+ return;
bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn");
// Otherwise, generate an appertainsTo check specific to this attribute which
- // checks all of the given subjects against the Decl passed in. Return the
- // name of that check to the caller.
+ // checks all of the given subjects against the Decl passed in.
//
// If D is null, that means the attribute was not applied to a declaration
// at all (for instance because it was applied to a type), or that the caller
// has determined that the check should fail (perhaps prior to the creation
// of the declaration).
- std::string FnName = "check" + Attr.getName().str() + "AppertainsTo";
- std::stringstream SS;
- SS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr, ";
- SS << "const Decl *D) {\n";
- SS << " if (!D || (";
+ OS << "bool diagAppertainsToDecl(Sema &S, ";
+ OS << "const ParsedAttr &Attr, const Decl *D) const override {\n";
+ OS << " if (";
for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) {
- // If the subject has custom code associated with it, generate a function
- // for it. The function cannot be inlined into this check (yet) because it
- // requires the subject to be of a specific type, and were that information
- // inlined here, it would not support an attribute with multiple custom
- // subjects.
+ // If the subject has custom code associated with it, use the generated
+ // function for it. The function cannot be inlined into this check (yet)
+ // because it requires the subject to be of a specific type, and were that
+ // information inlined here, it would not support an attribute with multiple
+ // custom subjects.
if ((*I)->isSubClassOf("SubsetSubject")) {
- SS << "!" << GenerateCustomAppertainsTo(**I, OS) << "(D)";
+ OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)";
} else {
- SS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)";
+ OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)";
}
if (I + 1 != E)
- SS << " && ";
+ OS << " && ";
}
- SS << ")) {\n";
- SS << " S.Diag(Attr.getLoc(), diag::";
- SS << (Warn ? "warn_attribute_wrong_decl_type_str" :
+ OS << ") {\n";
+ OS << " S.Diag(Attr.getLoc(), diag::";
+ OS << (Warn ? "warn_attribute_wrong_decl_type_str" :
"err_attribute_wrong_decl_type_str");
- SS << ")\n";
- SS << " << Attr << ";
- SS << CalculateDiagnostic(*SubjectObj) << ";\n";
- SS << " return false;\n";
- SS << " }\n";
- SS << " return true;\n";
- SS << "}\n\n";
-
- OS << SS.str();
- return FnName;
+ OS << ")\n";
+ OS << " << Attr << ";
+ OS << CalculateDiagnostic(*SubjectObj) << ";\n";
+ OS << " return false;\n";
+ OS << " }\n";
+ OS << " return true;\n";
+ OS << "}\n\n";
}
static void
@@ -3504,37 +3586,16 @@ emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport,
OS << "}\n\n";
}
-static void GenerateDefaultLangOptRequirements(raw_ostream &OS) {
- OS << "static bool defaultDiagnoseLangOpts(Sema &, ";
- OS << "const ParsedAttr &) {\n";
- OS << " return true;\n";
- OS << "}\n\n";
-}
-
-static std::string GenerateLangOptRequirements(const Record &R,
- raw_ostream &OS) {
+static void GenerateLangOptRequirements(const Record &R,
+ raw_ostream &OS) {
// If the attribute has an empty or unset list of language requirements,
- // return the default handler.
+ // use the default handler.
std::vector<Record *> LangOpts = R.getValueAsListOfDefs("LangOpts");
if (LangOpts.empty())
- return "defaultDiagnoseLangOpts";
-
- // Generate a unique function name for the diagnostic test. The list of
- // options should usually be short (one or two options), and the
- // uniqueness isn't strictly necessary (it is just for codegen efficiency).
- std::string FnName = "check";
- for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I)
- FnName += (*I)->getValueAsString("Name");
- FnName += "LangOpts";
-
- // If this code has already been generated, simply return the previous
- // instance of it.
- static std::set<std::string> CustomLangOptsSet;
- auto I = CustomLangOptsSet.find(FnName);
- if (I != CustomLangOptsSet.end())
- return *I;
-
- OS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr) {\n";
+ return;
+
+ OS << "bool diagLangOpts(Sema &S, const ParsedAttr &Attr) ";
+ OS << "const override {\n";
OS << " auto &LangOpts = S.LangOpts;\n";
OS << " if (" << GenerateTestExpression(LangOpts) << ")\n";
OS << " return true;\n\n";
@@ -3542,24 +3603,15 @@ static std::string GenerateLangOptRequirements(const Record &R,
OS << "<< Attr;\n";
OS << " return false;\n";
OS << "}\n\n";
-
- CustomLangOptsSet.insert(FnName);
- return FnName;
-}
-
-static void GenerateDefaultTargetRequirements(raw_ostream &OS) {
- OS << "static bool defaultTargetRequirements(const TargetInfo &) {\n";
- OS << " return true;\n";
- OS << "}\n\n";
}
-static std::string GenerateTargetRequirements(const Record &Attr,
- const ParsedAttrMap &Dupes,
- raw_ostream &OS) {
- // If the attribute is not a target specific attribute, return the default
+static void GenerateTargetRequirements(const Record &Attr,
+ const ParsedAttrMap &Dupes,
+ raw_ostream &OS) {
+ // If the attribute is not a target specific attribute, use the default
// target handler.
if (!Attr.isSubClassOf("TargetSpecificAttr"))
- return "defaultTargetRequirements";
+ return;
// Get the list of architectures to be tested for.
const Record *R = Attr.getValueAsDef("Target");
@@ -3587,55 +3639,51 @@ static std::string GenerateTargetRequirements(const Record &Attr,
std::string Test;
bool UsesT = GenerateTargetSpecificAttrChecks(R, Arches, Test, &FnName);
- // If this code has already been generated, simply return the previous
- // instance of it.
- static std::set<std::string> CustomTargetSet;
- auto I = CustomTargetSet.find(FnName);
- if (I != CustomTargetSet.end())
- return *I;
-
- OS << "static bool " << FnName << "(const TargetInfo &Target) {\n";
+ OS << "bool existsInTarget(const TargetInfo &Target) const override {\n";
if (UsesT)
OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n";
OS << " return " << Test << ";\n";
OS << "}\n\n";
-
- CustomTargetSet.insert(FnName);
- return FnName;
-}
-
-static void GenerateDefaultSpellingIndexToSemanticSpelling(raw_ostream &OS) {
- OS << "static unsigned defaultSpellingIndexToSemanticSpelling("
- << "const ParsedAttr &Attr) {\n";
- OS << " return UINT_MAX;\n";
- OS << "}\n\n";
}
-static std::string GenerateSpellingIndexToSemanticSpelling(const Record &Attr,
- raw_ostream &OS) {
+static void GenerateSpellingIndexToSemanticSpelling(const Record &Attr,
+ raw_ostream &OS) {
// If the attribute does not have a semantic form, we can bail out early.
if (!Attr.getValueAsBit("ASTNode"))
- return "defaultSpellingIndexToSemanticSpelling";
+ return;
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
// If there are zero or one spellings, or all of the spellings share the same
// name, we can also bail out early.
if (Spellings.size() <= 1 || SpellingNamesAreCommon(Spellings))
- return "defaultSpellingIndexToSemanticSpelling";
+ return;
// Generate the enumeration we will use for the mapping.
SemanticSpellingMap SemanticToSyntacticMap;
std::string Enum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
std::string Name = Attr.getName().str() + "AttrSpellingMap";
- OS << "static unsigned " << Name << "(const ParsedAttr &Attr) {\n";
+ OS << "unsigned spellingIndexToSemanticSpelling(";
+ OS << "const ParsedAttr &Attr) const override {\n";
OS << Enum;
OS << " unsigned Idx = Attr.getAttributeSpellingListIndex();\n";
WriteSemanticSpellingSwitch("Idx", SemanticToSyntacticMap, OS);
OS << "}\n\n";
+}
+
+static void GenerateHandleDeclAttribute(const Record &Attr, raw_ostream &OS) {
+ // Only generate if Attr can be handled simply.
+ if (!Attr.getValueAsBit("SimpleHandler"))
+ return;
- return Name;
+ // Generate a function which just converts from ParsedAttr to the Attr type.
+ OS << "AttrHandling handleDeclAttribute(Sema &S, Decl *D,";
+ OS << "const ParsedAttr &Attr) const override {\n";
+ OS << " D->addAttr(::new (S.Context) " << Attr.getName();
+ OS << "Attr(S.Context, Attr));\n";
+ OS << " return AttributeApplied;\n";
+ OS << "}\n\n";
}
static bool IsKnownToGCC(const Record &Attr) {
@@ -3658,19 +3706,19 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
ParsedAttrMap Dupes;
ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes);
- // Generate the default appertainsTo, target and language option diagnostic,
- // and spelling list index mapping methods.
- GenerateDefaultAppertainsTo(OS);
- GenerateDefaultLangOptRequirements(OS);
- GenerateDefaultTargetRequirements(OS);
- GenerateDefaultSpellingIndexToSemanticSpelling(OS);
-
- // Generate the appertainsTo diagnostic methods and write their names into
- // another mapping. At the same time, generate the AttrInfoMap object
- // contents. Due to the reliance on generated code, use separate streams so
- // that code will not be interleaved.
- std::string Buffer;
- raw_string_ostream SS {Buffer};
+ // Generate all of the custom appertainsTo functions that the attributes
+ // will be using.
+ for (auto I : Attrs) {
+ const Record &Attr = *I.second;
+ if (Attr.isValueUnset("Subjects"))
+ continue;
+ const Record *SubjectObj = Attr.getValueAsDef("Subjects");
+ for (auto Subject : SubjectObj->getValueAsListOfDefs("Subjects"))
+ if (Subject->isSubClassOf("SubsetSubject"))
+ GenerateCustomAppertainsTo(*Subject, OS);
+ }
+
+ // Generate a ParsedAttrInfo struct for each of the attributes.
for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
// TODO: If the attribute's kind appears in the list of duplicates, that is
// because it is a target-specific attribute that appears multiple times.
@@ -3680,33 +3728,63 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
// We need to generate struct instances based off ParsedAttrInfo from
// ParsedAttr.cpp.
- SS << " { ";
- emitArgInfo(*I->second, SS);
- SS << ", " << I->second->getValueAsBit("HasCustomParsing");
- SS << ", " << I->second->isSubClassOf("TargetSpecificAttr");
- SS << ", "
- << (I->second->isSubClassOf("TypeAttr") ||
- I->second->isSubClassOf("DeclOrTypeAttr"));
- SS << ", " << I->second->isSubClassOf("StmtAttr");
- SS << ", " << IsKnownToGCC(*I->second);
- SS << ", " << PragmaAttributeSupport.isAttributedSupported(*I->second);
- SS << ", " << GenerateAppertainsTo(*I->second, OS);
- SS << ", " << GenerateLangOptRequirements(*I->second, OS);
- SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS);
- SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS);
- SS << ", "
- << PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS);
- SS << " }";
-
- if (I + 1 != E)
- SS << ",";
-
- SS << " // AT_" << I->first << "\n";
+ const std::string &AttrName = I->first;
+ const Record &Attr = *I->second;
+ auto Spellings = GetFlattenedSpellings(Attr);
+ if (!Spellings.empty()) {
+ OS << "static constexpr ParsedAttrInfo::Spelling " << I->first
+ << "Spellings[] = {\n";
+ for (const auto &S : Spellings) {
+ const std::string &RawSpelling = S.name();
+ std::string Spelling;
+ if (!S.nameSpace().empty())
+ Spelling += S.nameSpace() + "::";
+ if (S.variety() == "GNU")
+ Spelling += NormalizeGNUAttrSpelling(RawSpelling);
+ else
+ Spelling += RawSpelling;
+ OS << " {AttributeCommonInfo::AS_" << S.variety();
+ OS << ", \"" << Spelling << "\"},\n";
+ }
+ OS << "};\n";
+ }
+ OS << "struct ParsedAttrInfo" << I->first
+ << " final : public ParsedAttrInfo {\n";
+ OS << " ParsedAttrInfo" << I->first << "() {\n";
+ OS << " AttrKind = ParsedAttr::AT_" << AttrName << ";\n";
+ emitArgInfo(Attr, OS);
+ OS << " HasCustomParsing = ";
+ OS << Attr.getValueAsBit("HasCustomParsing") << ";\n";
+ OS << " IsTargetSpecific = ";
+ OS << Attr.isSubClassOf("TargetSpecificAttr") << ";\n";
+ OS << " IsType = ";
+ OS << (Attr.isSubClassOf("TypeAttr") ||
+ Attr.isSubClassOf("DeclOrTypeAttr")) << ";\n";
+ OS << " IsStmt = ";
+ OS << Attr.isSubClassOf("StmtAttr") << ";\n";
+ OS << " IsKnownToGCC = ";
+ OS << IsKnownToGCC(Attr) << ";\n";
+ OS << " IsSupportedByPragmaAttribute = ";
+ OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n";
+ if (!Spellings.empty())
+ OS << " Spellings = " << I->first << "Spellings;\n";
+ OS << " }\n";
+ GenerateAppertainsTo(Attr, OS);
+ GenerateLangOptRequirements(Attr, OS);
+ GenerateTargetRequirements(Attr, Dupes, OS);
+ GenerateSpellingIndexToSemanticSpelling(Attr, OS);
+ PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS);
+ GenerateHandleDeclAttribute(Attr, OS);
+ OS << "static const ParsedAttrInfo" << I->first << " Instance;\n";
+ OS << "};\n";
+ OS << "const ParsedAttrInfo" << I->first << " ParsedAttrInfo" << I->first
+ << "::Instance;\n";
}
- OS << "static const ParsedAttrInfo AttrInfoMap[ParsedAttr::UnknownAttribute "
- "+ 1] = {\n";
- OS << SS.str();
+ OS << "static const ParsedAttrInfo *AttrInfoMap[] = {\n";
+ for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
+ OS << "&ParsedAttrInfo" << I->first << "::Instance,\n";
+ }
OS << "};\n\n";
// Generate the attribute match rules.
@@ -3740,7 +3818,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
std::string AttrName;
if (Attr.isSubClassOf("TargetSpecificAttr") &&
!Attr.isValueUnset("ParseKind")) {
- AttrName = Attr.getValueAsString("ParseKind");
+ AttrName = std::string(Attr.getValueAsString("ParseKind"));
if (Seen.find(AttrName) != Seen.end())
continue;
Seen.insert(AttrName);
@@ -3755,12 +3833,12 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
const std::string &Variety = S.variety();
if (Variety == "CXX11") {
Matches = &CXX11;
- Spelling += S.nameSpace();
- Spelling += "::";
+ if (!S.nameSpace().empty())
+ Spelling += S.nameSpace() + "::";
} else if (Variety == "C2x") {
Matches = &C2x;
- Spelling += S.nameSpace();
- Spelling += "::";
+ if (!S.nameSpace().empty())
+ Spelling += S.nameSpace() + "::";
} else if (Variety == "GNU")
Matches = &GNU;
else if (Variety == "Declspec")
@@ -3980,7 +4058,7 @@ GetAttributeHeadingAndSpellings(const Record &Documentation,
"documented");
// Determine the heading to be used for this attribute.
- std::string Heading = Documentation.getValueAsString("Heading");
+ std::string Heading = std::string(Documentation.getValueAsString("Heading"));
if (Heading.empty()) {
// If there's only one spelling, we can simply use that.
if (Spellings.size() == 1)
@@ -3989,7 +4067,8 @@ GetAttributeHeadingAndSpellings(const Record &Documentation,
std::set<std::string> Uniques;
for (auto I = Spellings.begin(), E = Spellings.end();
I != E && Uniques.size() <= 1; ++I) {
- std::string Spelling = NormalizeNameForSpellingComparison(I->name());
+ std::string Spelling =
+ std::string(NormalizeNameForSpellingComparison(I->name()));
Uniques.insert(Spelling);
}
// If the semantic map has only one spelling, that is sufficient for our
diff --git a/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp
index fc79d59713d6..eb2f23191c55 100644
--- a/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp
+++ b/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp
@@ -63,7 +63,7 @@ void clang::EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS)
std::vector<StringMatcher::StringPair> Matches;
for (size_t i = 0, e = Tags.size(); i != e; ++i) {
Record &Tag = *Tags[i];
- std::string Name = Tag.getValueAsString("Name");
+ std::string Name = std::string(Tag.getValueAsString("Name"));
std::string Return;
raw_string_ostream(Return) << "return &Commands[" << i << "];";
Matches.emplace_back(std::move(Name), std::move(Return));
diff --git a/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp b/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
index ed3f4bd6ef6c..15671a99a3fc 100644
--- a/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
+++ b/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
@@ -54,7 +54,7 @@ void clang::EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records,
for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end();
I != E; ++I) {
Record &Tag = **I;
- std::string Spelling = Tag.getValueAsString("Spelling");
+ std::string Spelling = std::string(Tag.getValueAsString("Spelling"));
uint64_t CodePoint = Tag.getValueAsInt("CodePoint");
CLiteral.clear();
CLiteral.append("return ");
@@ -66,7 +66,7 @@ void clang::EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records,
}
CLiteral.append(";");
- StringMatcher::StringPair Match(Spelling, CLiteral.str());
+ StringMatcher::StringPair Match(Spelling, std::string(CLiteral.str()));
NameToUTF8.push_back(Match);
}
diff --git a/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp b/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp
index 7b9fdfcb3f20..78bbbd1cba57 100644
--- a/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp
+++ b/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp
@@ -23,7 +23,8 @@ void clang::EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) {
std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag");
std::vector<StringMatcher::StringPair> Matches;
for (Record *Tag : Tags) {
- Matches.emplace_back(Tag->getValueAsString("Spelling"), "return true;");
+ Matches.emplace_back(std::string(Tag->getValueAsString("Spelling")),
+ "return true;");
}
emitSourceFileHeader("HTML tag name matcher", OS);
@@ -40,7 +41,7 @@ void clang::EmitClangCommentHTMLTagsProperties(RecordKeeper &Records,
std::vector<StringMatcher::StringPair> MatchesEndTagOptional;
std::vector<StringMatcher::StringPair> MatchesEndTagForbidden;
for (Record *Tag : Tags) {
- std::string Spelling = Tag->getValueAsString("Spelling");
+ std::string Spelling = std::string(Tag->getValueAsString("Spelling"));
StringMatcher::StringPair Match(Spelling, "return true;");
if (Tag->getValueAsBit("EndTagOptional"))
MatchesEndTagOptional.push_back(Match);
diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
index f694c3e4380a..76d412203009 100644
--- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
#include "llvm/TableGen/Error.h"
@@ -62,7 +63,7 @@ static std::string
getCategoryFromDiagGroup(const Record *Group,
DiagGroupParentMap &DiagGroupParents) {
// If the DiagGroup has a category, return it.
- std::string CatName = Group->getValueAsString("CategoryName");
+ std::string CatName = std::string(Group->getValueAsString("CategoryName"));
if (!CatName.empty()) return CatName;
// The diag group may the subgroup of one or more other diagnostic groups,
@@ -88,7 +89,7 @@ static std::string getDiagnosticCategory(const Record *R,
}
// If the diagnostic itself has a category, get it.
- return R->getValueAsString("CategoryName");
+ return std::string(R->getValueAsString("CategoryName"));
}
namespace {
@@ -168,7 +169,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
continue;
assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
"Note can't be in a DiagGroup");
- std::string GroupName = DI->getDef()->getValueAsString("GroupName");
+ std::string GroupName =
+ std::string(DI->getDef()->getValueAsString("GroupName"));
DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
}
@@ -179,7 +181,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
// groups (these are warnings that GCC supports that clang never produces).
for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
Record *Group = DiagGroups[i];
- GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
+ GroupInfo &GI =
+ DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
if (Group->isAnonymous()) {
if (GI.DiagsInGroup.size() > 1)
ImplicitGroups.insert(&GI);
@@ -192,7 +195,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
- GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName"));
+ GI.SubGroups.push_back(
+ std::string(SubGroups[j]->getValueAsString("GroupName")));
}
// Assign unique ID numbers to the groups.
@@ -219,7 +223,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
ArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
if ((*I)->ExplicitDef) {
- std::string Name = (*I)->ExplicitDef->getValueAsString("GroupName");
+ std::string Name =
+ std::string((*I)->ExplicitDef->getValueAsString("GroupName"));
for (ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
DE = GroupDiags.end();
DI != DE; ++DI) {
@@ -244,7 +249,8 @@ static void groupDiagnostics(const std::vector<Record*> &Diags,
const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
const Record *NextDiagGroup = GroupInit->getDef();
- std::string Name = NextDiagGroup->getValueAsString("GroupName");
+ std::string Name =
+ std::string(NextDiagGroup->getValueAsString("GroupName"));
SrcMgr.PrintMessage((*DI)->getLoc().front(),
SourceMgr::DK_Error,
@@ -315,8 +321,8 @@ private:
bool InferPedantic::isSubGroupOfGroup(const Record *Group,
llvm::StringRef GName) {
-
- const std::string &GroupName = Group->getValueAsString("GroupName");
+ const std::string &GroupName =
+ std::string(Group->getValueAsString("GroupName"));
if (GName == GroupName)
return true;
@@ -330,13 +336,14 @@ bool InferPedantic::isSubGroupOfGroup(const Record *Group,
/// Determine if the diagnostic is an extension.
bool InferPedantic::isExtension(const Record *Diag) {
- const std::string &ClsName = Diag->getValueAsDef("Class")->getName();
+ const std::string &ClsName =
+ std::string(Diag->getValueAsDef("Class")->getName());
return ClsName == "CLASS_EXTENSION";
}
bool InferPedantic::isOffByDefault(const Record *Diag) {
- const std::string &DefSeverity =
- Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name");
+ const std::string &DefSeverity = std::string(
+ Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
return DefSeverity == "Ignored";
}
@@ -344,7 +351,8 @@ bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
GMap::mapped_type &V = GroupCount[Group];
// Lazily compute the threshold value for the group count.
if (!V.second.hasValue()) {
- const GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
+ const GroupInfo &GI =
+ DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
}
@@ -1176,12 +1184,14 @@ std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
//===----------------------------------------------------------------------===//
static bool isError(const Record &Diag) {
- const std::string &ClsName = Diag.getValueAsDef("Class")->getName();
+ const std::string &ClsName =
+ std::string(Diag.getValueAsDef("Class")->getName());
return ClsName == "CLASS_ERROR";
}
static bool isRemark(const Record &Diag) {
- const std::string &ClsName = Diag.getValueAsDef("Class")->getName();
+ const std::string &ClsName =
+ std::string(Diag.getValueAsDef("Class")->getName());
return ClsName == "CLASS_REMARK";
}
@@ -1226,7 +1236,8 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
if (isError(R)) {
if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
const Record *GroupRec = Group->getDef();
- const std::string &GroupName = GroupRec->getValueAsString("GroupName");
+ const std::string &GroupName =
+ std::string(GroupRec->getValueAsString("GroupName"));
PrintFatalError(R.getLoc(), "Error " + R.getName() +
" cannot be in a warning group [" + GroupName + "]");
}
@@ -1256,8 +1267,8 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
// Warning associated with the diagnostic. This is stored as an index into
// the alphabetically sorted warning table.
if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
- std::map<std::string, GroupInfo>::iterator I =
- DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName"));
+ std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(
+ std::string(DI->getDef()->getValueAsString("GroupName")));
assert(I != DiagsInGroup.end());
OS << ", " << I->second.IDNo;
} else if (DiagsInPedantic.count(&R)) {
@@ -1299,7 +1310,7 @@ static std::string getDiagCategoryEnum(llvm::StringRef name) {
SmallString<256> enumName = llvm::StringRef("DiagCat_");
for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
enumName += isalnum(*I) ? *I : '_';
- return enumName.str();
+ return std::string(enumName.str());
}
/// Emit the array of diagnostic subgroups.
@@ -1335,7 +1346,8 @@ static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
// Emit the groups implicitly in "pedantic".
if (IsPedantic) {
for (auto const &Group : GroupsInPedantic) {
- const std::string &GroupName = Group->getValueAsString("GroupName");
+ const std::string &GroupName =
+ std::string(Group->getValueAsString("GroupName"));
std::map<std::string, GroupInfo>::const_iterator RI =
DiagsInGroup.find(GroupName);
assert(RI != DiagsInGroup.end() && "Referenced without existing?");
@@ -1572,8 +1584,8 @@ namespace {
struct RecordIndexElement
{
RecordIndexElement() {}
- explicit RecordIndexElement(Record const &R):
- Name(R.getName()) {}
+ explicit RecordIndexElement(Record const &R)
+ : Name(std::string(R.getName())) {}
std::string Name;
};
@@ -1614,7 +1626,7 @@ bool isRemarkGroup(const Record *DiagGroup,
bool AnyRemarks = false, AnyNonRemarks = false;
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
- auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
+ auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
for (const Record *Diag : GroupInfo.DiagsInGroup)
(isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
for (const auto &Name : GroupInfo.SubGroups)
@@ -1630,7 +1642,8 @@ bool isRemarkGroup(const Record *DiagGroup,
}
std::string getDefaultSeverity(const Record *Diag) {
- return Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name");
+ return std::string(
+ Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
}
std::set<std::string>
@@ -1639,7 +1652,7 @@ getDefaultSeverities(const Record *DiagGroup,
std::set<std::string> States;
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
- auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
+ auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
for (const Record *Diag : GroupInfo.DiagsInGroup)
States.insert(getDefaultSeverity(Diag));
for (const auto &Name : GroupInfo.SubGroups)
@@ -1714,7 +1727,8 @@ void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
DiagsInPedantic.begin(),
DiagsInPedantic.end());
for (auto *Group : GroupsInPedantic)
- PedDiags.SubGroups.push_back(Group->getValueAsString("GroupName"));
+ PedDiags.SubGroups.push_back(
+ std::string(Group->getValueAsString("GroupName")));
}
// FIXME: Write diagnostic categories and link to diagnostic groups in each.
@@ -1722,7 +1736,8 @@ void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
// Write out the diagnostic groups.
for (const Record *G : DiagGroups) {
bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
- auto &GroupInfo = DiagsInGroup[G->getValueAsString("GroupName")];
+ auto &GroupInfo =
+ DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
GroupInfo.SubGroups.size() == 1;
diff --git a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
index 41d33b550680..7c63cf51ecfa 100644
--- a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
@@ -313,11 +313,11 @@ struct OpenCLTypeStruct {
// Vector size (if applicable; 0 for scalars and generic types).
const unsigned VectorWidth;
// 0 if the type is not a pointer.
- const bool IsPointer;
+ const bool IsPointer : 1;
// 0 if the type is not const.
- const bool IsConst;
+ const bool IsConst : 1;
// 0 if the type is not volatile.
- const bool IsVolatile;
+ const bool IsVolatile : 1;
// Access qualifier.
const OpenCLAccessQual AccessQualifier;
// Address space of the pointer (if applicable).
@@ -333,11 +333,11 @@ struct OpenCLBuiltinStruct {
// index SigTableIndex is the return type.
const unsigned NumTypes;
// Function attribute __attribute__((pure))
- const bool IsPure;
+ const bool IsPure : 1;
// Function attribute __attribute__((const))
- const bool IsConst;
+ const bool IsConst : 1;
// Function attribute __attribute__((convergent))
- const bool IsConv;
+ const bool IsConv : 1;
// OpenCL extension(s) required for this overload.
const unsigned short Extension;
// First OpenCL version in which this overload was introduced (e.g. CL20).
@@ -473,11 +473,18 @@ void BuiltinNameEmitter::EmitSignatureTable() {
// Store a type (e.g. int, float, int2, ...). The type is stored as an index
// of a struct OpenCLType table. Multiple entries following each other form a
// signature.
- OS << "static const unsigned SignatureTable[] = {\n";
+ OS << "static const unsigned short SignatureTable[] = {\n";
for (const auto &P : SignaturesList) {
OS << " // " << P.second << "\n ";
for (const Record *R : P.first) {
- OS << TypeMap.find(R)->second << ", ";
+ unsigned Entry = TypeMap.find(R)->second;
+ if (Entry > USHRT_MAX) {
+ // Report an error when seeing an entry that is too large for the
+ // current index type (unsigned short). When hitting this, the type
+ // of SignatureTable will need to be changed.
+ PrintFatalError("Entry in SignatureTable exceeds limit.");
+ }
+ OS << Entry << ", ";
}
OS << "\n";
}
@@ -553,7 +560,7 @@ void BuiltinNameEmitter::GroupBySignature() {
CurSignatureList->push_back(Signature.second);
}
// Sort the list to facilitate future comparisons.
- std::sort(CurSignatureList->begin(), CurSignatureList->end());
+ llvm::sort(*CurSignatureList);
// Check if we have already seen another function with the same list of
// signatures. If so, just add the name of the function.
@@ -597,7 +604,8 @@ void BuiltinNameEmitter::EmitStringMatcher() {
SS << "return std::make_pair(" << CumulativeIndex << ", " << Ovl.size()
<< ");";
SS.flush();
- ValidBuiltins.push_back(StringMatcher::StringPair(FctName, RetStmt));
+ ValidBuiltins.push_back(
+ StringMatcher::StringPair(std::string(FctName), RetStmt));
}
CumulativeIndex += Ovl.size();
}
diff --git a/clang/utils/TableGen/ClangOptionDocEmitter.cpp b/clang/utils/TableGen/ClangOptionDocEmitter.cpp
index b944ad9608f5..23aa31cc732f 100644
--- a/clang/utils/TableGen/ClangOptionDocEmitter.cpp
+++ b/clang/utils/TableGen/ClangOptionDocEmitter.cpp
@@ -48,7 +48,7 @@ Documentation extractDocumentation(RecordKeeper &Records) {
std::map<std::string, Record*> OptionsByName;
for (Record *R : Records.getAllDerivedDefinitions("Option"))
- OptionsByName[R->getValueAsString("Name")] = R;
+ OptionsByName[std::string(R->getValueAsString("Name"))] = R;
auto Flatten = [](Record *R) {
return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten");
@@ -81,7 +81,7 @@ Documentation extractDocumentation(RecordKeeper &Records) {
}
// Pretend no-X and Xno-Y options are aliases of X and XY.
- std::string Name = R->getValueAsString("Name");
+ std::string Name = std::string(R->getValueAsString("Name"));
if (Name.size() >= 4) {
if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) {
Aliases[OptionsByName[Name.substr(3)]].push_back(R);
@@ -223,7 +223,7 @@ std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
return Field == Primary ? Value.str() : escapeRST(Value);
}
}
- return StringRef();
+ return std::string(StringRef());
}
void emitOptionWithArgs(StringRef Prefix, const Record *Option,
@@ -247,7 +247,7 @@ void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) {
std::vector<std::string> Args;
if (HasMetaVarName)
- Args.push_back(Option->getValueAsString("MetaVarName"));
+ Args.push_back(std::string(Option->getValueAsString("MetaVarName")));
else if (NumArgs == 1)
Args.push_back("<arg>");
@@ -316,8 +316,8 @@ void emitOption(const DocumentedOption &Option, const Record *DocInfo,
std::vector<std::string> SphinxOptionIDs;
forEachOptionName(Option, DocInfo, [&](const Record *Option) {
for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes"))
- SphinxOptionIDs.push_back(
- getSphinxOptionID((Prefix + Option->getValueAsString("Name")).str()));
+ SphinxOptionIDs.push_back(std::string(getSphinxOptionID(
+ (Prefix + Option->getValueAsString("Name")).str())));
});
assert(!SphinxOptionIDs.empty() && "no flags for option");
static std::map<std::string, int> NextSuffix;
diff --git a/clang/utils/TableGen/ClangSACheckersEmitter.cpp b/clang/utils/TableGen/ClangSACheckersEmitter.cpp
index feefbeb41138..00d88274fc38 100644
--- a/clang/utils/TableGen/ClangSACheckersEmitter.cpp
+++ b/clang/utils/TableGen/ClangSACheckersEmitter.cpp
@@ -53,7 +53,7 @@ static std::string getCheckerFullName(const Record *R) {
static std::string getStringValue(const Record &R, StringRef field) {
if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
- return SI->getValue();
+ return std::string(SI->getValue());
return std::string();
}
@@ -282,6 +282,31 @@ void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
OS << "\n"
"#endif // GET_CHECKER_DEPENDENCIES\n";
+ // Emit weak dependencies.
+ //
+ // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
+ // - FULLNAME: The full name of the checker that is supposed to be
+ // registered first.
+ // - DEPENDENCY: The full name of the checker FULLNAME weak depends on.
+ OS << "\n"
+ "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";
+ for (const Record *Checker : checkers) {
+ if (Checker->isValueUnset("WeakDependencies"))
+ continue;
+
+ for (const Record *Dependency :
+ Checker->getValueAsListOfDefs("WeakDependencies")) {
+ OS << "CHECKER_WEAK_DEPENDENCY(";
+ OS << '\"';
+ OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
+ OS << '\"';
+ OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
+ OS << ")\n";
+ }
+ }
+ OS << "\n"
+ "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";
+
// Emit a package option.
//
// CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)
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 = &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
diff --git a/clang/utils/TableGen/NeonEmitter.cpp b/clang/utils/TableGen/NeonEmitter.cpp
index a0f3fb2ddc08..d5bf59ef04ad 100644
--- a/clang/utils/TableGen/NeonEmitter.cpp
+++ b/clang/utils/TableGen/NeonEmitter.cpp
@@ -27,8 +27,9 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
-#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
@@ -98,7 +99,8 @@ enum EltType {
Poly128,
Float16,
Float32,
- Float64
+ Float64,
+ BFloat16
};
} // end namespace NeonTypeFlags
@@ -146,6 +148,7 @@ private:
SInt,
UInt,
Poly,
+ BFloat16,
};
TypeKind Kind;
bool Immediate, Constant, Pointer;
@@ -198,6 +201,7 @@ public:
bool isInt() const { return isInteger() && ElementBitwidth == 32; }
bool isLong() const { return isInteger() && ElementBitwidth == 64; }
bool isVoid() const { return Kind == Void; }
+ bool isBFloat16() const { return Kind == BFloat16; }
unsigned getNumElements() const { return Bitwidth / ElementBitwidth; }
unsigned getSizeInBits() const { return Bitwidth; }
unsigned getElementSizeInBits() const { return ElementBitwidth; }
@@ -238,6 +242,11 @@ public:
NumVectors = 1;
}
+ void make32BitElement() {
+ assert_with_loc(Bitwidth > 32, "Not enough bits to make it 32!");
+ ElementBitwidth = 32;
+ }
+
void doubleLanes() {
assert_with_loc(Bitwidth != 128, "Can't get bigger than 128!");
Bitwidth = 128;
@@ -297,14 +306,12 @@ public:
/// The main grunt class. This represents an instantiation of an intrinsic with
/// a particular typespec and prototype.
class Intrinsic {
- friend class DagEmitter;
-
/// The Record this intrinsic was created from.
Record *R;
/// The unmangled name.
std::string Name;
/// The input and output typespecs. InTS == OutTS except when
- /// CartesianProductOfTypes is 1 - this is the case for vreinterpret.
+ /// CartesianProductWith is non-empty - this is the case for vreinterpret.
TypeSpec OutTS, InTS;
/// The base class kind. Most intrinsics use ClassS, which has full type
/// info for integers (s32/u32). Some use ClassI, which doesn't care about
@@ -337,7 +344,7 @@ class Intrinsic {
/// The set of intrinsics that this intrinsic uses/requires.
std::set<Intrinsic *> Dependencies;
/// The "base type", which is Type('d', OutTS). InBaseType is only
- /// different if CartesianProductOfTypes = 1 (for vreinterpret).
+ /// different if CartesianProductWith is non-empty (for vreinterpret).
Type BaseType, InBaseType;
/// The return variable.
Variable RetVar;
@@ -518,7 +525,8 @@ private:
std::pair<Type, std::string> emitDagDupTyped(DagInit *DI);
std::pair<Type, std::string> emitDagShuffle(DagInit *DI);
std::pair<Type, std::string> emitDagCast(DagInit *DI, bool IsBitCast);
- std::pair<Type, std::string> emitDagCall(DagInit *DI);
+ std::pair<Type, std::string> emitDagCall(DagInit *DI,
+ bool MatchMangledName);
std::pair<Type, std::string> emitDagNameReplace(DagInit *DI);
std::pair<Type, std::string> emitDagLiteral(DagInit *DI);
std::pair<Type, std::string> emitDagOp(DagInit *DI);
@@ -546,7 +554,8 @@ class NeonEmitter {
public:
/// Called by Intrinsic - this attempts to get an intrinsic that takes
/// the given types as arguments.
- Intrinsic &getIntrinsic(StringRef Name, ArrayRef<Type> Types);
+ Intrinsic &getIntrinsic(StringRef Name, ArrayRef<Type> Types,
+ Optional<std::string> MangledName);
/// Called by Intrinsic - returns a globally-unique number.
unsigned getUniqueNumber() { return UniqueNumber++; }
@@ -577,8 +586,11 @@ public:
// runFP16 - Emit arm_fp16.h.inc
void runFP16(raw_ostream &o);
- // runHeader - Emit all the __builtin prototypes used in arm_neon.h
- // and arm_fp16.h
+ // runBF16 - Emit arm_bf16.h.inc
+ void runBF16(raw_ostream &o);
+
+ // runHeader - Emit all the __builtin prototypes used in arm_neon.h,
+ // arm_fp16.h and arm_bf16.h
void runHeader(raw_ostream &o);
// runTests - Emit tests for all the Neon intrinsics.
@@ -603,6 +615,8 @@ std::string Type::str() const {
S += "poly";
else if (isFloating())
S += "float";
+ else if (isBFloat16())
+ S += "bfloat";
else
S += "int";
@@ -642,7 +656,10 @@ std::string Type::builtin_str() const {
case 128: S += "LLLi"; break;
default: llvm_unreachable("Unhandled case!");
}
- else
+ else if (isBFloat16()) {
+ assert(ElementBitwidth == 16 && "BFloat16 can only be 16 bits");
+ S += "y";
+ } else
switch (ElementBitwidth) {
case 16: S += "h"; break;
case 32: S += "f"; break;
@@ -696,6 +713,11 @@ unsigned Type::getNeonEnum() const {
Base = (unsigned)NeonTypeFlags::Float16 + (Addend - 1);
}
+ if (isBFloat16()) {
+ assert(Addend == 1 && "BFloat16 is only 16 bit");
+ Base = (unsigned)NeonTypeFlags::BFloat16;
+ }
+
if (Bitwidth == 128)
Base |= (unsigned)NeonTypeFlags::QuadFlag;
if (isInteger() && !isSigned())
@@ -719,6 +741,9 @@ Type Type::fromTypedefName(StringRef Name) {
} else if (Name.startswith("poly")) {
T.Kind = Poly;
Name = Name.drop_front(4);
+ } else if (Name.startswith("bfloat")) {
+ T.Kind = BFloat16;
+ Name = Name.drop_front(6);
} else {
assert(Name.startswith("int"));
Name = Name.drop_front(3);
@@ -817,6 +842,10 @@ void Type::applyTypespec(bool &Quad) {
if (isPoly())
NumVectors = 0;
break;
+ case 'b':
+ Kind = BFloat16;
+ ElementBitwidth = 16;
+ break;
default:
llvm_unreachable("Unhandled type code!");
}
@@ -843,6 +872,10 @@ void Type::applyModifiers(StringRef Mods) {
case 'U':
Kind = UInt;
break;
+ case 'B':
+ Kind = BFloat16;
+ ElementBitwidth = 16;
+ break;
case 'F':
Kind = Float;
break;
@@ -924,6 +957,9 @@ std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {
if (CK == ClassB)
return "";
+ if (T.isBFloat16())
+ return "bf16";
+
if (T.isPoly())
typeCode = 'p';
else if (T.isInteger())
@@ -961,7 +997,7 @@ std::string Intrinsic::getBuiltinTypeStr() {
Type RetT = getReturnType();
if ((LocalCK == ClassI || LocalCK == ClassW) && RetT.isScalar() &&
- !RetT.isFloating())
+ !RetT.isFloating() && !RetT.isBFloat16())
RetT.makeInteger(RetT.getElementSizeInBits(), false);
// Since the return value must be one type, return a vector type of the
@@ -1026,7 +1062,8 @@ std::string Intrinsic::mangleName(std::string Name, ClassKind LocalCK) const {
std::string S = Name;
if (Name == "vcvt_f16_f32" || Name == "vcvt_f32_f16" ||
- Name == "vcvt_f32_f64" || Name == "vcvt_f64_f32")
+ Name == "vcvt_f32_f64" || Name == "vcvt_f64_f32" ||
+ Name == "vcvt_f32_bf16")
return Name;
if (!typeCode.empty()) {
@@ -1257,7 +1294,7 @@ void Intrinsic::emitBodyAsBuiltinCall() {
if (!getReturnType().isVoid() && !SRet)
S += "(" + RetVar.getType().str() + ") ";
- S += "__builtin_neon_" + mangleName(N, LocalCK) + "(";
+ S += "__builtin_neon_" + mangleName(std::string(N), LocalCK) + "(";
if (SRet)
S += "&" + RetVar.getName() + ", ";
@@ -1383,8 +1420,8 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDag(DagInit *DI) {
return emitDagSaveTemp(DI);
if (Op == "op")
return emitDagOp(DI);
- if (Op == "call")
- return emitDagCall(DI);
+ if (Op == "call" || Op == "call_mangled")
+ return emitDagCall(DI, Op == "call_mangled");
if (Op == "name_replace")
return emitDagNameReplace(DI);
if (Op == "literal")
@@ -1398,25 +1435,26 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagOp(DagInit *DI) {
if (DI->getNumArgs() == 2) {
// Unary op.
std::pair<Type, std::string> R =
- emitDagArg(DI->getArg(1), DI->getArgNameStr(1));
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
return std::make_pair(R.first, Op + R.second);
} else {
assert(DI->getNumArgs() == 3 && "Can only handle unary and binary ops!");
std::pair<Type, std::string> R1 =
- emitDagArg(DI->getArg(1), DI->getArgNameStr(1));
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
std::pair<Type, std::string> R2 =
- emitDagArg(DI->getArg(2), DI->getArgNameStr(2));
+ emitDagArg(DI->getArg(2), std::string(DI->getArgNameStr(2)));
assert_with_loc(R1.first == R2.first, "Argument type mismatch!");
return std::make_pair(R1.first, R1.second + " " + Op + " " + R2.second);
}
}
-std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCall(DagInit *DI) {
+std::pair<Type, std::string>
+Intrinsic::DagEmitter::emitDagCall(DagInit *DI, bool MatchMangledName) {
std::vector<Type> Types;
std::vector<std::string> Values;
for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) {
std::pair<Type, std::string> R =
- emitDagArg(DI->getArg(I + 1), DI->getArgNameStr(I + 1));
+ emitDagArg(DI->getArg(I + 1), std::string(DI->getArgNameStr(I + 1)));
Types.push_back(R.first);
Values.push_back(R.second);
}
@@ -1427,7 +1465,13 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCall(DagInit *DI) {
N = SI->getAsUnquotedString();
else
N = emitDagArg(DI->getArg(0), "").second;
- Intrinsic &Callee = Intr.Emitter.getIntrinsic(N, Types);
+ Optional<std::string> MangledName;
+ if (MatchMangledName) {
+ if (Intr.getRecord()->getValueAsBit("isLaneQ"))
+ N += "q";
+ MangledName = Intr.mangleName(N, ClassS);
+ }
+ Intrinsic &Callee = Intr.Emitter.getIntrinsic(N, Types, MangledName);
// Make sure the callee is known as an early def.
Callee.setNeededEarly();
@@ -1451,9 +1495,9 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCall(DagInit *DI) {
std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCast(DagInit *DI,
bool IsBitCast){
// (cast MOD* VAL) -> cast VAL to type given by MOD.
- std::pair<Type, std::string> R = emitDagArg(
- DI->getArg(DI->getNumArgs() - 1),
- DI->getArgNameStr(DI->getNumArgs() - 1));
+ std::pair<Type, std::string> R =
+ emitDagArg(DI->getArg(DI->getNumArgs() - 1),
+ std::string(DI->getArgNameStr(DI->getNumArgs() - 1)));
Type castToType = R.first;
for (unsigned ArgIdx = 0; ArgIdx < DI->getNumArgs() - 1; ++ArgIdx) {
@@ -1465,10 +1509,11 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCast(DagInit *DI,
// 5. The value "H" or "D" to half or double the bitwidth.
// 6. The value "8" to convert to 8-bit (signed) integer lanes.
if (!DI->getArgNameStr(ArgIdx).empty()) {
- assert_with_loc(Intr.Variables.find(DI->getArgNameStr(ArgIdx)) !=
- Intr.Variables.end(),
+ assert_with_loc(Intr.Variables.find(std::string(
+ DI->getArgNameStr(ArgIdx))) != Intr.Variables.end(),
"Variable not found");
- castToType = Intr.Variables[DI->getArgNameStr(ArgIdx)].getType();
+ castToType =
+ Intr.Variables[std::string(DI->getArgNameStr(ArgIdx))].getType();
} else {
StringInit *SI = dyn_cast<StringInit>(DI->getArg(ArgIdx));
assert_with_loc(SI, "Expected string type or $Name for cast type");
@@ -1485,6 +1530,8 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCast(DagInit *DI,
castToType.doubleLanes();
} else if (SI->getAsUnquotedString() == "8") {
castToType.makeInteger(8, true);
+ } else if (SI->getAsUnquotedString() == "32") {
+ castToType.make32BitElement();
} else {
castToType = Type::fromTypedefName(SI->getAsUnquotedString());
assert_with_loc(!castToType.isVoid(), "Unknown typedef");
@@ -1583,9 +1630,9 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagShuffle(DagInit *DI){
// (shuffle arg1, arg2, sequence)
std::pair<Type, std::string> Arg1 =
- emitDagArg(DI->getArg(0), DI->getArgNameStr(0));
+ emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));
std::pair<Type, std::string> Arg2 =
- emitDagArg(DI->getArg(1), DI->getArgNameStr(1));
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
assert_with_loc(Arg1.first == Arg2.first,
"Different types in arguments to shuffle!");
@@ -1627,8 +1674,8 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagShuffle(DagInit *DI){
std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDup(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 1, "dup() expects one argument");
- std::pair<Type, std::string> A = emitDagArg(DI->getArg(0),
- DI->getArgNameStr(0));
+ std::pair<Type, std::string> A =
+ emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));
assert_with_loc(A.first.isScalar(), "dup() expects a scalar argument");
Type T = Intr.getBaseType();
@@ -1646,10 +1693,10 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDup(DagInit *DI) {
std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDupTyped(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 2, "dup_typed() expects two arguments");
- std::pair<Type, std::string> A = emitDagArg(DI->getArg(0),
- DI->getArgNameStr(0));
- std::pair<Type, std::string> B = emitDagArg(DI->getArg(1),
- DI->getArgNameStr(1));
+ std::pair<Type, std::string> A =
+ emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));
+ std::pair<Type, std::string> B =
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
assert_with_loc(B.first.isScalar(),
"dup_typed() requires a scalar as the second argument");
@@ -1668,10 +1715,10 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDupTyped(DagInit *DI)
std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSplat(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 2, "splat() expects two arguments");
- std::pair<Type, std::string> A = emitDagArg(DI->getArg(0),
- DI->getArgNameStr(0));
- std::pair<Type, std::string> B = emitDagArg(DI->getArg(1),
- DI->getArgNameStr(1));
+ std::pair<Type, std::string> A =
+ emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));
+ std::pair<Type, std::string> B =
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
assert_with_loc(B.first.isScalar(),
"splat() requires a scalar int as the second argument");
@@ -1687,13 +1734,13 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSplat(DagInit *DI) {
std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSaveTemp(DagInit *DI) {
assert_with_loc(DI->getNumArgs() == 2, "save_temp() expects two arguments");
- std::pair<Type, std::string> A = emitDagArg(DI->getArg(1),
- DI->getArgNameStr(1));
+ std::pair<Type, std::string> A =
+ emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));
assert_with_loc(!A.first.isVoid(),
"Argument to save_temp() must have non-void type!");
- std::string N = DI->getArgNameStr(0);
+ std::string N = std::string(DI->getArgNameStr(0));
assert_with_loc(!N.empty(),
"save_temp() expects a name as the first argument");
@@ -1831,7 +1878,8 @@ void Intrinsic::indexBody() {
// NeonEmitter implementation
//===----------------------------------------------------------------------===//
-Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types) {
+Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types,
+ Optional<std::string> MangledName) {
// First, look up the name in the intrinsic map.
assert_with_loc(IntrinsicMap.find(Name.str()) != IntrinsicMap.end(),
("Intrinsic '" + Name + "' not found!").str());
@@ -1860,17 +1908,19 @@ Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types) {
}
ErrMsg += ")\n";
+ if (MangledName && MangledName != I.getMangledName(true))
+ continue;
+
if (I.getNumParams() != Types.size())
continue;
- bool Good = true;
- for (unsigned Arg = 0; Arg < Types.size(); ++Arg) {
- if (I.getParamType(Arg) != Types[Arg]) {
- Good = false;
- break;
- }
- }
- if (Good)
+ unsigned ArgNum = 0;
+ bool MatchingArgumentTypes =
+ std::all_of(Types.begin(), Types.end(), [&](const auto &Type) {
+ return Type == I.getParamType(ArgNum++);
+ });
+
+ if (MatchingArgumentTypes)
GoodVec.push_back(&I);
}
@@ -1883,14 +1933,14 @@ Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types) {
void NeonEmitter::createIntrinsic(Record *R,
SmallVectorImpl<Intrinsic *> &Out) {
- std::string Name = R->getValueAsString("Name");
- std::string Proto = R->getValueAsString("Prototype");
- std::string Types = R->getValueAsString("Types");
+ std::string Name = std::string(R->getValueAsString("Name"));
+ std::string Proto = std::string(R->getValueAsString("Prototype"));
+ std::string Types = std::string(R->getValueAsString("Types"));
Record *OperationRec = R->getValueAsDef("Operation");
- bool CartesianProductOfTypes = R->getValueAsBit("CartesianProductOfTypes");
bool BigEndianSafe = R->getValueAsBit("BigEndianSafe");
- std::string Guard = R->getValueAsString("ArchGuard");
+ std::string Guard = std::string(R->getValueAsString("ArchGuard"));
bool IsUnavailable = OperationRec->getValueAsBit("Unavailable");
+ std::string CartesianProductWith = std::string(R->getValueAsString("CartesianProductWith"));
// Set the global current record. This allows assert_with_loc to produce
// decent location information even when highly nested.
@@ -1905,17 +1955,20 @@ void NeonEmitter::createIntrinsic(Record *R,
CK = ClassMap[R->getSuperClasses()[1].first];
std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs;
- for (auto TS : TypeSpecs) {
- if (CartesianProductOfTypes) {
+ if (!CartesianProductWith.empty()) {
+ std::vector<TypeSpec> ProductTypeSpecs = TypeSpec::fromTypeSpecs(CartesianProductWith);
+ for (auto TS : TypeSpecs) {
Type DefaultT(TS, ".");
- for (auto SrcTS : TypeSpecs) {
+ for (auto SrcTS : ProductTypeSpecs) {
Type DefaultSrcT(SrcTS, ".");
if (TS == SrcTS ||
DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits())
continue;
NewTypeSpecs.push_back(std::make_pair(TS, SrcTS));
}
- } else {
+ }
+ } else {
+ for (auto TS : TypeSpecs) {
NewTypeSpecs.push_back(std::make_pair(TS, TS));
}
}
@@ -2143,6 +2196,74 @@ void NeonEmitter::runHeader(raw_ostream &OS) {
genIntrinsicRangeCheckCode(OS, Defs);
}
+static void emitNeonTypeDefs(const std::string& types, raw_ostream &OS) {
+ std::string TypedefTypes(types);
+ std::vector<TypeSpec> TDTypeVec = TypeSpec::fromTypeSpecs(TypedefTypes);
+
+ // Emit vector typedefs.
+ bool InIfdef = false;
+ for (auto &TS : TDTypeVec) {
+ bool IsA64 = false;
+ Type T(TS, ".");
+ if (T.isDouble())
+ IsA64 = true;
+
+ if (InIfdef && !IsA64) {
+ OS << "#endif\n";
+ InIfdef = false;
+ }
+ if (!InIfdef && IsA64) {
+ OS << "#ifdef __aarch64__\n";
+ InIfdef = true;
+ }
+
+ if (T.isPoly())
+ OS << "typedef __attribute__((neon_polyvector_type(";
+ else
+ OS << "typedef __attribute__((neon_vector_type(";
+
+ Type T2 = T;
+ T2.makeScalar();
+ OS << T.getNumElements() << "))) ";
+ OS << T2.str();
+ OS << " " << T.str() << ";\n";
+ }
+ if (InIfdef)
+ OS << "#endif\n";
+ OS << "\n";
+
+ // Emit struct typedefs.
+ InIfdef = false;
+ for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) {
+ for (auto &TS : TDTypeVec) {
+ bool IsA64 = false;
+ Type T(TS, ".");
+ if (T.isDouble())
+ IsA64 = true;
+
+ if (InIfdef && !IsA64) {
+ OS << "#endif\n";
+ InIfdef = false;
+ }
+ if (!InIfdef && IsA64) {
+ OS << "#ifdef __aarch64__\n";
+ InIfdef = true;
+ }
+
+ const char Mods[] = { static_cast<char>('2' + (NumMembers - 2)), 0};
+ Type VT(TS, Mods);
+ OS << "typedef struct " << VT.str() << " {\n";
+ OS << " " << T.str() << " val";
+ OS << "[" << NumMembers << "]";
+ OS << ";\n} ";
+ OS << VT.str() << ";\n";
+ OS << "\n";
+ }
+ }
+ if (InIfdef)
+ OS << "#endif\n";
+}
+
/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h
/// is comprised of type definitions and function declarations.
void NeonEmitter::run(raw_ostream &OS) {
@@ -2191,12 +2312,22 @@ void NeonEmitter::run(raw_ostream &OS) {
OS << "#ifndef __ARM_NEON_H\n";
OS << "#define __ARM_NEON_H\n\n";
+ OS << "#ifndef __ARM_FP\n";
+ OS << "#error \"NEON intrinsics not available with the soft-float ABI. "
+ "Please use -mfloat-abi=softfp or -mfloat-abi=hard\"\n";
+ OS << "#else\n\n";
+
OS << "#if !defined(__ARM_NEON)\n";
OS << "#error \"NEON support not enabled\"\n";
- OS << "#endif\n\n";
+ OS << "#else\n\n";
OS << "#include <stdint.h>\n\n";
+ OS << "#ifdef __ARM_FEATURE_BF16\n";
+ OS << "#include <arm_bf16.h>\n";
+ OS << "typedef __bf16 bfloat16_t;\n";
+ OS << "#endif\n\n";
+
// Emit NEON-specific scalar typedefs.
OS << "typedef float float32_t;\n";
OS << "typedef __fp16 float16_t;\n";
@@ -2214,76 +2345,14 @@ void NeonEmitter::run(raw_ostream &OS) {
OS << "#else\n";
OS << "typedef int8_t poly8_t;\n";
OS << "typedef int16_t poly16_t;\n";
+ OS << "typedef int64_t poly64_t;\n";
OS << "#endif\n";
- // Emit Neon vector typedefs.
- std::string TypedefTypes(
- "cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQdPcQPcPsQPsPlQPl");
- std::vector<TypeSpec> TDTypeVec = TypeSpec::fromTypeSpecs(TypedefTypes);
-
- // Emit vector typedefs.
- bool InIfdef = false;
- for (auto &TS : TDTypeVec) {
- bool IsA64 = false;
- Type T(TS, ".");
- if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
- IsA64 = true;
-
- if (InIfdef && !IsA64) {
- OS << "#endif\n";
- InIfdef = false;
- }
- if (!InIfdef && IsA64) {
- OS << "#ifdef __aarch64__\n";
- InIfdef = true;
- }
+ emitNeonTypeDefs("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQdPcQPcPsQPsPlQPl", OS);
- if (T.isPoly())
- OS << "typedef __attribute__((neon_polyvector_type(";
- else
- OS << "typedef __attribute__((neon_vector_type(";
-
- Type T2 = T;
- T2.makeScalar();
- OS << T.getNumElements() << "))) ";
- OS << T2.str();
- OS << " " << T.str() << ";\n";
- }
- if (InIfdef)
- OS << "#endif\n";
- OS << "\n";
-
- // Emit struct typedefs.
- InIfdef = false;
- for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) {
- for (auto &TS : TDTypeVec) {
- bool IsA64 = false;
- Type T(TS, ".");
- if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
- IsA64 = true;
-
- if (InIfdef && !IsA64) {
- OS << "#endif\n";
- InIfdef = false;
- }
- if (!InIfdef && IsA64) {
- OS << "#ifdef __aarch64__\n";
- InIfdef = true;
- }
-
- const char Mods[] = { static_cast<char>('2' + (NumMembers - 2)), 0};
- Type VT(TS, Mods);
- OS << "typedef struct " << VT.str() << " {\n";
- OS << " " << T.str() << " val";
- OS << "[" << NumMembers << "]";
- OS << ";\n} ";
- OS << VT.str() << ";\n";
- OS << "\n";
- }
- }
- if (InIfdef)
- OS << "#endif\n";
- OS << "\n";
+ OS << "#ifdef __ARM_FEATURE_BF16\n";
+ emitNeonTypeDefs("bQb", OS);
+ OS << "#endif\n\n";
OS << "#define __ai static __inline__ __attribute__((__always_inline__, "
"__nodebug__))\n\n";
@@ -2340,6 +2409,8 @@ void NeonEmitter::run(raw_ostream &OS) {
OS << "\n";
OS << "#undef __ai\n\n";
+ OS << "#endif /* if !defined(__ARM_NEON) */\n";
+ OS << "#endif /* ifndef __ARM_FP */\n";
OS << "#endif /* __ARM_NEON_H */\n";
}
@@ -2450,6 +2521,84 @@ void NeonEmitter::runFP16(raw_ostream &OS) {
OS << "#endif /* __ARM_FP16_H */\n";
}
+void NeonEmitter::runBF16(raw_ostream &OS) {
+ OS << "/*===---- arm_bf16.h - ARM BF16 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";
+
+ OS << "#ifndef __ARM_BF16_H\n";
+ OS << "#define __ARM_BF16_H\n\n";
+
+ OS << "typedef __bf16 bfloat16_t;\n";
+
+ OS << "#define __ai static __inline__ __attribute__((__always_inline__, "
+ "__nodebug__))\n\n";
+
+ SmallVector<Intrinsic *, 128> Defs;
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ for (auto *I : Defs)
+ I->indexBody();
+
+ llvm::stable_sort(Defs, llvm::deref<std::less<>>());
+
+ // Only emit a def when its requirements have been met.
+ // FIXME: This loop could be made faster, but it's fast enough for now.
+ bool MadeProgress = true;
+ std::string InGuard;
+ while (!Defs.empty() && MadeProgress) {
+ MadeProgress = false;
+
+ for (SmallVector<Intrinsic *, 128>::iterator I = Defs.begin();
+ I != Defs.end(); /*No step*/) {
+ bool DependenciesSatisfied = true;
+ for (auto *II : (*I)->getDependencies()) {
+ if (llvm::is_contained(Defs, II))
+ DependenciesSatisfied = false;
+ }
+ if (!DependenciesSatisfied) {
+ // Try the next one.
+ ++I;
+ continue;
+ }
+
+ // Emit #endif/#if pair if needed.
+ if ((*I)->getGuard() != InGuard) {
+ if (!InGuard.empty())
+ OS << "#endif\n";
+ InGuard = (*I)->getGuard();
+ if (!InGuard.empty())
+ OS << "#if " << InGuard << "\n";
+ }
+
+ // Actually generate the intrinsic code.
+ OS << (*I)->generate();
+
+ MadeProgress = true;
+ I = Defs.erase(I);
+ }
+ }
+ assert(Defs.empty() && "Some requirements were not satisfied!");
+ if (!InGuard.empty())
+ OS << "#endif\n";
+
+ OS << "\n";
+ OS << "#undef __ai\n\n";
+
+ OS << "#endif\n";
+}
+
void clang::EmitNeon(RecordKeeper &Records, raw_ostream &OS) {
NeonEmitter(Records).run(OS);
}
@@ -2458,6 +2607,10 @@ void clang::EmitFP16(RecordKeeper &Records, raw_ostream &OS) {
NeonEmitter(Records).runFP16(OS);
}
+void clang::EmitBF16(RecordKeeper &Records, raw_ostream &OS) {
+ NeonEmitter(Records).runBF16(OS);
+}
+
void clang::EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) {
NeonEmitter(Records).runHeader(OS);
}
diff --git a/clang/utils/TableGen/SveEmitter.cpp b/clang/utils/TableGen/SveEmitter.cpp
new file mode 100644
index 000000000000..1d42edd8a94a
--- /dev/null
+++ b/clang/utils/TableGen/SveEmitter.cpp
@@ -0,0 +1,1436 @@
+//===- SveEmitter.cpp - Generate arm_sve.h for use with clang -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tablegen backend is responsible for emitting arm_sve.h, which includes
+// a declaration and definition of each function specified by the ARM C/C++
+// Language Extensions (ACLE).
+//
+// For details, visit:
+// https://developer.arm.com/architectures/system-architectures/software-standards/acle
+//
+// Each SVE instruction is implemented in terms of 1 or more functions which
+// are suffixed with the element type of the input vectors. Functions may be
+// implemented in terms of generic vector operations such as +, *, -, etc. or
+// by calling a __builtin_-prefixed function which will be handled by clang's
+// CodeGen library.
+//
+// See also the documentation in include/clang/Basic/arm_sve.td.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/Error.h"
+#include <string>
+#include <sstream>
+#include <set>
+#include <cctype>
+#include <tuple>
+
+using namespace llvm;
+
+enum ClassKind {
+ ClassNone,
+ ClassS, // signed/unsigned, e.g., "_s8", "_u8" suffix
+ ClassG, // Overloaded name without type suffix
+};
+
+using TypeSpec = std::string;
+
+namespace {
+
+class ImmCheck {
+ unsigned Arg;
+ unsigned Kind;
+ unsigned ElementSizeInBits;
+
+public:
+ ImmCheck(unsigned Arg, unsigned Kind, unsigned ElementSizeInBits = 0)
+ : Arg(Arg), Kind(Kind), ElementSizeInBits(ElementSizeInBits) {}
+ ImmCheck(const ImmCheck &Other) = default;
+ ~ImmCheck() = default;
+
+ unsigned getArg() const { return Arg; }
+ unsigned getKind() const { return Kind; }
+ unsigned getElementSizeInBits() const { return ElementSizeInBits; }
+};
+
+class SVEType {
+ TypeSpec TS;
+ bool Float, Signed, Immediate, Void, Constant, Pointer, BFloat;
+ bool DefaultType, IsScalable, Predicate, PredicatePattern, PrefetchOp;
+ unsigned Bitwidth, ElementBitwidth, NumVectors;
+
+public:
+ SVEType() : SVEType(TypeSpec(), 'v') {}
+
+ SVEType(TypeSpec TS, char CharMod)
+ : TS(TS), Float(false), Signed(true), Immediate(false), Void(false),
+ Constant(false), Pointer(false), BFloat(false), DefaultType(false),
+ IsScalable(true), Predicate(false), PredicatePattern(false),
+ PrefetchOp(false), Bitwidth(128), ElementBitwidth(~0U), NumVectors(1) {
+ if (!TS.empty())
+ applyTypespec();
+ applyModifier(CharMod);
+ }
+
+ bool isPointer() const { return Pointer; }
+ bool isVoidPointer() const { return Pointer && Void; }
+ bool isSigned() const { return Signed; }
+ bool isImmediate() const { return Immediate; }
+ bool isScalar() const { return NumVectors == 0; }
+ bool isVector() const { return NumVectors > 0; }
+ bool isScalableVector() const { return isVector() && IsScalable; }
+ bool isChar() const { return ElementBitwidth == 8; }
+ bool isVoid() const { return Void & !Pointer; }
+ bool isDefault() const { return DefaultType; }
+ bool isFloat() const { return Float && !BFloat; }
+ bool isBFloat() const { return BFloat && !Float; }
+ bool isFloatingPoint() const { return Float || BFloat; }
+ bool isInteger() const { return !isFloatingPoint() && !Predicate; }
+ bool isScalarPredicate() const {
+ return !isFloatingPoint() && Predicate && NumVectors == 0;
+ }
+ bool isPredicateVector() const { return Predicate; }
+ bool isPredicatePattern() const { return PredicatePattern; }
+ bool isPrefetchOp() const { return PrefetchOp; }
+ bool isConstant() const { return Constant; }
+ unsigned getElementSizeInBits() const { return ElementBitwidth; }
+ unsigned getNumVectors() const { return NumVectors; }
+
+ unsigned getNumElements() const {
+ assert(ElementBitwidth != ~0U);
+ return Bitwidth / ElementBitwidth;
+ }
+ unsigned getSizeInBits() const {
+ return Bitwidth;
+ }
+
+ /// Return the string representation of a type, which is an encoded
+ /// string for passing to the BUILTIN() macro in Builtins.def.
+ std::string builtin_str() const;
+
+ /// Return the C/C++ string representation of a type for use in the
+ /// arm_sve.h header file.
+ std::string str() const;
+
+private:
+ /// Creates the type based on the typespec string in TS.
+ void applyTypespec();
+
+ /// Applies a prototype modifier to the type.
+ void applyModifier(char Mod);
+};
+
+
+class SVEEmitter;
+
+/// The main grunt class. This represents an instantiation of an intrinsic with
+/// a particular typespec and prototype.
+class Intrinsic {
+ /// The unmangled name.
+ std::string Name;
+
+ /// The name of the corresponding LLVM IR intrinsic.
+ std::string LLVMName;
+
+ /// Intrinsic prototype.
+ std::string Proto;
+
+ /// The base type spec for this intrinsic.
+ TypeSpec BaseTypeSpec;
+
+ /// The base class kind. Most intrinsics use ClassS, which has full type
+ /// info for integers (_s32/_u32), or ClassG which is used for overloaded
+ /// intrinsics.
+ ClassKind Class;
+
+ /// The architectural #ifdef guard.
+ std::string Guard;
+
+ // The merge suffix such as _m, _x or _z.
+ std::string MergeSuffix;
+
+ /// The types of return value [0] and parameters [1..].
+ std::vector<SVEType> Types;
+
+ /// The "base type", which is VarType('d', BaseTypeSpec).
+ SVEType BaseType;
+
+ uint64_t Flags;
+
+ SmallVector<ImmCheck, 2> ImmChecks;
+
+public:
+ Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy,
+ StringRef MergeSuffix, uint64_t MemoryElementTy, StringRef LLVMName,
+ uint64_t Flags, ArrayRef<ImmCheck> ImmChecks, TypeSpec BT,
+ ClassKind Class, SVEEmitter &Emitter, StringRef Guard);
+
+ ~Intrinsic()=default;
+
+ std::string getName() const { return Name; }
+ std::string getLLVMName() const { return LLVMName; }
+ std::string getProto() const { return Proto; }
+ TypeSpec getBaseTypeSpec() const { return BaseTypeSpec; }
+ SVEType getBaseType() const { return BaseType; }
+
+ StringRef getGuard() const { return Guard; }
+ ClassKind getClassKind() const { return Class; }
+
+ SVEType getReturnType() const { return Types[0]; }
+ ArrayRef<SVEType> getTypes() const { return Types; }
+ SVEType getParamType(unsigned I) const { return Types[I + 1]; }
+ unsigned getNumParams() const { return Proto.size() - 1; }
+
+ uint64_t getFlags() const { return Flags; }
+ bool isFlagSet(uint64_t Flag) const { return Flags & Flag;}
+
+ ArrayRef<ImmCheck> getImmChecks() const { return ImmChecks; }
+
+ /// Return the type string for a BUILTIN() macro in Builtins.def.
+ std::string getBuiltinTypeStr();
+
+ /// Return the name, mangled with type information. The name is mangled for
+ /// ClassS, so will add type suffixes such as _u32/_s32.
+ std::string getMangledName() const { return mangleName(ClassS); }
+
+ /// Returns true if the intrinsic is overloaded, in that it should also generate
+ /// a short form without the type-specifiers, e.g. 'svld1(..)' instead of
+ /// 'svld1_u32(..)'.
+ static bool isOverloadedIntrinsic(StringRef Name) {
+ auto BrOpen = Name.find("[");
+ auto BrClose = Name.find(']');
+ return BrOpen != std::string::npos && BrClose != std::string::npos;
+ }
+
+ /// Return true if the intrinsic takes a splat operand.
+ bool hasSplat() const {
+ // These prototype modifiers are described in arm_sve.td.
+ return Proto.find_first_of("ajfrKLR@") != std::string::npos;
+ }
+
+ /// Return the parameter index of the splat operand.
+ unsigned getSplatIdx() const {
+ // These prototype modifiers are described in arm_sve.td.
+ auto Idx = Proto.find_first_of("ajfrKLR@");
+ assert(Idx != std::string::npos && Idx > 0 &&
+ "Prototype has no splat operand");
+ return Idx - 1;
+ }
+
+ /// Emits the intrinsic declaration to the ostream.
+ void emitIntrinsic(raw_ostream &OS) const;
+
+private:
+ std::string getMergeSuffix() const { return MergeSuffix; }
+ std::string mangleName(ClassKind LocalCK) const;
+ std::string replaceTemplatedArgs(std::string Name, TypeSpec TS,
+ std::string Proto) const;
+};
+
+class SVEEmitter {
+private:
+ // The reinterpret builtins are generated separately because they
+ // need the cross product of all types (121 functions in total),
+ // which is inconvenient to specify in the arm_sve.td file or
+ // generate in CGBuiltin.cpp.
+ struct ReinterpretTypeInfo {
+ const char *Suffix;
+ const char *Type;
+ const char *BuiltinType;
+ };
+ SmallVector<ReinterpretTypeInfo, 12> Reinterprets = {
+ {"s8", "svint8_t", "q16Sc"}, {"s16", "svint16_t", "q8Ss"},
+ {"s32", "svint32_t", "q4Si"}, {"s64", "svint64_t", "q2SWi"},
+ {"u8", "svuint8_t", "q16Uc"}, {"u16", "svuint16_t", "q8Us"},
+ {"u32", "svuint32_t", "q4Ui"}, {"u64", "svuint64_t", "q2UWi"},
+ {"f16", "svfloat16_t", "q8h"}, {"bf16", "svbfloat16_t", "q8y"},
+ {"f32", "svfloat32_t", "q4f"}, {"f64", "svfloat64_t", "q2d"}};
+
+ RecordKeeper &Records;
+ llvm::StringMap<uint64_t> EltTypes;
+ llvm::StringMap<uint64_t> MemEltTypes;
+ llvm::StringMap<uint64_t> FlagTypes;
+ llvm::StringMap<uint64_t> MergeTypes;
+ llvm::StringMap<uint64_t> ImmCheckTypes;
+
+public:
+ SVEEmitter(RecordKeeper &R) : Records(R) {
+ for (auto *RV : Records.getAllDerivedDefinitions("EltType"))
+ EltTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ for (auto *RV : Records.getAllDerivedDefinitions("MemEltType"))
+ MemEltTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ for (auto *RV : Records.getAllDerivedDefinitions("FlagType"))
+ FlagTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ for (auto *RV : Records.getAllDerivedDefinitions("MergeType"))
+ MergeTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ for (auto *RV : Records.getAllDerivedDefinitions("ImmCheckType"))
+ ImmCheckTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ }
+
+ /// Returns the enum value for the immcheck type
+ unsigned getEnumValueForImmCheck(StringRef C) const {
+ auto It = ImmCheckTypes.find(C);
+ if (It != ImmCheckTypes.end())
+ return It->getValue();
+ llvm_unreachable("Unsupported imm check");
+ }
+
+ /// Returns the enum value for the flag type
+ uint64_t getEnumValueForFlag(StringRef C) const {
+ auto Res = FlagTypes.find(C);
+ if (Res != FlagTypes.end())
+ return Res->getValue();
+ llvm_unreachable("Unsupported flag");
+ }
+
+ // Returns the SVETypeFlags for a given value and mask.
+ uint64_t encodeFlag(uint64_t V, StringRef MaskName) const {
+ auto It = FlagTypes.find(MaskName);
+ if (It != FlagTypes.end()) {
+ uint64_t Mask = It->getValue();
+ unsigned Shift = llvm::countTrailingZeros(Mask);
+ return (V << Shift) & Mask;
+ }
+ llvm_unreachable("Unsupported flag");
+ }
+
+ // Returns the SVETypeFlags for the given element type.
+ uint64_t encodeEltType(StringRef EltName) {
+ auto It = EltTypes.find(EltName);
+ if (It != EltTypes.end())
+ return encodeFlag(It->getValue(), "EltTypeMask");
+ llvm_unreachable("Unsupported EltType");
+ }
+
+ // Returns the SVETypeFlags for the given memory element type.
+ uint64_t encodeMemoryElementType(uint64_t MT) {
+ return encodeFlag(MT, "MemEltTypeMask");
+ }
+
+ // Returns the SVETypeFlags for the given merge type.
+ uint64_t encodeMergeType(uint64_t MT) {
+ return encodeFlag(MT, "MergeTypeMask");
+ }
+
+ // Returns the SVETypeFlags for the given splat operand.
+ unsigned encodeSplatOperand(unsigned SplatIdx) {
+ assert(SplatIdx < 7 && "SplatIdx out of encodable range");
+ return encodeFlag(SplatIdx + 1, "SplatOperandMask");
+ }
+
+ // Returns the SVETypeFlags value for the given SVEType.
+ uint64_t encodeTypeFlags(const SVEType &T);
+
+ /// Emit arm_sve.h.
+ void createHeader(raw_ostream &o);
+
+ /// Emit all the __builtin prototypes and code needed by Sema.
+ void createBuiltins(raw_ostream &o);
+
+ /// Emit all the information needed to map builtin -> LLVM IR intrinsic.
+ void createCodeGenMap(raw_ostream &o);
+
+ /// Emit all the range checks for the immediates.
+ void createRangeChecks(raw_ostream &o);
+
+ /// Create the SVETypeFlags used in CGBuiltins
+ void createTypeFlags(raw_ostream &o);
+
+ /// Create intrinsic and add it to \p Out
+ void createIntrinsic(Record *R, SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out);
+};
+
+} // end anonymous namespace
+
+
+//===----------------------------------------------------------------------===//
+// Type implementation
+//===----------------------------------------------------------------------===//
+
+std::string SVEType::builtin_str() const {
+ std::string S;
+ if (isVoid())
+ return "v";
+
+ if (isVoidPointer())
+ S += "v";
+ else if (!isFloatingPoint())
+ switch (ElementBitwidth) {
+ case 1: S += "b"; break;
+ case 8: S += "c"; break;
+ case 16: S += "s"; break;
+ case 32: S += "i"; break;
+ case 64: S += "Wi"; break;
+ case 128: S += "LLLi"; break;
+ default: llvm_unreachable("Unhandled case!");
+ }
+ else if (isFloat())
+ switch (ElementBitwidth) {
+ case 16: S += "h"; break;
+ case 32: S += "f"; break;
+ case 64: S += "d"; break;
+ default: llvm_unreachable("Unhandled case!");
+ }
+ else if (isBFloat()) {
+ assert(ElementBitwidth == 16 && "Not a valid BFloat.");
+ S += "y";
+ }
+
+ if (!isFloatingPoint()) {
+ if ((isChar() || isPointer()) && !isVoidPointer()) {
+ // Make chars and typed pointers explicitly signed.
+ if (Signed)
+ S = "S" + S;
+ else if (!Signed)
+ S = "U" + S;
+ } else if (!isVoidPointer() && !Signed) {
+ S = "U" + S;
+ }
+ }
+
+ // Constant indices are "int", but have the "constant expression" modifier.
+ if (isImmediate()) {
+ assert(!isFloat() && "fp immediates are not supported");
+ S = "I" + S;
+ }
+
+ if (isScalar()) {
+ if (Constant) S += "C";
+ if (Pointer) S += "*";
+ return S;
+ }
+
+ assert(isScalableVector() && "Unsupported type");
+ return "q" + utostr(getNumElements() * NumVectors) + S;
+}
+
+std::string SVEType::str() const {
+ if (isPredicatePattern())
+ return "sv_pattern";
+
+ if (isPrefetchOp())
+ return "sv_prfop";
+
+ std::string S;
+ if (Void)
+ S += "void";
+ else {
+ if (isScalableVector())
+ S += "sv";
+ if (!Signed && !isFloatingPoint())
+ S += "u";
+
+ if (Float)
+ S += "float";
+ else if (isScalarPredicate() || isPredicateVector())
+ S += "bool";
+ else if (isBFloat())
+ S += "bfloat";
+ else
+ S += "int";
+
+ if (!isScalarPredicate() && !isPredicateVector())
+ S += utostr(ElementBitwidth);
+ if (!isScalableVector() && isVector())
+ S += "x" + utostr(getNumElements());
+ if (NumVectors > 1)
+ S += "x" + utostr(NumVectors);
+ if (!isScalarPredicate())
+ S += "_t";
+ }
+
+ if (Constant)
+ S += " const";
+ if (Pointer)
+ S += " *";
+
+ return S;
+}
+void SVEType::applyTypespec() {
+ for (char I : TS) {
+ switch (I) {
+ case 'P':
+ Predicate = true;
+ break;
+ case 'U':
+ Signed = false;
+ break;
+ case 'c':
+ ElementBitwidth = 8;
+ break;
+ case 's':
+ ElementBitwidth = 16;
+ break;
+ case 'i':
+ ElementBitwidth = 32;
+ break;
+ case 'l':
+ ElementBitwidth = 64;
+ break;
+ case 'h':
+ Float = true;
+ ElementBitwidth = 16;
+ break;
+ case 'f':
+ Float = true;
+ ElementBitwidth = 32;
+ break;
+ case 'd':
+ Float = true;
+ ElementBitwidth = 64;
+ break;
+ case 'b':
+ BFloat = true;
+ Float = false;
+ ElementBitwidth = 16;
+ break;
+ default:
+ llvm_unreachable("Unhandled type code!");
+ }
+ }
+ assert(ElementBitwidth != ~0U && "Bad element bitwidth!");
+}
+
+void SVEType::applyModifier(char Mod) {
+ switch (Mod) {
+ case '2':
+ NumVectors = 2;
+ break;
+ case '3':
+ NumVectors = 3;
+ break;
+ case '4':
+ NumVectors = 4;
+ break;
+ case 'v':
+ Void = true;
+ break;
+ case 'd':
+ DefaultType = true;
+ break;
+ case 'c':
+ Constant = true;
+ LLVM_FALLTHROUGH;
+ case 'p':
+ Pointer = true;
+ Bitwidth = ElementBitwidth;
+ NumVectors = 0;
+ break;
+ case 'e':
+ Signed = false;
+ ElementBitwidth /= 2;
+ break;
+ case 'h':
+ ElementBitwidth /= 2;
+ break;
+ case 'q':
+ ElementBitwidth /= 4;
+ break;
+ case 'b':
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth /= 4;
+ break;
+ case 'o':
+ ElementBitwidth *= 4;
+ break;
+ case 'P':
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ Predicate = true;
+ Bitwidth = 16;
+ ElementBitwidth = 1;
+ break;
+ case 's':
+ case 'a':
+ Bitwidth = ElementBitwidth;
+ NumVectors = 0;
+ break;
+ case 'R':
+ ElementBitwidth /= 2;
+ NumVectors = 0;
+ break;
+ case 'r':
+ ElementBitwidth /= 4;
+ NumVectors = 0;
+ break;
+ case '@':
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth /= 4;
+ NumVectors = 0;
+ break;
+ case 'K':
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ Bitwidth = ElementBitwidth;
+ NumVectors = 0;
+ break;
+ case 'L':
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ Bitwidth = ElementBitwidth;
+ NumVectors = 0;
+ break;
+ case 'u':
+ Predicate = false;
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ break;
+ case 'x':
+ Predicate = false;
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ break;
+ case 'i':
+ Predicate = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ Signed = false;
+ Immediate = true;
+ break;
+ case 'I':
+ Predicate = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = true;
+ Immediate = true;
+ PredicatePattern = true;
+ break;
+ case 'J':
+ Predicate = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = true;
+ Immediate = true;
+ PrefetchOp = true;
+ break;
+ case 'k':
+ Predicate = false;
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ break;
+ case 'l':
+ Predicate = false;
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ break;
+ case 'm':
+ Predicate = false;
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ break;
+ case 'n':
+ Predicate = false;
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ break;
+ case 'w':
+ ElementBitwidth = 64;
+ break;
+ case 'j':
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ break;
+ case 'f':
+ Signed = false;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ break;
+ case 'g':
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = 64;
+ break;
+ case 't':
+ Signed = true;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = 32;
+ break;
+ case 'z':
+ Signed = false;
+ Float = false;
+ BFloat = false;
+ ElementBitwidth = 32;
+ break;
+ case 'O':
+ Predicate = false;
+ Float = true;
+ ElementBitwidth = 16;
+ break;
+ case 'M':
+ Predicate = false;
+ Float = true;
+ BFloat = false;
+ ElementBitwidth = 32;
+ break;
+ case 'N':
+ Predicate = false;
+ Float = true;
+ ElementBitwidth = 64;
+ break;
+ case 'Q':
+ Constant = true;
+ Pointer = true;
+ Void = true;
+ NumVectors = 0;
+ break;
+ case 'S':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 8;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'W':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 8;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ case 'T':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 16;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'X':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 16;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ case 'Y':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ case 'U':
+ Constant = true;
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'A':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 8;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'B':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 16;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'C':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'D':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ Signed = true;
+ break;
+ case 'E':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 8;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ case 'F':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 16;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ case 'G':
+ Pointer = true;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = false;
+ break;
+ default:
+ llvm_unreachable("Unhandled character!");
+ }
+}
+
+
+//===----------------------------------------------------------------------===//
+// Intrinsic implementation
+//===----------------------------------------------------------------------===//
+
+Intrinsic::Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy,
+ StringRef MergeSuffix, uint64_t MemoryElementTy,
+ StringRef LLVMName, uint64_t Flags,
+ ArrayRef<ImmCheck> Checks, TypeSpec BT, ClassKind Class,
+ SVEEmitter &Emitter, StringRef Guard)
+ : Name(Name.str()), LLVMName(LLVMName), Proto(Proto.str()),
+ BaseTypeSpec(BT), Class(Class), Guard(Guard.str()),
+ MergeSuffix(MergeSuffix.str()), BaseType(BT, 'd'), Flags(Flags),
+ ImmChecks(Checks.begin(), Checks.end()) {
+ // Types[0] is the return value.
+ for (unsigned I = 0; I < Proto.size(); ++I) {
+ SVEType T(BaseTypeSpec, Proto[I]);
+ Types.push_back(T);
+
+ // Add range checks for immediates
+ if (I > 0) {
+ if (T.isPredicatePattern())
+ ImmChecks.emplace_back(
+ I - 1, Emitter.getEnumValueForImmCheck("ImmCheck0_31"));
+ else if (T.isPrefetchOp())
+ ImmChecks.emplace_back(
+ I - 1, Emitter.getEnumValueForImmCheck("ImmCheck0_13"));
+ }
+ }
+
+ // Set flags based on properties
+ this->Flags |= Emitter.encodeTypeFlags(BaseType);
+ this->Flags |= Emitter.encodeMemoryElementType(MemoryElementTy);
+ this->Flags |= Emitter.encodeMergeType(MergeTy);
+ if (hasSplat())
+ this->Flags |= Emitter.encodeSplatOperand(getSplatIdx());
+}
+
+std::string Intrinsic::getBuiltinTypeStr() {
+ std::string S = getReturnType().builtin_str();
+ for (unsigned I = 0; I < getNumParams(); ++I)
+ S += getParamType(I).builtin_str();
+
+ return S;
+}
+
+std::string Intrinsic::replaceTemplatedArgs(std::string Name, TypeSpec TS,
+ std::string Proto) const {
+ std::string Ret = Name;
+ while (Ret.find('{') != std::string::npos) {
+ size_t Pos = Ret.find('{');
+ size_t End = Ret.find('}');
+ unsigned NumChars = End - Pos + 1;
+ assert(NumChars == 3 && "Unexpected template argument");
+
+ SVEType T;
+ char C = Ret[Pos+1];
+ switch(C) {
+ default:
+ llvm_unreachable("Unknown predication specifier");
+ case 'd':
+ T = SVEType(TS, 'd');
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ T = SVEType(TS, Proto[C - '0']);
+ break;
+ }
+
+ // Replace templated arg with the right suffix (e.g. u32)
+ std::string TypeCode;
+ if (T.isInteger())
+ TypeCode = T.isSigned() ? 's' : 'u';
+ else if (T.isPredicateVector())
+ TypeCode = 'b';
+ else if (T.isBFloat())
+ TypeCode = "bf";
+ else
+ TypeCode = 'f';
+ Ret.replace(Pos, NumChars, TypeCode + utostr(T.getElementSizeInBits()));
+ }
+
+ return Ret;
+}
+
+std::string Intrinsic::mangleName(ClassKind LocalCK) const {
+ std::string S = getName();
+
+ if (LocalCK == ClassG) {
+ // Remove the square brackets and everything in between.
+ while (S.find("[") != std::string::npos) {
+ auto Start = S.find("[");
+ auto End = S.find(']');
+ S.erase(Start, (End-Start)+1);
+ }
+ } else {
+ // Remove the square brackets.
+ while (S.find("[") != std::string::npos) {
+ auto BrPos = S.find('[');
+ if (BrPos != std::string::npos)
+ S.erase(BrPos, 1);
+ BrPos = S.find(']');
+ if (BrPos != std::string::npos)
+ S.erase(BrPos, 1);
+ }
+ }
+
+ // Replace all {d} like expressions with e.g. 'u32'
+ return replaceTemplatedArgs(S, getBaseTypeSpec(), getProto()) +
+ getMergeSuffix();
+}
+
+void Intrinsic::emitIntrinsic(raw_ostream &OS) const {
+ // Use the preprocessor to
+ if (getClassKind() != ClassG || getProto().size() <= 1) {
+ OS << "#define " << mangleName(getClassKind())
+ << "(...) __builtin_sve_" << mangleName(ClassS)
+ << "(__VA_ARGS__)\n";
+ } else {
+ std::string FullName = mangleName(ClassS);
+ std::string ProtoName = mangleName(ClassG);
+
+ OS << "__aio __attribute__((__clang_arm_builtin_alias("
+ << "__builtin_sve_" << FullName << ")))\n";
+
+ OS << getTypes()[0].str() << " " << ProtoName << "(";
+ for (unsigned I = 0; I < getTypes().size() - 1; ++I) {
+ if (I != 0)
+ OS << ", ";
+ OS << getTypes()[I + 1].str();
+ }
+ OS << ");\n";
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// SVEEmitter implementation
+//===----------------------------------------------------------------------===//
+uint64_t SVEEmitter::encodeTypeFlags(const SVEType &T) {
+ if (T.isFloat()) {
+ switch (T.getElementSizeInBits()) {
+ case 16:
+ return encodeEltType("EltTyFloat16");
+ case 32:
+ return encodeEltType("EltTyFloat32");
+ case 64:
+ return encodeEltType("EltTyFloat64");
+ default:
+ llvm_unreachable("Unhandled float element bitwidth!");
+ }
+ }
+
+ if (T.isBFloat()) {
+ assert(T.getElementSizeInBits() == 16 && "Not a valid BFloat.");
+ return encodeEltType("EltTyBFloat16");
+ }
+
+ if (T.isPredicateVector()) {
+ switch (T.getElementSizeInBits()) {
+ case 8:
+ return encodeEltType("EltTyBool8");
+ case 16:
+ return encodeEltType("EltTyBool16");
+ case 32:
+ return encodeEltType("EltTyBool32");
+ case 64:
+ return encodeEltType("EltTyBool64");
+ default:
+ llvm_unreachable("Unhandled predicate element bitwidth!");
+ }
+ }
+
+ switch (T.getElementSizeInBits()) {
+ case 8:
+ return encodeEltType("EltTyInt8");
+ case 16:
+ return encodeEltType("EltTyInt16");
+ case 32:
+ return encodeEltType("EltTyInt32");
+ case 64:
+ return encodeEltType("EltTyInt64");
+ default:
+ llvm_unreachable("Unhandled integer element bitwidth!");
+ }
+}
+
+void SVEEmitter::createIntrinsic(
+ Record *R, SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out) {
+ StringRef Name = R->getValueAsString("Name");
+ StringRef Proto = R->getValueAsString("Prototype");
+ StringRef Types = R->getValueAsString("Types");
+ StringRef Guard = R->getValueAsString("ArchGuard");
+ StringRef LLVMName = R->getValueAsString("LLVMIntrinsic");
+ uint64_t Merge = R->getValueAsInt("Merge");
+ StringRef MergeSuffix = R->getValueAsString("MergeSuffix");
+ uint64_t MemEltType = R->getValueAsInt("MemEltType");
+ std::vector<Record*> FlagsList = R->getValueAsListOfDefs("Flags");
+ std::vector<Record*> ImmCheckList = R->getValueAsListOfDefs("ImmChecks");
+
+ int64_t Flags = 0;
+ for (auto FlagRec : FlagsList)
+ Flags |= FlagRec->getValueAsInt("Value");
+
+ // Create a dummy TypeSpec for non-overloaded builtins.
+ if (Types.empty()) {
+ assert((Flags & getEnumValueForFlag("IsOverloadNone")) &&
+ "Expect TypeSpec for overloaded builtin!");
+ Types = "i";
+ }
+
+ // Extract type specs from string
+ SmallVector<TypeSpec, 8> TypeSpecs;
+ TypeSpec Acc;
+ for (char I : Types) {
+ Acc.push_back(I);
+ if (islower(I)) {
+ TypeSpecs.push_back(TypeSpec(Acc));
+ Acc.clear();
+ }
+ }
+
+ // Remove duplicate type specs.
+ llvm::sort(TypeSpecs);
+ TypeSpecs.erase(std::unique(TypeSpecs.begin(), TypeSpecs.end()),
+ TypeSpecs.end());
+
+ // Create an Intrinsic for each type spec.
+ for (auto TS : TypeSpecs) {
+ // Collate a list of range/option checks for the immediates.
+ SmallVector<ImmCheck, 2> ImmChecks;
+ for (auto *R : ImmCheckList) {
+ int64_t Arg = R->getValueAsInt("Arg");
+ int64_t EltSizeArg = R->getValueAsInt("EltSizeArg");
+ int64_t Kind = R->getValueAsDef("Kind")->getValueAsInt("Value");
+ assert(Arg >= 0 && Kind >= 0 && "Arg and Kind must be nonnegative");
+
+ unsigned ElementSizeInBits = 0;
+ if (EltSizeArg >= 0)
+ ElementSizeInBits =
+ SVEType(TS, Proto[EltSizeArg + /* offset by return arg */ 1])
+ .getElementSizeInBits();
+ ImmChecks.push_back(ImmCheck(Arg, Kind, ElementSizeInBits));
+ }
+
+ Out.push_back(std::make_unique<Intrinsic>(
+ Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags, ImmChecks,
+ TS, ClassS, *this, Guard));
+
+ // Also generate the short-form (e.g. svadd_m) for the given type-spec.
+ if (Intrinsic::isOverloadedIntrinsic(Name))
+ Out.push_back(std::make_unique<Intrinsic>(
+ Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags,
+ ImmChecks, TS, ClassG, *this, Guard));
+ }
+}
+
+void SVEEmitter::createHeader(raw_ostream &OS) {
+ OS << "/*===---- arm_sve.h - ARM SVE 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";
+
+ OS << "#ifndef __ARM_SVE_H\n";
+ OS << "#define __ARM_SVE_H\n\n";
+
+ OS << "#if !defined(__ARM_FEATURE_SVE)\n";
+ OS << "#error \"SVE support not enabled\"\n";
+ OS << "#else\n\n";
+
+ OS << "#if !defined(__LITTLE_ENDIAN__)\n";
+ OS << "#error \"Big endian is currently not supported for arm_sve.h\"\n";
+ OS << "#endif\n";
+
+ OS << "#include <stdint.h>\n\n";
+ OS << "#ifdef __cplusplus\n";
+ OS << "extern \"C\" {\n";
+ OS << "#else\n";
+ OS << "#include <stdbool.h>\n";
+ OS << "#endif\n\n";
+
+ OS << "typedef __fp16 float16_t;\n";
+ OS << "typedef float float32_t;\n";
+ OS << "typedef double float64_t;\n";
+
+ OS << "typedef __SVInt8_t svint8_t;\n";
+ OS << "typedef __SVInt16_t svint16_t;\n";
+ OS << "typedef __SVInt32_t svint32_t;\n";
+ OS << "typedef __SVInt64_t svint64_t;\n";
+ OS << "typedef __SVUint8_t svuint8_t;\n";
+ OS << "typedef __SVUint16_t svuint16_t;\n";
+ OS << "typedef __SVUint32_t svuint32_t;\n";
+ OS << "typedef __SVUint64_t svuint64_t;\n";
+ OS << "typedef __SVFloat16_t svfloat16_t;\n\n";
+
+ OS << "#if defined(__ARM_FEATURE_SVE_BF16) && "
+ "!defined(__ARM_FEATURE_BF16_SCALAR_ARITHMETIC)\n";
+ OS << "#error \"__ARM_FEATURE_BF16_SCALAR_ARITHMETIC must be defined when "
+ "__ARM_FEATURE_SVE_BF16 is defined\"\n";
+ OS << "#endif\n\n";
+
+ OS << "#if defined(__ARM_FEATURE_SVE_BF16)\n";
+ OS << "typedef __SVBFloat16_t svbfloat16_t;\n";
+ OS << "#endif\n\n";
+
+ OS << "#if defined(__ARM_FEATURE_BF16_SCALAR_ARITHMETIC)\n";
+ OS << "#include <arm_bf16.h>\n";
+ OS << "typedef __bf16 bfloat16_t;\n";
+ OS << "#endif\n\n";
+
+ OS << "typedef __SVFloat32_t svfloat32_t;\n";
+ OS << "typedef __SVFloat64_t svfloat64_t;\n";
+ OS << "typedef __clang_svint8x2_t svint8x2_t;\n";
+ OS << "typedef __clang_svint16x2_t svint16x2_t;\n";
+ OS << "typedef __clang_svint32x2_t svint32x2_t;\n";
+ OS << "typedef __clang_svint64x2_t svint64x2_t;\n";
+ OS << "typedef __clang_svuint8x2_t svuint8x2_t;\n";
+ OS << "typedef __clang_svuint16x2_t svuint16x2_t;\n";
+ OS << "typedef __clang_svuint32x2_t svuint32x2_t;\n";
+ OS << "typedef __clang_svuint64x2_t svuint64x2_t;\n";
+ OS << "typedef __clang_svfloat16x2_t svfloat16x2_t;\n";
+ OS << "typedef __clang_svfloat32x2_t svfloat32x2_t;\n";
+ OS << "typedef __clang_svfloat64x2_t svfloat64x2_t;\n";
+ OS << "typedef __clang_svint8x3_t svint8x3_t;\n";
+ OS << "typedef __clang_svint16x3_t svint16x3_t;\n";
+ OS << "typedef __clang_svint32x3_t svint32x3_t;\n";
+ OS << "typedef __clang_svint64x3_t svint64x3_t;\n";
+ OS << "typedef __clang_svuint8x3_t svuint8x3_t;\n";
+ OS << "typedef __clang_svuint16x3_t svuint16x3_t;\n";
+ OS << "typedef __clang_svuint32x3_t svuint32x3_t;\n";
+ OS << "typedef __clang_svuint64x3_t svuint64x3_t;\n";
+ OS << "typedef __clang_svfloat16x3_t svfloat16x3_t;\n";
+ OS << "typedef __clang_svfloat32x3_t svfloat32x3_t;\n";
+ OS << "typedef __clang_svfloat64x3_t svfloat64x3_t;\n";
+ OS << "typedef __clang_svint8x4_t svint8x4_t;\n";
+ OS << "typedef __clang_svint16x4_t svint16x4_t;\n";
+ OS << "typedef __clang_svint32x4_t svint32x4_t;\n";
+ OS << "typedef __clang_svint64x4_t svint64x4_t;\n";
+ OS << "typedef __clang_svuint8x4_t svuint8x4_t;\n";
+ OS << "typedef __clang_svuint16x4_t svuint16x4_t;\n";
+ OS << "typedef __clang_svuint32x4_t svuint32x4_t;\n";
+ OS << "typedef __clang_svuint64x4_t svuint64x4_t;\n";
+ OS << "typedef __clang_svfloat16x4_t svfloat16x4_t;\n";
+ OS << "typedef __clang_svfloat32x4_t svfloat32x4_t;\n";
+ OS << "typedef __clang_svfloat64x4_t svfloat64x4_t;\n";
+ OS << "typedef __SVBool_t svbool_t;\n\n";
+
+ OS << "#ifdef __ARM_FEATURE_SVE_BF16\n";
+ OS << "typedef __clang_svbfloat16x2_t svbfloat16x2_t;\n";
+ OS << "typedef __clang_svbfloat16x3_t svbfloat16x3_t;\n";
+ OS << "typedef __clang_svbfloat16x4_t svbfloat16x4_t;\n";
+ OS << "#endif\n";
+
+ OS << "typedef enum\n";
+ OS << "{\n";
+ OS << " SV_POW2 = 0,\n";
+ OS << " SV_VL1 = 1,\n";
+ OS << " SV_VL2 = 2,\n";
+ OS << " SV_VL3 = 3,\n";
+ OS << " SV_VL4 = 4,\n";
+ OS << " SV_VL5 = 5,\n";
+ OS << " SV_VL6 = 6,\n";
+ OS << " SV_VL7 = 7,\n";
+ OS << " SV_VL8 = 8,\n";
+ OS << " SV_VL16 = 9,\n";
+ OS << " SV_VL32 = 10,\n";
+ OS << " SV_VL64 = 11,\n";
+ OS << " SV_VL128 = 12,\n";
+ OS << " SV_VL256 = 13,\n";
+ OS << " SV_MUL4 = 29,\n";
+ OS << " SV_MUL3 = 30,\n";
+ OS << " SV_ALL = 31\n";
+ OS << "} sv_pattern;\n\n";
+
+ OS << "typedef enum\n";
+ OS << "{\n";
+ OS << " SV_PLDL1KEEP = 0,\n";
+ OS << " SV_PLDL1STRM = 1,\n";
+ OS << " SV_PLDL2KEEP = 2,\n";
+ OS << " SV_PLDL2STRM = 3,\n";
+ OS << " SV_PLDL3KEEP = 4,\n";
+ OS << " SV_PLDL3STRM = 5,\n";
+ OS << " SV_PSTL1KEEP = 8,\n";
+ OS << " SV_PSTL1STRM = 9,\n";
+ OS << " SV_PSTL2KEEP = 10,\n";
+ OS << " SV_PSTL2STRM = 11,\n";
+ OS << " SV_PSTL3KEEP = 12,\n";
+ OS << " SV_PSTL3STRM = 13\n";
+ OS << "} sv_prfop;\n\n";
+
+ OS << "/* Function attributes */\n";
+ OS << "#define __aio static inline __attribute__((__always_inline__, "
+ "__nodebug__, __overloadable__))\n\n";
+
+ // Add reinterpret functions.
+ for (auto ShortForm : { false, true } )
+ for (const ReinterpretTypeInfo &From : Reinterprets)
+ for (const ReinterpretTypeInfo &To : Reinterprets) {
+ const bool IsBFloat = StringRef(From.Suffix).equals("bf16") ||
+ StringRef(To.Suffix).equals("bf16");
+ if (IsBFloat)
+ OS << "#if defined(__ARM_FEATURE_SVE_BF16)\n";
+ if (ShortForm) {
+ OS << "__aio " << From.Type << " svreinterpret_" << From.Suffix;
+ OS << "(" << To.Type << " op) {\n";
+ OS << " return __builtin_sve_reinterpret_" << From.Suffix << "_"
+ << To.Suffix << "(op);\n";
+ OS << "}\n\n";
+ } else
+ OS << "#define svreinterpret_" << From.Suffix << "_" << To.Suffix
+ << "(...) __builtin_sve_reinterpret_" << From.Suffix << "_"
+ << To.Suffix << "(__VA_ARGS__)\n";
+ if (IsBFloat)
+ OS << "#endif /* #if defined(__ARM_FEATURE_SVE_BF16) */\n";
+ }
+
+ SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ // Sort intrinsics in header file by following order/priority:
+ // - Architectural guard (i.e. does it require SVE2 or SVE2_AES)
+ // - Class (is intrinsic overloaded or not)
+ // - Intrinsic name
+ std::stable_sort(
+ Defs.begin(), Defs.end(), [](const std::unique_ptr<Intrinsic> &A,
+ const std::unique_ptr<Intrinsic> &B) {
+ auto ToTuple = [](const std::unique_ptr<Intrinsic> &I) {
+ return std::make_tuple(I->getGuard(), (unsigned)I->getClassKind(), I->getName());
+ };
+ return ToTuple(A) < ToTuple(B);
+ });
+
+ StringRef InGuard = "";
+ for (auto &I : Defs) {
+ // Emit #endif/#if pair if needed.
+ if (I->getGuard() != InGuard) {
+ if (!InGuard.empty())
+ OS << "#endif //" << InGuard << "\n";
+ InGuard = I->getGuard();
+ if (!InGuard.empty())
+ OS << "\n#if " << InGuard << "\n";
+ }
+
+ // Actually emit the intrinsic declaration.
+ I->emitIntrinsic(OS);
+ }
+
+ if (!InGuard.empty())
+ OS << "#endif //" << InGuard << "\n";
+
+ OS << "#if defined(__ARM_FEATURE_SVE_BF16)\n";
+ OS << "#define svcvtnt_bf16_x svcvtnt_bf16_m\n";
+ OS << "#define svcvtnt_bf16_f32_x svcvtnt_bf16_f32_m\n";
+ OS << "#endif /*__ARM_FEATURE_SVE_BF16 */\n\n";
+
+ OS << "#if defined(__ARM_FEATURE_SVE2)\n";
+ OS << "#define svcvtnt_f16_x svcvtnt_f16_m\n";
+ OS << "#define svcvtnt_f16_f32_x svcvtnt_f16_f32_m\n";
+ OS << "#define svcvtnt_f32_x svcvtnt_f32_m\n";
+ OS << "#define svcvtnt_f32_f64_x svcvtnt_f32_f64_m\n\n";
+
+ OS << "#define svcvtxnt_f32_x svcvtxnt_f32_m\n";
+ OS << "#define svcvtxnt_f32_f64_x svcvtxnt_f32_f64_m\n\n";
+
+ OS << "#endif /*__ARM_FEATURE_SVE2 */\n\n";
+
+ OS << "#ifdef __cplusplus\n";
+ OS << "} // extern \"C\"\n";
+ OS << "#endif\n\n";
+ OS << "#endif /*__ARM_FEATURE_SVE */\n\n";
+ OS << "#endif /* __ARM_SVE_H */\n";
+}
+
+void SVEEmitter::createBuiltins(raw_ostream &OS) {
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ // The mappings must be sorted based on BuiltinID.
+ llvm::sort(Defs, [](const std::unique_ptr<Intrinsic> &A,
+ const std::unique_ptr<Intrinsic> &B) {
+ return A->getMangledName() < B->getMangledName();
+ });
+
+ OS << "#ifdef GET_SVE_BUILTINS\n";
+ for (auto &Def : Defs) {
+ // Only create BUILTINs for non-overloaded intrinsics, as overloaded
+ // declarations only live in the header file.
+ if (Def->getClassKind() != ClassG)
+ OS << "BUILTIN(__builtin_sve_" << Def->getMangledName() << ", \""
+ << Def->getBuiltinTypeStr() << "\", \"n\")\n";
+ }
+
+ // Add reinterpret builtins
+ for (const ReinterpretTypeInfo &From : Reinterprets)
+ for (const ReinterpretTypeInfo &To : Reinterprets)
+ OS << "BUILTIN(__builtin_sve_reinterpret_" << From.Suffix << "_"
+ << To.Suffix << +", \"" << From.BuiltinType << To.BuiltinType
+ << "\", \"n\")\n";
+
+ OS << "#endif\n\n";
+ }
+
+void SVEEmitter::createCodeGenMap(raw_ostream &OS) {
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ // The mappings must be sorted based on BuiltinID.
+ llvm::sort(Defs, [](const std::unique_ptr<Intrinsic> &A,
+ const std::unique_ptr<Intrinsic> &B) {
+ return A->getMangledName() < B->getMangledName();
+ });
+
+ OS << "#ifdef GET_SVE_LLVM_INTRINSIC_MAP\n";
+ for (auto &Def : Defs) {
+ // Builtins only exist for non-overloaded intrinsics, overloaded
+ // declarations only live in the header file.
+ if (Def->getClassKind() == ClassG)
+ continue;
+
+ uint64_t Flags = Def->getFlags();
+ auto FlagString = std::to_string(Flags);
+
+ std::string LLVMName = Def->getLLVMName();
+ std::string Builtin = Def->getMangledName();
+ if (!LLVMName.empty())
+ OS << "SVEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString
+ << "),\n";
+ else
+ OS << "SVEMAP2(" << Builtin << ", " << FlagString << "),\n";
+ }
+ OS << "#endif\n\n";
+}
+
+void SVEEmitter::createRangeChecks(raw_ostream &OS) {
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ // The mappings must be sorted based on BuiltinID.
+ llvm::sort(Defs, [](const std::unique_ptr<Intrinsic> &A,
+ const std::unique_ptr<Intrinsic> &B) {
+ return A->getMangledName() < B->getMangledName();
+ });
+
+
+ OS << "#ifdef GET_SVE_IMMEDIATE_CHECK\n";
+
+ // Ensure these are only emitted once.
+ std::set<std::string> Emitted;
+
+ for (auto &Def : Defs) {
+ if (Emitted.find(Def->getMangledName()) != Emitted.end() ||
+ Def->getImmChecks().empty())
+ continue;
+
+ OS << "case SVE::BI__builtin_sve_" << Def->getMangledName() << ":\n";
+ for (auto &Check : Def->getImmChecks())
+ OS << "ImmChecks.push_back(std::make_tuple(" << Check.getArg() << ", "
+ << Check.getKind() << ", " << Check.getElementSizeInBits() << "));\n";
+ OS << " break;\n";
+
+ Emitted.insert(Def->getMangledName());
+ }
+
+ OS << "#endif\n\n";
+}
+
+/// Create the SVETypeFlags used in CGBuiltins
+void SVEEmitter::createTypeFlags(raw_ostream &OS) {
+ OS << "#ifdef LLVM_GET_SVE_TYPEFLAGS\n";
+ for (auto &KV : FlagTypes)
+ OS << "const uint64_t " << KV.getKey() << " = " << KV.getValue() << ";\n";
+ OS << "#endif\n\n";
+
+ OS << "#ifdef LLVM_GET_SVE_ELTTYPES\n";
+ for (auto &KV : EltTypes)
+ OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
+ OS << "#endif\n\n";
+
+ OS << "#ifdef LLVM_GET_SVE_MEMELTTYPES\n";
+ for (auto &KV : MemEltTypes)
+ OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
+ OS << "#endif\n\n";
+
+ OS << "#ifdef LLVM_GET_SVE_MERGETYPES\n";
+ for (auto &KV : MergeTypes)
+ OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
+ OS << "#endif\n\n";
+
+ OS << "#ifdef LLVM_GET_SVE_IMMCHECKTYPES\n";
+ for (auto &KV : ImmCheckTypes)
+ OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
+ OS << "#endif\n\n";
+}
+
+namespace clang {
+void EmitSveHeader(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createHeader(OS);
+}
+
+void EmitSveBuiltins(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createBuiltins(OS);
+}
+
+void EmitSveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createCodeGenMap(OS);
+}
+
+void EmitSveRangeChecks(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createRangeChecks(OS);
+}
+
+void EmitSveTypeFlags(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createTypeFlags(OS);
+}
+
+} // End namespace clang
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 6ba90cee4aae..1d6ef8065bb8 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -63,6 +63,7 @@ enum ActionType {
GenClangOpenCLBuiltins,
GenArmNeon,
GenArmFP16,
+ GenArmBF16,
GenArmNeonSema,
GenArmNeonTest,
GenArmMveHeader,
@@ -70,6 +71,16 @@ enum ActionType {
GenArmMveBuiltinSema,
GenArmMveBuiltinCG,
GenArmMveBuiltinAliases,
+ GenArmSveHeader,
+ GenArmSveBuiltins,
+ GenArmSveBuiltinCG,
+ GenArmSveTypeFlags,
+ GenArmSveRangeChecks,
+ GenArmCdeHeader,
+ GenArmCdeBuiltinDef,
+ GenArmCdeBuiltinSema,
+ GenArmCdeBuiltinCG,
+ GenArmCdeBuiltinAliases,
GenAttrDocs,
GenDiagDocs,
GenOptDocs,
@@ -176,10 +187,21 @@ cl::opt<ActionType> Action(
"Generate OpenCL builtin declaration handlers"),
clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"),
clEnumValN(GenArmFP16, "gen-arm-fp16", "Generate arm_fp16.h for clang"),
+ clEnumValN(GenArmBF16, "gen-arm-bf16", "Generate arm_bf16.h for clang"),
clEnumValN(GenArmNeonSema, "gen-arm-neon-sema",
"Generate ARM NEON sema support for clang"),
clEnumValN(GenArmNeonTest, "gen-arm-neon-test",
"Generate ARM NEON tests for clang"),
+ clEnumValN(GenArmSveHeader, "gen-arm-sve-header",
+ "Generate arm_sve.h for clang"),
+ clEnumValN(GenArmSveBuiltins, "gen-arm-sve-builtins",
+ "Generate arm_sve_builtins.inc for clang"),
+ clEnumValN(GenArmSveBuiltinCG, "gen-arm-sve-builtin-codegen",
+ "Generate arm_sve_builtin_cg_map.inc for clang"),
+ clEnumValN(GenArmSveTypeFlags, "gen-arm-sve-typeflags",
+ "Generate arm_sve_typeflags.inc for clang"),
+ clEnumValN(GenArmSveRangeChecks, "gen-arm-sve-sema-rangechecks",
+ "Generate arm_sve_sema_rangechecks.inc for clang"),
clEnumValN(GenArmMveHeader, "gen-arm-mve-header",
"Generate arm_mve.h for clang"),
clEnumValN(GenArmMveBuiltinDef, "gen-arm-mve-builtin-def",
@@ -190,6 +212,16 @@ cl::opt<ActionType> Action(
"Generate ARM MVE builtin code-generator for clang"),
clEnumValN(GenArmMveBuiltinAliases, "gen-arm-mve-builtin-aliases",
"Generate list of valid ARM MVE builtin aliases for clang"),
+ clEnumValN(GenArmCdeHeader, "gen-arm-cde-header",
+ "Generate arm_cde.h for clang"),
+ clEnumValN(GenArmCdeBuiltinDef, "gen-arm-cde-builtin-def",
+ "Generate ARM CDE builtin definitions for clang"),
+ clEnumValN(GenArmCdeBuiltinSema, "gen-arm-cde-builtin-sema",
+ "Generate ARM CDE builtin sema checks for clang"),
+ clEnumValN(GenArmCdeBuiltinCG, "gen-arm-cde-builtin-codegen",
+ "Generate ARM CDE builtin code-generator for clang"),
+ clEnumValN(GenArmCdeBuiltinAliases, "gen-arm-cde-builtin-aliases",
+ "Generate list of valid ARM CDE builtin aliases for clang"),
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
@@ -330,6 +362,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenArmFP16:
EmitFP16(Records, OS);
break;
+ case GenArmBF16:
+ EmitBF16(Records, OS);
+ break;
case GenArmNeonSema:
EmitNeonSema(Records, OS);
break;
@@ -351,6 +386,36 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenArmMveBuiltinAliases:
EmitMveBuiltinAliases(Records, OS);
break;
+ case GenArmSveHeader:
+ EmitSveHeader(Records, OS);
+ break;
+ case GenArmSveBuiltins:
+ EmitSveBuiltins(Records, OS);
+ break;
+ case GenArmSveBuiltinCG:
+ EmitSveBuiltinCG(Records, OS);
+ break;
+ case GenArmSveTypeFlags:
+ EmitSveTypeFlags(Records, OS);
+ break;
+ case GenArmSveRangeChecks:
+ EmitSveRangeChecks(Records, OS);
+ break;
+ case GenArmCdeHeader:
+ EmitCdeHeader(Records, OS);
+ break;
+ case GenArmCdeBuiltinDef:
+ EmitCdeBuiltinDef(Records, OS);
+ break;
+ case GenArmCdeBuiltinSema:
+ EmitCdeBuiltinSema(Records, OS);
+ break;
+ case GenArmCdeBuiltinCG:
+ EmitCdeBuiltinCG(Records, OS);
+ break;
+ case GenArmCdeBuiltinAliases:
+ EmitCdeBuiltinAliases(Records, OS);
+ break;
case GenAttrDocs:
EmitClangAttrDocs(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index 7ac2e0eeb1f3..9717903ba52c 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -85,18 +85,31 @@ void EmitClangOpcodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeon(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitFP16(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitBF16(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonTest(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeon2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonSema2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonTest2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveTypeFlags(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveRangeChecks(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+
void EmitMveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitCdeHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitCdeBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitCdeBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitCdeBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitCdeBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+
void EmitClangAttrDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangDiagDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangOptDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);