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