diff options
Diffstat (limited to 'contrib/llvm-project/clang/utils/TableGen')
21 files changed, 3038 insertions, 1904 deletions
diff --git a/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.cpp b/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.cpp index 3f6da40964e0..54288ff6a03b 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.cpp @@ -15,6 +15,7 @@ #include "ASTTableGen.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/Error.h" +#include <optional> using namespace llvm; using namespace clang; @@ -32,7 +33,7 @@ llvm::StringRef clang::tblgen::HasProperties::getName() const { static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) { StringRef nodeName = node->getName(); - if (!nodeName.endswith(suffix)) { + if (!nodeName.ends_with(suffix)) { PrintFatalError(node->getLoc(), Twine("name of node doesn't end in ") + suffix); } @@ -81,7 +82,7 @@ void PropertyType::emitCXXValueTypeName(bool forRead, raw_ostream &out) const { elementType.emitCXXValueTypeName(forRead, out); out << ">"; } else if (auto valueType = getOptionalElementType()) { - out << "llvm::Optional<"; + out << "std::optional<"; valueType.emitCXXValueTypeName(forRead, out); out << ">"; } else { @@ -107,7 +108,7 @@ static void visitASTNodeRecursive(ASTNode node, ASTNode base, static void visitHierarchy(RecordKeeper &records, StringRef nodeClassName, ASTNodeHierarchyVisitor<ASTNode> visit) { - // Check for the node class, just as a sanity check. + // Check for the node class, just as a basic correctness check. if (!records.getClass(nodeClassName)) { PrintFatalError(Twine("cannot find definition for node class ") + nodeClassName); diff --git a/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.h b/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.h index ab9429f3feee..41f78a6a3bbc 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.h +++ b/contrib/llvm-project/clang/utils/TableGen/ASTTableGen.h @@ -11,6 +11,7 @@ #include "llvm/TableGen/Record.h" #include "llvm/ADT/STLExtras.h" +#include <optional> // These are spellings in the tblgen files. diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangASTNodesEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangASTNodesEmitter.cpp index 2b8d7a9efdf1..07ddafce3291 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangASTNodesEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangASTNodesEmitter.cpp @@ -33,6 +33,7 @@ class ClangASTNodesEmitter { typedef std::multimap<ASTNode, ASTNode> ChildMap; typedef ChildMap::const_iterator ChildIterator; + std::set<ASTNode> PrioritizedClasses; RecordKeeper &Records; ASTNode Root; const std::string &NodeClassName; @@ -70,8 +71,16 @@ class ClangASTNodesEmitter { std::pair<ASTNode, ASTNode> EmitNode(raw_ostream& OS, ASTNode Base); public: explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N, - const std::string &S) - : Records(R), NodeClassName(N), BaseSuffix(S) {} + const std::string &S, + std::string_view PriorizeIfSubclassOf) + : Records(R), NodeClassName(N), BaseSuffix(S) { + auto vecPrioritized = + PriorizeIfSubclassOf.empty() + ? std::vector<Record *>{} + : R.getAllDerivedDefinitions(PriorizeIfSubclassOf); + PrioritizedClasses = + std::set<ASTNode>(vecPrioritized.begin(), vecPrioritized.end()); + } // run - Output the .inc file contents void run(raw_ostream &OS); @@ -95,8 +104,23 @@ std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS, if (!Base.isAbstract()) First = Last = Base; + auto comp = [this](ASTNode LHS, ASTNode RHS) { + auto LHSPrioritized = PrioritizedClasses.count(LHS) > 0; + auto RHSPrioritized = PrioritizedClasses.count(RHS) > 0; + if (LHSPrioritized && !RHSPrioritized) + return true; + if (!LHSPrioritized && RHSPrioritized) + return false; + + return LHS.getName() > RHS.getName(); + }; + auto SortedChildren = std::set<ASTNode, decltype(comp)>(comp); + for (; i != e; ++i) { - ASTNode Child = i->second; + SortedChildren.insert(i->second); + } + + for (const auto &Child : SortedChildren) { bool Abstract = Child.isAbstract(); std::string NodeName = macroName(std::string(Child.getName())); @@ -148,9 +172,7 @@ void ClangASTNodesEmitter::deriveChildTree() { const std::vector<Record*> Stmts = Records.getAllDerivedDefinitions(NodeClassName); - for (unsigned i = 0, e = Stmts.size(); i != e; ++i) { - Record *R = Stmts[i]; - + for (auto *R : Stmts) { if (auto B = R->getValueAsOptionalDef(BaseFieldName)) Tree.insert(std::make_pair(B, R)); else if (Root) @@ -169,7 +191,7 @@ void ClangASTNodesEmitter::deriveChildTree() { void ClangASTNodesEmitter::run(raw_ostream &OS) { deriveChildTree(); - emitSourceFileHeader("List of AST nodes of a particular kind", OS); + emitSourceFileHeader("List of AST nodes of a particular kind", OS, Records); // Write the preamble OS << "#ifndef ABSTRACT_" << macroHierarchyName() << "\n"; @@ -182,9 +204,9 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) { OS << "#endif\n\n"; OS << "#ifndef LAST_" << macroHierarchyName() << "_RANGE\n"; - OS << "# define LAST_" - << macroHierarchyName() << "_RANGE(Base, First, Last) " - << macroHierarchyName() << "_RANGE(Base, First, Last)\n"; + OS << "# define LAST_" << macroHierarchyName() + << "_RANGE(Base, First, Last) " << macroHierarchyName() + << "_RANGE(Base, First, Last)\n"; OS << "#endif\n\n"; EmitNode(OS, Root); @@ -196,8 +218,20 @@ void ClangASTNodesEmitter::run(raw_ostream &OS) { } void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, - const std::string &N, const std::string &S) { - ClangASTNodesEmitter(RK, N, S).run(OS); + const std::string &N, const std::string &S, + std::string_view PriorizeIfSubclassOf) { + ClangASTNodesEmitter(RK, N, S, PriorizeIfSubclassOf).run(OS); +} + +void printDeclContext(const std::multimap<Record *, Record *> &Tree, + Record *DeclContext, raw_ostream &OS) { + if (!DeclContext->getValueAsBit(AbstractFieldName)) + OS << "DECL_CONTEXT(" << DeclContext->getName() << ")\n"; + auto i = Tree.lower_bound(DeclContext); + auto end = Tree.upper_bound(DeclContext); + for (; i != end; ++i) { + printDeclContext(Tree, i->second, OS); + } } // Emits and addendum to a .inc file to enumerate the clang declaration @@ -205,43 +239,30 @@ void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, void clang::EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) { // FIXME: Find a .td file format to allow for this to be represented better. - emitSourceFileHeader("List of AST Decl nodes", OS); + emitSourceFileHeader("List of AST Decl nodes", OS, Records); OS << "#ifndef DECL_CONTEXT\n"; OS << "# define DECL_CONTEXT(DECL)\n"; OS << "#endif\n"; - - OS << "#ifndef DECL_CONTEXT_BASE\n"; - OS << "# define DECL_CONTEXT_BASE(DECL) DECL_CONTEXT(DECL)\n"; - OS << "#endif\n"; - - typedef std::set<Record*> RecordSet; - typedef std::vector<Record*> RecordVector; - - RecordVector DeclContextsVector - = Records.getAllDerivedDefinitions(DeclContextNodeClassName); - RecordVector Decls = Records.getAllDerivedDefinitions(DeclNodeClassName); - RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end()); - - for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) { - Record *R = *i; - - if (Record *B = R->getValueAsOptionalDef(BaseFieldName)) { - if (DeclContexts.find(B) != DeclContexts.end()) { - OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n"; - DeclContexts.erase(B); - } - } + + std::vector<Record *> DeclContextsVector = + Records.getAllDerivedDefinitions(DeclContextNodeClassName); + std::vector<Record *> Decls = + Records.getAllDerivedDefinitions(DeclNodeClassName); + + std::multimap<Record *, Record *> Tree; + + const std::vector<Record *> Stmts = + Records.getAllDerivedDefinitions(DeclNodeClassName); + + for (auto *R : Stmts) { + if (auto *B = R->getValueAsOptionalDef(BaseFieldName)) + Tree.insert(std::make_pair(B, R)); } - // To keep identical order, RecordVector may be used - // instead of RecordSet. - for (RecordVector::iterator - i = DeclContextsVector.begin(), e = DeclContextsVector.end(); - i != e; ++i) - if (DeclContexts.find(*i) != DeclContexts.end()) - OS << "DECL_CONTEXT(" << (*i)->getName() << ")\n"; + for (auto *DeclContext : DeclContextsVector) { + printDeclContext(Tree, DeclContext, OS); + } OS << "#undef DECL_CONTEXT\n"; - OS << "#undef DECL_CONTEXT_BASE\n"; } diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp index caced02e1e11..de8dda60681f 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp @@ -20,6 +20,7 @@ #include "llvm/TableGen/TableGenBackend.h" #include <cctype> #include <map> +#include <optional> #include <set> #include <string> using namespace llvm; @@ -455,7 +456,7 @@ void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node, // Emit code to read all the properties. visitAllProperties(node, nodeInfo, [&](Property prop) { // Verify that the creation code refers to this property. - if (info.IsReader && creationCode.find(prop.getName()) == StringRef::npos) + if (info.IsReader && !creationCode.contains(prop.getName())) PrintFatalError(nodeInfo.Creator.getLoc(), "creation code for " + node.getName() + " doesn't refer to property \"" @@ -525,7 +526,8 @@ void ASTPropsEmitter::emitReadOfProperty(StringRef readerName, // get a pr-value back from read(), and we should be able to forward // that in the creation rule. Out << " "; - if (!condition.empty()) Out << "llvm::Optional<"; + if (!condition.empty()) + Out << "std::optional<"; type.emitCXXValueTypeName(true, Out); if (!condition.empty()) Out << ">"; Out << " " << name; @@ -591,7 +593,7 @@ void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName, template <class NodeClass> static void emitASTReader(RecordKeeper &records, raw_ostream &out, StringRef description) { - emitSourceFileHeader(description, out); + emitSourceFileHeader(description, out, records); ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>(); } @@ -605,7 +607,7 @@ void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) { template <class NodeClass> static void emitASTWriter(RecordKeeper &records, raw_ostream &out, StringRef description) { - emitSourceFileHeader(description, out); + emitSourceFileHeader(description, out, records); ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>(); } @@ -662,9 +664,7 @@ ASTPropsEmitter::emitDispatcherTemplate(const ReaderWriterInfo &info) { declareSpecialization("<class T>", "llvm::ArrayRef<T>", "Array"); - declareSpecialization("<class T>", - "llvm::Optional<T>", - "Optional"); + declareSpecialization("<class T>", "std::optional<T>", "Optional"); Out << "\n"; } @@ -677,15 +677,20 @@ ASTPropsEmitter::emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info) { Out << "template <class ValueType>\n" "struct " << classPrefix << "OptionalValue;\n"; - auto declareSpecialization = [&](const Twine &typeName, - StringRef code) { + auto declareSpecialization = [&](const Twine &typeName, StringRef code) { Out << "template <>\n" - "struct " << classPrefix << "OptionalValue<" << typeName << "> {\n" - " static " << (info.IsReader ? "Optional<" : "") << typeName - << (info.IsReader ? "> " : " ") << methodName << "(" - << (info.IsReader ? "" : "Optional<") << typeName - << (info.IsReader ? "" : ">") << " value) {\n" - " return " << code << ";\n" + "struct " + << classPrefix << "OptionalValue<" << typeName + << "> {\n" + " static " + << (info.IsReader ? "std::optional<" : "") << typeName + << (info.IsReader ? "> " : " ") << methodName << "(" + << (info.IsReader ? "" : "std::optional<") << typeName + << (info.IsReader ? "" : ">") + << " value) {\n" + " return " + << code + << ";\n" " }\n" "};\n"; }; @@ -847,7 +852,7 @@ void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) { /// Emit an .inc file that defines some helper classes for reading /// basic values. void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) { - emitSourceFileHeader("Helper classes for BasicReaders", out); + emitSourceFileHeader("Helper classes for BasicReaders", out, records); // Use any property, we won't be using those properties. auto info = ReaderWriterInfo::forReader<TypeNode>(); @@ -857,7 +862,7 @@ void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) { /// Emit an .inc file that defines some helper classes for writing /// basic values. void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) { - emitSourceFileHeader("Helper classes for BasicWriters", out); + emitSourceFileHeader("Helper classes for BasicWriters", out, records); // Use any property, we won't be using those properties. auto info = ReaderWriterInfo::forWriter<TypeNode>(); diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangAttrEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangAttrEmitter.cpp index d679d58aaef1..89b88e386f25 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -36,6 +37,7 @@ #include <cstdint> #include <map> #include <memory> +#include <optional> #include <set> #include <sstream> #include <string> @@ -49,17 +51,21 @@ namespace { class FlattenedSpelling { std::string V, N, NS; bool K = false; + const Record &OriginalSpelling; public: FlattenedSpelling(const std::string &Variety, const std::string &Name, - const std::string &Namespace, bool KnownToGCC) : - V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {} + const std::string &Namespace, bool KnownToGCC, + const Record &OriginalSpelling) + : V(Variety), N(Name), NS(Namespace), K(KnownToGCC), + OriginalSpelling(OriginalSpelling) {} explicit FlattenedSpelling(const Record &Spelling) : V(std::string(Spelling.getValueAsString("Variety"))), - N(std::string(Spelling.getValueAsString("Name"))) { + N(std::string(Spelling.getValueAsString("Name"))), + OriginalSpelling(Spelling) { assert(V != "GCC" && V != "Clang" && "Given a GCC spelling, which means this hasn't been flattened!"); - if (V == "CXX11" || V == "C2x" || V == "Pragma") + if (V == "CXX11" || V == "C23" || V == "Pragma") NS = std::string(Spelling.getValueAsString("Namespace")); } @@ -67,6 +73,7 @@ public: const std::string &name() const { return N; } const std::string &nameSpace() const { return NS; } bool knownToGCC() const { return K; } + const Record &getSpellingRecord() const { return OriginalSpelling; } }; } // end anonymous namespace @@ -80,15 +87,15 @@ GetFlattenedSpellings(const Record &Attr) { StringRef Variety = Spelling->getValueAsString("Variety"); StringRef Name = Spelling->getValueAsString("Name"); if (Variety == "GCC") { - Ret.emplace_back("GNU", std::string(Name), "", true); - Ret.emplace_back("CXX11", std::string(Name), "gnu", true); + Ret.emplace_back("GNU", std::string(Name), "", true, *Spelling); + Ret.emplace_back("CXX11", std::string(Name), "gnu", true, *Spelling); if (Spelling->getValueAsBit("AllowInC")) - Ret.emplace_back("C2x", std::string(Name), "gnu", true); + Ret.emplace_back("C23", std::string(Name), "gnu", true, *Spelling); } else if (Variety == "Clang") { - Ret.emplace_back("GNU", std::string(Name), "", false); - Ret.emplace_back("CXX11", std::string(Name), "clang", false); + Ret.emplace_back("GNU", std::string(Name), "", false, *Spelling); + Ret.emplace_back("CXX11", std::string(Name), "clang", false, *Spelling); if (Spelling->getValueAsBit("AllowInC")) - Ret.emplace_back("C2x", std::string(Name), "clang", false); + Ret.emplace_back("C23", std::string(Name), "clang", false, *Spelling); } else Ret.push_back(FlattenedSpelling(*Spelling)); } @@ -154,7 +161,7 @@ static StringRef NormalizeNameForSpellingComparison(StringRef Name) { // Normalize the spelling of a GNU attribute (i.e. "x" in "__attribute__((x))"), // removing "__" if it appears at the beginning and end of the attribute's name. static StringRef NormalizeGNUAttrSpelling(StringRef AttrSpelling) { - if (AttrSpelling.startswith("__") && AttrSpelling.endswith("__")) { + if (AttrSpelling.starts_with("__") && AttrSpelling.ends_with("__")) { AttrSpelling = AttrSpelling.substr(2, AttrSpelling.size() - 4); } @@ -201,9 +208,9 @@ namespace { bool Fake; public: - Argument(const Record &Arg, StringRef Attr) - : lowerName(std::string(Arg.getValueAsString("Name"))), - upperName(lowerName), attrName(Attr), isOpt(false), Fake(false) { + Argument(StringRef Arg, StringRef Attr) + : lowerName(std::string(Arg)), upperName(lowerName), attrName(Attr), + isOpt(false), Fake(false) { if (!lowerName.empty()) { lowerName[0] = std::tolower(lowerName[0]); upperName[0] = std::toupper(upperName[0]); @@ -214,6 +221,8 @@ namespace { if (lowerName == "interface") lowerName = "interface_"; } + Argument(const Record &Arg, StringRef Attr) + : Argument(Arg.getValueAsString("Name"), Attr) {} virtual ~Argument() = default; StringRef getLowerName() const { return lowerName; } @@ -311,12 +320,19 @@ namespace { } std::string getIsOmitted() const override { - if (type == "IdentifierInfo *") + auto IsOneOf = [](StringRef subject, auto... list) { + return ((subject == list) || ...); + }; + + if (IsOneOf(type, "IdentifierInfo *", "Expr *")) return "!get" + getUpperName().str() + "()"; - if (type == "TypeSourceInfo *") + if (IsOneOf(type, "TypeSourceInfo *")) return "!get" + getUpperName().str() + "Loc()"; - if (type == "ParamIdx") + if (IsOneOf(type, "ParamIdx")) return "!get" + getUpperName().str() + "().isValid()"; + + assert(IsOneOf(type, "unsigned", "int", "bool", "FunctionDecl *", + "VarDecl *")); return "false"; } @@ -340,7 +356,7 @@ namespace { } void writeDump(raw_ostream &OS) const override { - if (StringRef(type).endswith("Decl *")) { + if (StringRef(type).ends_with("Decl *")) { OS << " OS << \" \";\n"; OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n"; } else if (type == "IdentifierInfo *") { @@ -499,6 +515,16 @@ namespace { OS << " assert(!is" << getLowerName() << "Expr);\n"; OS << " return " << getLowerName() << "Type;\n"; OS << " }"; + + OS << " std::optional<unsigned> getCached" << getUpperName() + << "Value() const {\n"; + OS << " return " << getLowerName() << "Cache;\n"; + OS << " }"; + + OS << " void setCached" << getUpperName() + << "Value(unsigned AlignVal) {\n"; + OS << " " << getLowerName() << "Cache = AlignVal;\n"; + OS << " }"; } void writeAccessorDefinitions(raw_ostream &OS) const override { @@ -521,21 +547,6 @@ namespace { 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 - OS << "unsigned " << getAttrName() << "Attr::get" << getUpperName() - << "(ASTContext &Ctx) const {\n"; - OS << " assert(!is" << getUpperName() << "Dependent());\n"; - OS << " if (is" << getLowerName() << "Expr)\n"; - OS << " return " << getLowerName() << "Expr ? " << getLowerName() - << "Expr->EvaluateKnownConstInt(Ctx).getZExtValue()" - << " * Ctx.getCharWidth() : " - << "Ctx.getTargetDefaultAlignForAttributeAligned();\n"; - OS << " else\n"; - OS << " return 0; // FIXME\n"; - OS << "}\n"; } void writeASTVisitorTraversal(raw_ostream &OS) const override { @@ -592,7 +603,8 @@ namespace { OS << "union {\n"; OS << "Expr *" << getLowerName() << "Expr;\n"; OS << "TypeSourceInfo *" << getLowerName() << "Type;\n"; - OS << "};"; + OS << "};\n"; + OS << "std::optional<unsigned> " << getLowerName() << "Cache;\n"; } void writePCHReadArgs(raw_ostream &OS) const override { @@ -619,14 +631,21 @@ namespace { } std::string getIsOmitted() const override { - return "!is" + getLowerName().str() + "Expr || !" + getLowerName().str() - + "Expr"; + return "!((is" + getLowerName().str() + "Expr && " + + getLowerName().str() + "Expr) || (!is" + getLowerName().str() + + "Expr && " + getLowerName().str() + "Type))"; } void writeValue(raw_ostream &OS) const override { OS << "\";\n"; - OS << " " << getLowerName() + OS << " if (is" << getLowerName() << "Expr && " << getLowerName() + << "Expr)"; + OS << " " << getLowerName() << "Expr->printPretty(OS, nullptr, Policy);\n"; + OS << " if (!is" << getLowerName() << "Expr && " << getLowerName() + << "Type)"; + OS << " " << getLowerName() + << "Type->getType().print(OS, Policy);\n"; OS << " OS << \""; } @@ -665,6 +684,11 @@ namespace { ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), RangeName(std::string(getLowerName())) {} + VariadicArgument(StringRef Arg, StringRef Attr, std::string T) + : Argument(Arg, Attr), Type(std::move(T)), + ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), + RangeName(std::string(getLowerName())) {} + const std::string &getType() const { return Type; } const std::string &getArgName() const { return ArgName; } const std::string &getArgSizeName() const { return ArgSizeName; } @@ -687,6 +711,18 @@ namespace { << "); }\n"; } + void writeSetter(raw_ostream &OS) const { + OS << " void set" << getUpperName() << "(ASTContext &Ctx, "; + writeCtorParameters(OS); + OS << ") {\n"; + OS << " " << ArgSizeName << " = " << getUpperName() << "Size;\n"; + OS << " " << ArgName << " = new (Ctx, 16) " << getType() << "[" + << ArgSizeName << "];\n"; + OS << " "; + writeCtorBody(OS); + OS << " }\n"; + } + void writeCloneArgs(raw_ostream &OS) const override { OS << ArgName << ", " << ArgSizeName; } @@ -786,6 +822,49 @@ namespace { } }; + class VariadicOMPInteropInfoArgument : public VariadicArgument { + public: + VariadicOMPInteropInfoArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "OMPInteropInfo") {} + + void writeDump(raw_ostream &OS) const override { + OS << " for (" << getAttrName() << "Attr::" << getLowerName() + << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" + << getLowerName() << "_end(); I != E; ++I) {\n"; + OS << " if (I->IsTarget && I->IsTargetSync)\n"; + OS << " OS << \" Target_TargetSync\";\n"; + OS << " else if (I->IsTarget)\n"; + OS << " OS << \" Target\";\n"; + OS << " else\n"; + OS << " OS << \" TargetSync\";\n"; + OS << " }\n"; + } + + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; + OS << " SmallVector<OMPInteropInfo, 4> " << getLowerName() << ";\n"; + OS << " " << getLowerName() << ".reserve(" << getLowerName() + << "Size);\n"; + OS << " for (unsigned I = 0, E = " << getLowerName() << "Size; "; + OS << "I != E; ++I) {\n"; + OS << " bool IsTarget = Record.readBool();\n"; + OS << " bool IsTargetSync = Record.readBool();\n"; + OS << " " << getLowerName() + << ".emplace_back(IsTarget, IsTargetSync);\n"; + OS << " }\n"; + } + + void writePCHWrite(raw_ostream &OS) const override { + OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; + OS << " for (" << getAttrName() << "Attr::" << getLowerName() + << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" + << getLowerName() << "_end(); I != E; ++I) {\n"; + OS << " Record.writeBool(I->IsTarget);\n"; + OS << " Record.writeBool(I->IsTargetSync);\n"; + OS << " }\n"; + } + }; + class VariadicParamIdxArgument : public VariadicArgument { public: VariadicParamIdxArgument(const Record &Arg, StringRef Attr) @@ -819,15 +898,25 @@ namespace { } class EnumArgument : public Argument { - std::string type; + std::string fullType; + StringRef shortType; std::vector<StringRef> values, enums, uniques; + bool isExternal; public: EnumArgument(const Record &Arg, StringRef Attr) - : Argument(Arg, Attr), type(std::string(Arg.getValueAsString("Type"))), - values(Arg.getValueAsListOfStrings("Values")), + : Argument(Arg, Attr), values(Arg.getValueAsListOfStrings("Values")), enums(Arg.getValueAsListOfStrings("Enums")), - uniques(uniqueEnumsInOrder(enums)) { + uniques(uniqueEnumsInOrder(enums)), + isExternal(Arg.getValueAsBit("IsExternalType")) { + StringRef Type = Arg.getValueAsString("Type"); + shortType = isExternal ? Type.rsplit("::").second : Type; + // If shortType didn't contain :: at all rsplit will give us an empty + // string. + if (shortType.empty()) + shortType = Type; + fullType = isExternal ? Type : (getAttrName() + "Attr::" + Type).str(); + // FIXME: Emit a proper error assert(!uniques.empty()); } @@ -835,7 +924,7 @@ namespace { bool isEnumArg() const override { return true; } void writeAccessors(raw_ostream &OS) const override { - OS << " " << type << " get" << getUpperName() << "() const {\n"; + OS << " " << fullType << " get" << getUpperName() << "() const {\n"; OS << " return " << getLowerName() << ";\n"; OS << " }"; } @@ -851,30 +940,32 @@ namespace { OS << getLowerName() << "(" << getUpperName() << ")"; } void writeCtorDefaultInitializers(raw_ostream &OS) const override { - OS << getLowerName() << "(" << type << "(0))"; + OS << getLowerName() << "(" << fullType << "(0))"; } void writeCtorParameters(raw_ostream &OS) const override { - OS << type << " " << getUpperName(); + OS << fullType << " " << getUpperName(); } void writeDeclarations(raw_ostream &OS) const override { - auto i = uniques.cbegin(), e = uniques.cend(); - // The last one needs to not have a comma. - --e; + if (!isExternal) { + auto i = uniques.cbegin(), e = uniques.cend(); + // The last one needs to not have a comma. + --e; + + OS << "public:\n"; + OS << " enum " << shortType << " {\n"; + for (; i != e; ++i) + OS << " " << *i << ",\n"; + OS << " " << *e << "\n"; + OS << " };\n"; + } - OS << "public:\n"; - OS << " enum " << type << " {\n"; - for (; i != e; ++i) - OS << " " << *i << ",\n"; - OS << " " << *e << "\n"; - OS << " };\n"; OS << "private:\n"; - OS << " " << type << " " << getLowerName() << ";"; + OS << " " << fullType << " " << getLowerName() << ";"; } void writePCHReadDecls(raw_ostream &OS) const override { - OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName() - << "(static_cast<" << getAttrName() << "Attr::" << type - << ">(Record.readInt()));\n"; + OS << " " << fullType << " " << getLowerName() << "(static_cast<" + << fullType << ">(Record.readInt()));\n"; } void writePCHReadArgs(raw_ostream &OS) const override { @@ -882,45 +973,50 @@ namespace { } void writePCHWrite(raw_ostream &OS) const override { - OS << "Record.push_back(SA->get" << getUpperName() << "());\n"; + OS << "Record.push_back(static_cast<uint64_t>(SA->get" << getUpperName() + << "()));\n"; } void writeValue(raw_ostream &OS) const override { // FIXME: this isn't 100% correct -- some enum arguments require printing // as a string literal, while others require printing as an identifier. // Tablegen currently does not distinguish between the two forms. - OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << type << "ToStr(get" - << getUpperName() << "()) << \"\\\""; + OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << shortType + << "ToStr(get" << getUpperName() << "()) << \"\\\""; } void writeDump(raw_ostream &OS) const override { OS << " switch(SA->get" << getUpperName() << "()) {\n"; for (const auto &I : uniques) { - OS << " case " << getAttrName() << "Attr::" << I << ":\n"; + OS << " case " << fullType << "::" << I << ":\n"; OS << " OS << \" " << I << "\";\n"; OS << " break;\n"; } + if (isExternal) { + OS << " default:\n"; + OS << " llvm_unreachable(\"Invalid attribute value\");\n"; + } OS << " }\n"; } 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"; + OS << " static bool ConvertStrTo" << shortType << "(StringRef Val, " + << fullType << " &Out);\n"; + OS << " static const char *Convert" << shortType << "ToStr(" + << fullType << " 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"; + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << shortType + << "(StringRef Val, " << fullType << " &Out) {\n"; + OS << " std::optional<" << fullType << "> " + << "R = llvm::StringSwitch<std::optional<" << fullType << ">>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { OS << " .Case(\"" << values[I] << "\", "; - OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + OS << fullType << "::" << enums[I] << ")\n"; } - OS << " .Default(Optional<" << type << ">());\n"; + OS << " .Default(std::optional<" << fullType << ">());\n"; OS << " if (R) {\n"; OS << " Out = *R;\n return true;\n }\n"; OS << " return false;\n"; @@ -930,14 +1026,17 @@ namespace { // trivial because some enumeration values have multiple named // enumerators, such as type_visibility(internal) and // type_visibility(hidden) both mapping to TypeVisibilityAttr::Hidden. - OS << "const char *" << getAttrName() << "Attr::Convert" << type - << "ToStr(" << type << " Val) {\n" + OS << "const char *" << getAttrName() << "Attr::Convert" << shortType + << "ToStr(" << fullType << " 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 " << fullType << "::" << enums[I] << ": return \"" + << values[I] << "\";\n"; + } + if (isExternal) { + OS << " default: llvm_unreachable(\"Invalid attribute value\");\n"; } OS << " }\n" << " llvm_unreachable(\"No enumerator with that value\");\n" @@ -946,27 +1045,36 @@ namespace { }; class VariadicEnumArgument: public VariadicArgument { - std::string type, QualifiedTypeName; + std::string fullType; + StringRef shortType; std::vector<StringRef> values, enums, uniques; + bool isExternal; protected: void writeValueImpl(raw_ostream &OS) const override { // FIXME: this isn't 100% correct -- some enum arguments require printing // as a string literal, while others require printing as an identifier. // Tablegen currently does not distinguish between the two forms. - OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" << type - << "ToStr(Val)" << "<< \"\\\"\";\n"; + OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" + << shortType << "ToStr(Val)" + << "<< \"\\\"\";\n"; } public: VariadicEnumArgument(const Record &Arg, StringRef Attr) : 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; + uniques(uniqueEnumsInOrder(enums)), + isExternal(Arg.getValueAsBit("IsExternalType")) { + StringRef Type = Arg.getValueAsString("Type"); + shortType = isExternal ? Type.rsplit("::").second : Type; + // If shortType didn't contain :: at all rsplit will give us an empty + // string. + if (shortType.empty()) + shortType = Type; + fullType = isExternal ? Type : (getAttrName() + "Attr::" + Type).str(); // FIXME: Emit a proper error assert(!uniques.empty()); @@ -975,16 +1083,18 @@ namespace { 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. - --e; - - OS << "public:\n"; - OS << " enum " << type << " {\n"; - for (; i != e; ++i) - OS << " " << *i << ",\n"; - OS << " " << *e << "\n"; - OS << " };\n"; + if (!isExternal) { + auto i = uniques.cbegin(), e = uniques.cend(); + // The last one needs to not have a comma. + --e; + + OS << "public:\n"; + OS << " enum " << shortType << " {\n"; + for (; i != e; ++i) + OS << " " << *i << ",\n"; + OS << " " << *e << "\n"; + OS << " };\n"; + } OS << "private:\n"; VariadicArgument::writeDeclarations(OS); @@ -996,7 +1106,7 @@ namespace { << getLowerName() << "_end(); I != E; ++I) {\n"; OS << " switch(*I) {\n"; for (const auto &UI : uniques) { - OS << " case " << getAttrName() << "Attr::" << UI << ":\n"; + OS << " case " << fullType << "::" << UI << ":\n"; OS << " OS << \" " << UI << "\";\n"; OS << " break;\n"; } @@ -1006,13 +1116,13 @@ namespace { void writePCHReadDecls(raw_ostream &OS) const override { OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n"; - OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName() + OS << " SmallVector<" << fullType << ", 4> " << getLowerName() << ";\n"; OS << " " << getLowerName() << ".reserve(" << getLowerName() << "Size);\n"; OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; - OS << " " << getLowerName() << ".push_back(" << "static_cast<" - << QualifiedTypeName << ">(Record.readInt()));\n"; + OS << " " << getLowerName() << ".push_back(" + << "static_cast<" << fullType << ">(Record.readInt()));\n"; } void writePCHWrite(raw_ostream &OS) const override { @@ -1020,41 +1130,42 @@ namespace { OS << " for (" << getAttrName() << "Attr::" << getLowerName() << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->" << getLowerName() << "_end(); i != e; ++i)\n"; - OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)"); + OS << " " << WritePCHRecord(fullType, "(*i)"); } 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"; + OS << " static bool ConvertStrTo" << shortType << "(StringRef Val, " + << fullType << " &Out);\n"; + OS << " static const char *Convert" << shortType << "ToStr(" + << fullType << " Val);\n"; return; } - OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type + OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << shortType << "(StringRef Val, "; - OS << type << " &Out) {\n"; - OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; - OS << type << ">>(Val)\n"; + OS << fullType << " &Out) {\n"; + OS << " std::optional<" << fullType + << "> R = llvm::StringSwitch<std::optional<"; + OS << fullType << ">>(Val)\n"; for (size_t I = 0; I < enums.size(); ++I) { OS << " .Case(\"" << values[I] << "\", "; - OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + OS << fullType << "::" << enums[I] << ")\n"; } - OS << " .Default(Optional<" << type << ">());\n"; + OS << " .Default(std::optional<" << fullType << ">());\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" + OS << "const char *" << getAttrName() << "Attr::Convert" << shortType + << "ToStr(" << fullType << " 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 " << fullType << "::" << enums[I] << ": return \"" + << values[I] << "\";\n"; } OS << " }\n" << " llvm_unreachable(\"No enumerator with that value\");\n" @@ -1153,6 +1264,13 @@ namespace { OS << " }\n"; } + void writeValue(raw_ostream &OS) const override { + OS << "\";\n"; + OS << " get" << getUpperName() + << "()->printPretty(OS, nullptr, Policy);\n"; + OS << " OS << \""; + } + void writeDump(raw_ostream &OS) const override {} void writeDumpChildren(raw_ostream &OS) const override { @@ -1168,6 +1286,9 @@ namespace { : VariadicArgument(Arg, Attr, "Expr *") {} + VariadicExprArgument(StringRef ArgName, StringRef Attr) + : VariadicArgument(ArgName, Attr, "Expr *") {} + void writeASTVisitorTraversal(raw_ostream &OS) const override { OS << " {\n"; OS << " " << getType() << " *I = A->" << getLowerName() @@ -1293,7 +1414,29 @@ namespace { } }; -} // end anonymous namespace + class WrappedAttr : public SimpleArgument { + public: + WrappedAttr(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Attr *") {} + + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " Attr *" << getLowerName() << " = Record.readAttr();"; + } + + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddAttr(SA->get" << getUpperName() << "());"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " Visit(SA->get" << getUpperName() << "());\n"; + } + + void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + }; + + } // end anonymous namespace static std::unique_ptr<Argument> createArgument(const Record &Arg, StringRef Attr, @@ -1349,8 +1492,12 @@ 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 == "WrappedAttr") + Ptr = std::make_unique<WrappedAttr>(Arg, Attr); else if (ArgName == "OMPTraitInfoArgument") Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *"); + else if (ArgName == "VariadicOMPInteropInfoArgument") + Ptr = std::make_unique<VariadicOMPInteropInfoArgument>(Arg, Attr); if (!Ptr) { // Search in reverse order so that the most-derived type is handled first. @@ -1445,7 +1592,7 @@ writePrettyPrintFunction(const Record &R, if (Variety == "GNU") { Prefix = " __attribute__(("; Suffix = "))"; - } else if (Variety == "CXX11" || Variety == "C2x") { + } else if (Variety == "CXX11" || Variety == "C23") { Prefix = " [["; Suffix = "]]"; std::string Namespace = Spellings[I].nameSpace(); @@ -1470,6 +1617,9 @@ writePrettyPrintFunction(const Record &R, Spelling += Namespace; Spelling += " "; } + } else if (Variety == "HLSLSemantic") { + Prefix = ":"; + Suffix = ""; } else { llvm_unreachable("Unknown attribute syntax variety!"); } @@ -1499,12 +1649,10 @@ writePrettyPrintFunction(const Record &R, // To avoid printing parentheses around an empty argument list or // printing spurious commas at the end of an argument list, we need to // determine where the last provided non-fake argument is. - unsigned NonFakeArgs = 0; bool FoundNonOptArg = false; for (const auto &arg : llvm::reverse(Args)) { if (arg->isFake()) continue; - ++NonFakeArgs; if (FoundNonOptArg) continue; // FIXME: arg->getIsOmitted() == "false" means we haven't implemented @@ -1598,8 +1746,7 @@ SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) { assert(!Spellings.empty() && "An empty list of spellings was provided"); std::string FirstName = std::string(NormalizeNameForSpellingComparison(Spellings.front().name())); - for (const auto &Spelling : - llvm::make_range(std::next(Spellings.begin()), Spellings.end())) { + for (const auto &Spelling : llvm::drop_begin(Spellings)) { std::string Name = std::string(NormalizeNameForSpellingComparison(Spelling.name())); if (Name != FirstName) @@ -1772,7 +1919,7 @@ struct AttributeSubjectMatchRule { } if (isAbstractRule()) Result += "_abstract"; - return std::string(Result.str()); + return std::string(Result); } std::string getEnumValue() const { return "attr::" + getEnumValueName(); } @@ -1969,7 +2116,7 @@ bool PragmaClangAttributeSupport::isAttributedSupported( for (const auto *Subject : Subjects) { if (!isSupportedPragmaClangAttributeSubject(*Subject)) continue; - if (SubjectsToRules.find(Subject) == SubjectsToRules.end()) + if (!SubjectsToRules.contains(Subject)) return false; HasAtLeastOneValidSubject = true; } @@ -2038,12 +2185,12 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { // Generate routines that check the names of sub-rules. - OS << "Optional<attr::SubjectMatchRule> " + OS << "std::optional<attr::SubjectMatchRule> " "defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n"; - OS << " return None;\n"; + OS << " return std::nullopt;\n"; OS << "}\n\n"; - std::map<const Record *, std::vector<AttributeSubjectMatchRule>> + llvm::MapVector<const Record *, std::vector<AttributeSubjectMatchRule>> SubMatchRules; for (const auto &Rule : Rules) { if (!Rule.isSubRule()) @@ -2052,36 +2199,37 @@ void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { } for (const auto &SubMatchRule : SubMatchRules) { - OS << "Optional<attr::SubjectMatchRule> isAttributeSubjectMatchSubRuleFor_" + OS << "std::optional<attr::SubjectMatchRule> " + "isAttributeSubjectMatchSubRuleFor_" << SubMatchRule.first->getValueAsString("Name") << "(StringRef Name, bool IsUnless) {\n"; OS << " if (IsUnless)\n"; OS << " return " - "llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n"; + "llvm::StringSwitch<std::optional<attr::SubjectMatchRule>>(Name).\n"; for (const auto &Rule : SubMatchRule.second) { if (Rule.isNegatedSubRule()) OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() << ").\n"; } - OS << " Default(None);\n"; + OS << " Default(std::nullopt);\n"; OS << " return " - "llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n"; + "llvm::StringSwitch<std::optional<attr::SubjectMatchRule>>(Name).\n"; for (const auto &Rule : SubMatchRule.second) { if (!Rule.isNegatedSubRule()) OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue() << ").\n"; } - OS << " Default(None);\n"; + OS << " Default(std::nullopt);\n"; OS << "}\n\n"; } // Generate the function that checks for the top-level rules. - OS << "std::pair<Optional<attr::SubjectMatchRule>, " - "Optional<attr::SubjectMatchRule> (*)(StringRef, " + OS << "std::pair<std::optional<attr::SubjectMatchRule>, " + "std::optional<attr::SubjectMatchRule> (*)(StringRef, " "bool)> isAttributeSubjectMatchRule(StringRef Name) {\n"; OS << " return " - "llvm::StringSwitch<std::pair<Optional<attr::SubjectMatchRule>, " - "Optional<attr::SubjectMatchRule> (*) (StringRef, " + "llvm::StringSwitch<std::pair<std::optional<attr::SubjectMatchRule>, " + "std::optional<attr::SubjectMatchRule> (*) (StringRef, " "bool)>>(Name).\n"; for (const auto &Rule : Rules) { if (Rule.isSubRule()) @@ -2095,7 +2243,7 @@ void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { OS << " Case(\"" << Rule.getName() << "\", std::make_pair(" << Rule.getEnumValue() << ", " << SubRuleFunction << ")).\n"; } - OS << " Default(std::make_pair(None, " + OS << " Default(std::make_pair(std::nullopt, " "defaultIsAttributeSubjectMatchSubRuleFor));\n"; OS << "}\n\n"; @@ -2137,6 +2285,11 @@ static void forEachUniqueSpelling(const Record &Attr, Fn &&F) { } } +static bool isTypeArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + Arg->getSuperClasses().back().first->getName() == "TypeArgument"; +} + /// Emits the first-argument-is-type property for attributes. static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_TYPE_ARG_LIST)\n"; @@ -2148,7 +2301,7 @@ static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) { if (Args.empty()) continue; - if (Args[0]->getSuperClasses().back().first->getName() != "TypeArgument") + if (!isTypeArgument(Args[0])) continue; // All these spellings take a single type argument. @@ -2178,7 +2331,7 @@ static void emitClangAttrArgContextList(RecordKeeper &Records, raw_ostream &OS) OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n"; } -static bool isIdentifierArgument(Record *Arg) { +static bool isIdentifierArgument(const Record *Arg) { return !Arg->getSuperClasses().empty() && llvm::StringSwitch<bool>(Arg->getSuperClasses().back().first->getName()) .Case("IdentifierArgument", true) @@ -2187,7 +2340,7 @@ static bool isIdentifierArgument(Record *Arg) { .Default(false); } -static bool isVariadicIdentifierArgument(Record *Arg) { +static bool isVariadicIdentifierArgument(const Record *Arg) { return !Arg->getSuperClasses().empty() && llvm::StringSwitch<bool>( Arg->getSuperClasses().back().first->getName()) @@ -2196,6 +2349,30 @@ static bool isVariadicIdentifierArgument(Record *Arg) { .Default(false); } +static bool isVariadicExprArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch<bool>( + Arg->getSuperClasses().back().first->getName()) + .Case("VariadicExprArgument", true) + .Default(false); +} + +static bool isStringLiteralArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch<bool>( + Arg->getSuperClasses().back().first->getName()) + .Case("StringArgument", true) + .Default(false); +} + +static bool isVariadicStringLiteralArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch<bool>( + Arg->getSuperClasses().back().first->getName()) + .Case("VariadicStringArgument", true) + .Default(false); +} + static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n"; @@ -2216,6 +2393,34 @@ static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n"; } +// Emits the list of arguments that should be parsed as unevaluated string +// literals for each attribute. +static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST)\n"; + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + for (const auto *Attr : Attrs) { + std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args"); + uint32_t Bits = 0; + assert(Args.size() <= 32 && "unsupported number of arguments in attribute"); + for (uint32_t N = 0; N < Args.size(); ++N) { + Bits |= (isStringLiteralArgument(Args[N]) << N); + // If we have a variadic string argument, set all the remaining bits to 1 + if (isVariadicStringLiteralArgument(Args[N])) { + Bits |= maskTrailingZeros<decltype(Bits)>(N); + break; + } + } + if (!Bits) + continue; + // All these spellings have at least one string literal has argument. + forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", " << Bits << ")\n"; + }); + } + OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n"; +} + // Emits the first-argument-is-identifier property for attributes. static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n"; @@ -2263,6 +2468,40 @@ static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records, OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; } +static void emitClangAttrAcceptsExprPack(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK)\n"; + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &Attr = *I.second; + + if (!Attr.getValueAsBit("AcceptsExprPack")) + continue; + + forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", true)\n"; + }); + } + OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n"; +} + +static bool isRegularKeywordAttribute(const FlattenedSpelling &S) { + return (S.variety() == "Keyword" && + !S.getSpellingRecord().getValueAsBit("HasOwnParseRules")); +} + +static void emitFormInitializer(raw_ostream &OS, + const FlattenedSpelling &Spelling, + StringRef SpellingIndex) { + bool IsAlignas = + (Spelling.variety() == "Keyword" && Spelling.name() == "alignas"); + OS << "{AttributeCommonInfo::AS_" << Spelling.variety() << ", " + << SpellingIndex << ", " << (IsAlignas ? "true" : "false") + << " /*IsAlignas*/, " + << (isRegularKeywordAttribute(Spelling) ? "true" : "false") + << " /*IsRegularKeywordAttribute*/}"; +} + static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, bool Header) { std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); @@ -2319,6 +2558,25 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, std::vector<std::unique_ptr<Argument>> Args; Args.reserve(ArgRecords.size()); + bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack"); + if (AttrAcceptsExprPack) { + for (size_t I = 0; I < ArgRecords.size(); ++I) { + const Record *ArgR = ArgRecords[I]; + if (isIdentifierArgument(ArgR) || isVariadicIdentifierArgument(ArgR) || + isTypeArgument(ArgR)) + PrintFatalError(Attr->getLoc(), + "Attributes accepting packs cannot also " + "have identifier or type arguments."); + // When trying to determine if value-dependent expressions can populate + // the attribute without prior instantiation, the decision is made based + // on the assumption that only the last argument is ever variadic. + if (I < (ArgRecords.size() - 1) && isVariadicExprArgument(ArgR)) + PrintFatalError(Attr->getLoc(), + "Attributes accepting packs can only have the last " + "argument be variadic."); + } + } + bool HasOptArg = false; bool HasFakeArg = false; for (const auto *ArgRecord : ArgRecords) { @@ -2336,6 +2594,16 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, } } + std::unique_ptr<VariadicExprArgument> DelayedArgs = nullptr; + if (AttrAcceptsExprPack) { + DelayedArgs = + std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName()); + if (Header) { + DelayedArgs->writeDeclarations(OS); + OS << "\n\n"; + } + } + if (Header) OS << "public:\n"; @@ -2362,7 +2630,7 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, }); // Emit CreateImplicit factory methods. - auto emitCreate = [&](bool Implicit, bool emitFake) { + auto emitCreate = [&](bool Implicit, bool DelayedArgsOnly, bool emitFake) { if (Header) OS << " static "; OS << R.getName() << "Attr *"; @@ -2371,16 +2639,22 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, OS << "Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "("; OS << "ASTContext &Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeCtorParameters(OS); + } + } else { OS << ", "; - ai->writeCtorParameters(OS); + DelayedArgs->writeCtorParameters(OS); } OS << ", const AttributeCommonInfo &CommonInfo"; - if (Header && Implicit) - OS << " = {SourceRange{}}"; OS << ")"; if (Header) { OS << ";\n"; @@ -2390,10 +2664,14 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, OS << " {\n"; OS << " auto *A = new (Ctx) " << R.getName(); OS << "Attr(Ctx, CommonInfo"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; - OS << ", "; - ai->writeImplicitCtorArgs(OS); + + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeImplicitCtorArgs(OS); + } } OS << ");\n"; if (Implicit) { @@ -2404,10 +2682,16 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, "!A->getAttrName())\n"; OS << " A->setAttributeSpellingListIndex(0);\n"; } + if (DelayedArgsOnly) { + OS << " A->setDelayedArgs(Ctx, "; + DelayedArgs->writeImplicitCtorArgs(OS); + OS << ");\n"; + } OS << " return A;\n}\n\n"; }; - auto emitCreateNoCI = [&](bool Implicit, bool emitFake) { + auto emitCreateNoCI = [&](bool Implicit, bool DelayedArgsOnly, + bool emitFake) { if (Header) OS << " static "; OS << R.getName() << "Attr *"; @@ -2416,18 +2700,28 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, OS << "Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "("; OS << "ASTContext &Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeCtorParameters(OS); + } + } else { OS << ", "; - ai->writeCtorParameters(OS); + DelayedArgs->writeCtorParameters(OS); } - OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax"; - if (!ElideSpelling) { - OS << ", " << R.getName() << "Attr::Spelling S"; + OS << ", SourceRange Range"; + if (Header) + OS << " = {}"; + if (Spellings.size() > 1) { + OS << ", Spelling S"; if (Header) - OS << " = static_cast<Spelling>(SpellingNotCalculated)"; + OS << " = " << SemanticToSyntacticMap[0]; } OS << ")"; if (Header) { @@ -2443,45 +2737,89 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, else OS << "NoSemaHandlerAttribute"; - OS << ", Syntax"; - if (!ElideSpelling) - OS << ", S"; + if (Spellings.size() == 0) { + OS << ", AttributeCommonInfo::Form::Implicit()"; + } else if (Spellings.size() == 1) { + OS << ", "; + emitFormInitializer(OS, Spellings[0], "0"); + } else { + OS << ", [&]() {\n"; + OS << " switch (S) {\n"; + std::set<std::string> Uniques; + unsigned Idx = 0; + for (auto I = Spellings.begin(), E = Spellings.end(); I != E; + ++I, ++Idx) { + const FlattenedSpelling &S = *I; + const auto &Name = SemanticToSyntacticMap[Idx]; + if (Uniques.insert(Name).second) { + OS << " case " << Name << ":\n"; + OS << " return AttributeCommonInfo::Form"; + emitFormInitializer(OS, S, Name); + OS << ";\n"; + } + } + OS << " default:\n"; + OS << " llvm_unreachable(\"Unknown attribute spelling!\");\n" + << " return AttributeCommonInfo::Form"; + emitFormInitializer(OS, Spellings[0], "0"); + OS << ";\n" + << " }\n" + << " }()"; + } + OS << ");\n"; OS << " return Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "(Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeImplicitCtorArgs(OS); + } + } else { OS << ", "; - ai->writeImplicitCtorArgs(OS); + DelayedArgs->writeImplicitCtorArgs(OS); } OS << ", I);\n"; OS << "}\n\n"; }; - auto emitCreates = [&](bool emitFake) { - emitCreate(true, emitFake); - emitCreate(false, emitFake); - emitCreateNoCI(true, emitFake); - emitCreateNoCI(false, emitFake); + auto emitCreates = [&](bool DelayedArgsOnly, bool emitFake) { + emitCreate(true, DelayedArgsOnly, emitFake); + emitCreate(false, DelayedArgsOnly, emitFake); + emitCreateNoCI(true, DelayedArgsOnly, emitFake); + emitCreateNoCI(false, DelayedArgsOnly, emitFake); }; if (Header) OS << " // Factory methods\n"; // Emit a CreateImplicit that takes all the arguments. - emitCreates(true); + emitCreates(false, true); // Emit a CreateImplicit that takes all the non-fake arguments. if (HasFakeArg) - emitCreates(false); + emitCreates(false, false); + + // Emit a CreateWithDelayedArgs that takes only the dependent argument + // expressions. + if (DelayedArgs) + emitCreates(true, false); // Emit constructors. - auto emitCtor = [&](bool emitOpt, bool emitFake) { + auto emitCtor = [&](bool emitOpt, bool emitFake, bool emitNoArgs) { auto shouldEmitArg = [=](const std::unique_ptr<Argument> &arg) { - if (arg->isFake()) return emitFake; - if (arg->isOptional()) return emitOpt; + if (emitNoArgs) + return false; + if (arg->isFake()) + return emitFake; + if (arg->isOptional()) + return emitOpt; return true; }; if (Header) @@ -2492,7 +2830,8 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, << "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo"; OS << '\n'; for (auto const &ai : Args) { - if (!shouldEmitArg(ai)) continue; + if (!shouldEmitArg(ai)) + continue; OS << " , "; ai->writeCtorParameters(OS); OS << "\n"; @@ -2522,11 +2861,17 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, } OS << "\n"; } + if (DelayedArgs) { + OS << " , "; + DelayedArgs->writeCtorDefaultInitializers(OS); + OS << "\n"; + } OS << " {\n"; for (auto const &ai : Args) { - if (!shouldEmitArg(ai)) continue; + if (!shouldEmitArg(ai)) + continue; ai->writeCtorBody(OS); } OS << "}\n\n"; @@ -2537,15 +2882,24 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, // Emit a constructor that includes all the arguments. // This is necessary for cloning. - emitCtor(true, true); + emitCtor(true, true, false); // Emit a constructor that takes all the non-fake arguments. if (HasFakeArg) - emitCtor(true, false); + emitCtor(true, false, false); // Emit a constructor that takes all the non-fake, non-optional arguments. if (HasOptArg) - emitCtor(false, false); + emitCtor(false, false, false); + + // Emit constructors that takes no arguments if none already exists. + // This is used for delaying arguments. + bool HasRequiredArgs = + llvm::count_if(Args, [=](const std::unique_ptr<Argument> &arg) { + return !arg->isFake() && !arg->isOptional(); + }); + if (DelayedArgs && HasRequiredArgs) + emitCtor(false, false, true); if (Header) { OS << '\n'; @@ -2591,6 +2945,11 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, } if (Header) { + if (DelayedArgs) { + DelayedArgs->writeAccessors(OS); + DelayedArgs->writeSetter(OS); + } + OS << R.getValueAsString("AdditionalMembers"); OS << "\n\n"; @@ -2599,6 +2958,9 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, OS << "};\n\n"; } else { + if (DelayedArgs) + DelayedArgs->writeAccessorDefinitions(OS); + OS << R.getName() << "Attr *" << R.getName() << "Attr::clone(ASTContext &C) const {\n"; OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this"; @@ -2610,6 +2972,11 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, OS << " A->Inherited = Inherited;\n"; OS << " A->IsPackExpansion = IsPackExpansion;\n"; OS << " A->setImplicit(Implicit);\n"; + if (DelayedArgs) { + OS << " A->setDelayedArgs(C, "; + DelayedArgs->writeCloneArgs(OS); + OS << ");\n"; + } OS << " return A;\n}\n\n"; writePrettyPrintFunction(R, Args, OS); @@ -2619,7 +2986,7 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, } // Emits the class definitions for attributes. void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute classes' definitions", OS); + emitSourceFileHeader("Attribute classes' definitions", OS, Records); OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n"; @@ -2631,7 +2998,8 @@ void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { // Emits the class method definitions for attributes. void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute classes' member function definitions", OS); + emitSourceFileHeader("Attribute classes' member function definitions", OS, + Records); emitAttributes(Records, OS, false); @@ -2676,9 +3044,9 @@ static void emitAttrList(raw_ostream &OS, StringRef Class, // Determines if an attribute has a Pragma spelling. static bool AttrHasPragmaSpelling(const Record *R) { std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R); - return llvm::find_if(Spellings, [](const FlattenedSpelling &S) { - return S.variety() == "Pragma"; - }) != Spellings.end(); + return llvm::any_of(Spellings, [](const FlattenedSpelling &S) { + return S.variety() == "Pragma"; + }); } namespace { @@ -2698,7 +3066,8 @@ static const AttrClassDescriptor AttrClassDescriptors[] = { { "INHERITABLE_ATTR", "InheritableAttr" }, { "DECL_OR_TYPE_ATTR", "DeclOrTypeAttr" }, { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" }, - { "PARAMETER_ABI_ATTR", "ParameterABIAttr" } + { "PARAMETER_ABI_ATTR", "ParameterABIAttr" }, + { "HLSL_ANNOTATION_ATTR", "HLSLAnnotationAttr"} }; static void emitDefaultDefine(raw_ostream &OS, StringRef name, @@ -2866,7 +3235,8 @@ namespace clang { // Emits the enumeration list for attributes. void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + emitSourceFileHeader("List of all attributes that Clang recognizes", OS, + Records); AttrClassHierarchy Hierarchy(Records); @@ -2905,9 +3275,41 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { } // Emits the enumeration list for attributes. +void EmitClangAttrPrintList(const std::string &FieldName, RecordKeeper &Records, + raw_ostream &OS) { + emitSourceFileHeader( + "List of attributes that can be print on the left side of a decl", OS, + Records); + + AttrClassHierarchy Hierarchy(Records); + + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + std::vector<Record *> PragmaAttrs; + bool first = false; + + for (auto *Attr : Attrs) { + if (!Attr->getValueAsBit("ASTNode")) + continue; + + if (!Attr->getValueAsBit(FieldName)) + continue; + + if (!first) { + first = true; + OS << "#define CLANG_ATTR_LIST_" << FieldName; + } + + OS << " \\\n case attr::" << Attr->getName() << ":"; + } + + OS << '\n'; +} + +// Emits the enumeration list for attributes. void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader( - "List of all attribute subject matching rules that Clang recognizes", OS); + "List of all attribute subject matching rules that Clang recognizes", OS, + Records); PragmaClangAttributeSupport &PragmaAttributeSupport = getPragmaAttributeSupport(Records); emitDefaultDefine(OS, "ATTR_MATCH_RULE", nullptr); @@ -2917,12 +3319,13 @@ void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS) { // Emits the code to read an attribute from a precompiled header. void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute deserialization code", OS); + emitSourceFileHeader("Attribute deserialization code", OS, Records); Record *InhClass = Records.getClass("InheritableAttr"); std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), ArgRecords; std::vector<std::unique_ptr<Argument>> Args; + std::unique_ptr<VariadicExprArgument> DelayedArgs; OS << " switch (Kind) {\n"; for (const auto *Attr : Attrs) { @@ -2935,6 +3338,12 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { OS << " bool isInherited = Record.readInt();\n"; OS << " bool isImplicit = Record.readInt();\n"; OS << " bool isPackExpansion = Record.readInt();\n"; + DelayedArgs = nullptr; + if (Attr->getValueAsBit("AcceptsExprPack")) { + DelayedArgs = + std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName()); + DelayedArgs->writePCHReadDecls(OS); + } ArgRecords = R.getValueAsListOfDefs("Args"); Args.clear(); for (const auto *Arg : ArgRecords) { @@ -2951,6 +3360,12 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n"; OS << " New->setImplicit(isImplicit);\n"; OS << " New->setPackExpansion(isPackExpansion);\n"; + if (DelayedArgs) { + OS << " cast<" << R.getName() + << "Attr>(New)->setDelayedArgs(Context, "; + DelayedArgs->writePCHReadArgs(OS); + OS << ");\n"; + } OS << " break;\n"; OS << " }\n"; } @@ -2959,7 +3374,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { // Emits the code to write an attribute to a precompiled header. void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute serialization code", OS); + emitSourceFileHeader("Attribute serialization code", OS, Records); Record *InhClass = Records.getClass("InheritableAttr"); std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; @@ -2978,6 +3393,8 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { OS << " Record.push_back(SA->isInherited());\n"; OS << " Record.push_back(A->isImplicit());\n"; OS << " Record.push_back(A->isPackExpansion());\n"; + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writePCHWrite(OS); for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writePCHWrite(OS); @@ -3070,9 +3487,10 @@ static bool GenerateTargetSpecificAttrChecks(const Record *R, } static void GenerateHasAttrSpellingStringSwitch( - const std::vector<Record *> &Attrs, raw_ostream &OS, - const std::string &Variety = "", const std::string &Scope = "") { - for (const auto *Attr : Attrs) { + const std::vector<std::pair<const Record *, FlattenedSpelling>> &Attrs, + raw_ostream &OS, const std::string &Variety, + const std::string &Scope = "") { + for (const auto &[Attr, Spelling] : Attrs) { // C++11-style attributes have specific version information associated with // them. If the attribute has no scope, the version information must not // have the default value (1), as that's incorrect. Instead, the unscoped @@ -3080,22 +3498,33 @@ static void GenerateHasAttrSpellingStringSwitch( // document, which can be found at: // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations // - // C2x-style attributes have the same kind of version information + // C23-style attributes have the same kind of version information // associated with them. The unscoped attribute version information should // be taken from the specification of the attribute in the C Standard. + // + // Clang-specific attributes have the same kind of version information + // associated with them. This version is typically the default value (1). + // These version values are clang-specific and should typically be + // incremented once the attribute changes its syntax and/or semantics in a + // a way that is impactful to the end user. int Version = 1; - if (Variety == "CXX11" || Variety == "C2x") { - std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings"); - for (const auto &Spelling : Spellings) { - if (Spelling->getValueAsString("Variety") == Variety) { - Version = static_cast<int>(Spelling->getValueAsInt("Version")); - if (Scope.empty() && Version == 1) - PrintError(Spelling->getLoc(), "Standard attributes must have " - "valid version information."); - break; - } - } + assert(Spelling.variety() == Variety); + std::string Name = ""; + if (Spelling.nameSpace().empty() || Scope == Spelling.nameSpace()) { + Name = Spelling.name(); + Version = static_cast<int>( + Spelling.getSpellingRecord().getValueAsInt("Version")); + // Verify that explicitly specified CXX11 and C23 spellings (i.e. + // not inferred from Clang/GCC spellings) have a version that's + // different from the default (1). + bool RequiresValidVersion = + (Variety == "CXX11" || Variety == "C23") && + Spelling.getSpellingRecord().getValueAsString("Variety") == Variety; + if (RequiresValidVersion && Scope.empty() && Version == 1) + PrintError(Spelling.getSpellingRecord().getLoc(), + "Standard attributes must have " + "valid version information."); } std::string Test; @@ -3107,35 +3536,77 @@ static void GenerateHasAttrSpellingStringSwitch( // If this is the C++11 variety, also add in the LangOpts test. if (Variety == "CXX11") Test += " && LangOpts.CPlusPlus11"; - else if (Variety == "C2x") - Test += " && LangOpts.DoubleSquareBracketAttributes"; + } else if (!Attr->getValueAsListOfDefs("TargetSpecificSpellings").empty()) { + // Add target checks if this spelling is target-specific. + const std::vector<Record *> TargetSpellings = + Attr->getValueAsListOfDefs("TargetSpecificSpellings"); + for (const auto &TargetSpelling : TargetSpellings) { + // Find spelling that matches current scope and name. + for (const auto &Spelling : GetFlattenedSpellings(*TargetSpelling)) { + if (Scope == Spelling.nameSpace() && Name == Spelling.name()) { + const Record *Target = TargetSpelling->getValueAsDef("Target"); + std::vector<StringRef> Arches = + Target->getValueAsListOfStrings("Arches"); + GenerateTargetSpecificAttrChecks(Target, Arches, Test, + /*FnName=*/nullptr); + break; + } + } + } + + if (Variety == "CXX11") + Test += " && LangOpts.CPlusPlus11"; } else if (Variety == "CXX11") // C++11 mode should be checked against LangOpts, which is presumed to be // present in the caller. Test = "LangOpts.CPlusPlus11"; - else if (Variety == "C2x") - Test = "LangOpts.DoubleSquareBracketAttributes"; - - std::string TestStr = - !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1"; - std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); - for (const auto &S : Spellings) - if (Variety.empty() || (Variety == S.variety() && - (Scope.empty() || Scope == S.nameSpace()))) - OS << " .Case(\"" << S.name() << "\", " << TestStr << ")\n"; + + std::string TestStr = !Test.empty() + ? Test + " ? " + llvm::itostr(Version) + " : 0" + : llvm::itostr(Version); + if (Scope.empty() || Scope == Spelling.nameSpace()) + OS << " .Case(\"" << Spelling.name() << "\", " << TestStr << ")\n"; } OS << " .Default(0);\n"; } +// Emits list of regular keyword attributes with info about their arguments. +void EmitClangRegularKeywordAttributeInfo(RecordKeeper &Records, + raw_ostream &OS) { + emitSourceFileHeader( + "A list of regular keyword attributes generated from the attribute" + " definitions", + OS); + // Assume for now that the same token is not used in multiple regular + // keyword attributes. + for (auto *R : Records.getAllDerivedDefinitions("Attr")) + for (const auto &S : GetFlattenedSpellings(*R)) { + if (!isRegularKeywordAttribute(S)) + continue; + std::vector<Record *> Args = R->getValueAsListOfDefs("Args"); + bool HasArgs = llvm::any_of( + Args, [](const Record *Arg) { return !Arg->getValueAsBit("Fake"); }); + + OS << "KEYWORD_ATTRIBUTE(" + << S.getSpellingRecord().getValueAsString("Name") << ", " + << (HasArgs ? "true" : "false") << ", )\n"; + } + OS << "#undef KEYWORD_ATTRIBUTE\n"; +} + // Emits the list of spellings for attributes. void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Code to implement the __has_attribute logic", OS); + emitSourceFileHeader("Code to implement the __has_attribute logic", OS, + Records); // Separate all of the attributes out into four group: generic, C++11, GNU, // and declspecs. Then generate a big switch statement for each of them. std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); - std::vector<Record *> Declspec, Microsoft, GNU, Pragma; - std::map<std::string, std::vector<Record *>> CXX, C2x; + std::vector<std::pair<const Record *, FlattenedSpelling>> Declspec, Microsoft, + GNU, Pragma, HLSLSemantic; + std::map<std::string, + std::vector<std::pair<const Record *, FlattenedSpelling>>> + CXX, C23; // Walk over the list of all attributes, and split them out based on the // spelling variety. @@ -3144,37 +3615,45 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { for (const auto &SI : Spellings) { const std::string &Variety = SI.variety(); if (Variety == "GNU") - GNU.push_back(R); + GNU.emplace_back(R, SI); else if (Variety == "Declspec") - Declspec.push_back(R); + Declspec.emplace_back(R, SI); else if (Variety == "Microsoft") - Microsoft.push_back(R); + Microsoft.emplace_back(R, SI); else if (Variety == "CXX11") - CXX[SI.nameSpace()].push_back(R); - else if (Variety == "C2x") - C2x[SI.nameSpace()].push_back(R); + CXX[SI.nameSpace()].emplace_back(R, SI); + else if (Variety == "C23") + C23[SI.nameSpace()].emplace_back(R, SI); else if (Variety == "Pragma") - Pragma.push_back(R); + Pragma.emplace_back(R, SI); + else if (Variety == "HLSLSemantic") + HLSLSemantic.emplace_back(R, SI); } } OS << "const llvm::Triple &T = Target.getTriple();\n"; OS << "switch (Syntax) {\n"; - OS << "case AttrSyntax::GNU:\n"; + OS << "case AttributeCommonInfo::Syntax::AS_GNU:\n"; OS << " return llvm::StringSwitch<int>(Name)\n"; GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU"); - OS << "case AttrSyntax::Declspec:\n"; + OS << "case AttributeCommonInfo::Syntax::AS_Declspec:\n"; OS << " return llvm::StringSwitch<int>(Name)\n"; GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec"); - OS << "case AttrSyntax::Microsoft:\n"; + OS << "case AttributeCommonInfo::Syntax::AS_Microsoft:\n"; OS << " return llvm::StringSwitch<int>(Name)\n"; GenerateHasAttrSpellingStringSwitch(Microsoft, OS, "Microsoft"); - OS << "case AttrSyntax::Pragma:\n"; + OS << "case AttributeCommonInfo::Syntax::AS_Pragma:\n"; OS << " return llvm::StringSwitch<int>(Name)\n"; GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma"); - auto fn = [&OS](const char *Spelling, const char *Variety, - const std::map<std::string, std::vector<Record *>> &List) { - OS << "case AttrSyntax::" << Variety << ": {\n"; + OS << "case AttributeCommonInfo::Syntax::AS_HLSLSemantic:\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(HLSLSemantic, OS, "HLSLSemantic"); + auto fn = [&OS](const char *Spelling, + const std::map< + std::string, + std::vector<std::pair<const Record *, FlattenedSpelling>>> + &List) { + OS << "case AttributeCommonInfo::Syntax::AS_" << Spelling << ": {\n"; // C++11-style attributes are further split out based on the Scope. for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) { if (I != List.cbegin()) @@ -3189,14 +3668,24 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } OS << "\n} break;\n"; }; - fn("CXX11", "CXX", CXX); - fn("C2x", "C", C2x); + fn("CXX11", CXX); + fn("C23", C23); + OS << "case AttributeCommonInfo::Syntax::AS_Keyword:\n"; + OS << "case AttributeCommonInfo::Syntax::AS_ContextSensitiveKeyword:\n"; + OS << " llvm_unreachable(\"hasAttribute not supported for keyword\");\n"; + OS << " return 0;\n"; + OS << "case AttributeCommonInfo::Syntax::AS_Implicit:\n"; + OS << " llvm_unreachable (\"hasAttribute not supported for " + "AS_Implicit\");\n"; + OS << " return 0;\n"; + OS << "}\n"; } void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Code to translate different attribute spellings " - "into internal identifiers", OS); + emitSourceFileHeader("Code to translate different attribute spellings into " + "internal identifiers", + OS, Records); OS << " switch (getParsedKind()) {\n"; OS << " case IgnoredAttribute:\n"; @@ -3226,7 +3715,8 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { // Emits code used by RecursiveASTVisitor to visit attributes void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS); + emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS, + Records); std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); @@ -3265,6 +3755,10 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { for (const auto *Arg : ArgRecords) createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()) + .writeASTVisitorTraversal(OS); + OS << " return true;\n"; OS << "}\n\n"; } @@ -3347,7 +3841,8 @@ void EmitClangAttrTemplateInstantiateHelper(const std::vector<Record *> &Attrs, // Emits code to instantiate dependent attributes on templates. void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Template instantiation code for attributes", OS); + emitSourceFileHeader("Template instantiation code for attributes", OS, + Records); std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); @@ -3369,7 +3864,8 @@ void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) { // Emits the list of parsed attributes. void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + emitSourceFileHeader("List of all attributes that Clang recognizes", OS, + Records); OS << "#ifndef PARSED_ATTR\n"; OS << "#define PARSED_ATTR(NAME) NAME\n"; @@ -3390,7 +3886,7 @@ static void emitArgInfo(const Record &R, raw_ostream &OS) { // attribute and emit the number of required arguments followed by the // number of optional arguments. std::vector<Record *> Args = R.getValueAsListOfDefs("Args"); - unsigned ArgCount = 0, OptCount = 0; + unsigned ArgCount = 0, OptCount = 0, ArgMemberCount = 0; bool HasVariadic = false; for (const auto *Arg : Args) { // If the arg is fake, it's the user's job to supply it: general parsing @@ -3398,14 +3894,16 @@ static void emitArgInfo(const Record &R, raw_ostream &OS) { if (Arg->getValueAsBit("Fake")) continue; Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount; + ++ArgMemberCount; if (!HasVariadic && isArgVariadic(*Arg, R.getName())) HasVariadic = true; } // 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 << " NumArgs = " << ArgCount << ";\n"; - OS << " OptArgs = " << (HasVariadic ? 15 : OptCount) << ";\n"; + OS << " /*NumArgs=*/" << ArgCount << ",\n"; + OS << " /*OptArgs=*/" << (HasVariadic ? 15 : OptCount) << ",\n"; + OS << " /*NumArgMembers=*/" << ArgMemberCount << ",\n"; } static std::string GetDiagnosticSpelling(const Record &R) { @@ -3554,8 +4052,9 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { if (!StmtSubjects.empty()) { OS << "bool diagAppertainsToDecl(Sema &S, const ParsedAttr &AL, "; OS << "const Decl *D) const override {\n"; - OS << " S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl)\n"; - OS << " << AL << D->getLocation();\n"; + OS << " S.Diag(AL.getLoc(), diag::err_attribute_invalid_on_decl)\n"; + OS << " << AL << AL.isRegularKeywordAttribute() << " + "D->getLocation();\n"; OS << " return false;\n"; OS << "}\n\n"; } @@ -3584,7 +4083,7 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { OS << (Warn ? "warn_attribute_wrong_decl_type_str" : "err_attribute_wrong_decl_type_str"); OS << ")\n"; - OS << " << Attr << "; + OS << " << Attr << Attr.isRegularKeywordAttribute() << "; OS << CalculateDiagnostic(*SubjectObj) << ";\n"; OS << " return false;\n"; OS << " }\n"; @@ -3599,7 +4098,8 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { OS << "bool diagAppertainsToStmt(Sema &S, const ParsedAttr &AL, "; OS << "const Stmt *St) const override {\n"; OS << " S.Diag(AL.getLoc(), diag::err_decl_attribute_invalid_on_stmt)\n"; - OS << " << AL << St->getBeginLoc();\n"; + OS << " << AL << AL.isRegularKeywordAttribute() << " + "St->getBeginLoc();\n"; OS << " return false;\n"; OS << "}\n\n"; } @@ -3618,7 +4118,7 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { OS << (Warn ? "warn_attribute_wrong_decl_type_str" : "err_attribute_wrong_decl_type_str"); OS << ")\n"; - OS << " << Attr << "; + OS << " << Attr << Attr.isRegularKeywordAttribute() << "; OS << CalculateDiagnostic(*SubjectObj) << ";\n"; OS << " return false;\n"; OS << " }\n"; @@ -3689,7 +4189,8 @@ static void GenerateMutualExclusionsChecks(const Record &Attr, for (const std::string &A : DeclAttrs) { OS << " if (const auto *A = D->getAttr<" << A << ">()) {\n"; OS << " S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)" - << " << AL << A;\n"; + << " << AL << A << (AL.isRegularKeywordAttribute() ||" + << " A->isRegularKeywordAttribute());\n"; OS << " S.Diag(A->getLocation(), diag::note_conflicting_attribute);"; OS << " \nreturn false;\n"; OS << " }\n"; @@ -3710,7 +4211,8 @@ static void GenerateMutualExclusionsChecks(const Record &Attr, << ">()) {\n"; MergeDeclOS << " S.Diag(First->getLocation(), " << "diag::err_attributes_are_not_compatible) << First << " - << "Second;\n"; + << "Second << (First->isRegularKeywordAttribute() || " + << "Second->isRegularKeywordAttribute());\n"; MergeDeclOS << " S.Diag(Second->getLocation(), " << "diag::note_conflicting_attribute);\n"; MergeDeclOS << " return false;\n"; @@ -3750,7 +4252,8 @@ static void GenerateMutualExclusionsChecks(const Record &Attr, MergeStmtOS << " if (Iter != C.end()) {\n"; MergeStmtOS << " S.Diag((*Iter)->getLocation(), " << "diag::err_attributes_are_not_compatible) << *Iter << " - << "Second;\n"; + << "Second << ((*Iter)->isRegularKeywordAttribute() || " + << "Second->isRegularKeywordAttribute());\n"; MergeStmtOS << " S.Diag(Second->getLocation(), " << "diag::note_conflicting_attribute);\n"; MergeStmtOS << " return false;\n"; @@ -3803,14 +4306,8 @@ static void GenerateLangOptRequirements(const Record &R, if (LangOpts.empty()) 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"; - OS << " S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) "; - OS << "<< Attr;\n"; - OS << " return false;\n"; + OS << "bool acceptsLangOpts(const LangOptions &LangOpts) const override {\n"; + OS << " return " << GenerateTestExpression(LangOpts) << ";\n"; OS << "}\n\n"; } @@ -3855,6 +4352,51 @@ static void GenerateTargetRequirements(const Record &Attr, OS << "}\n\n"; } +static void +GenerateSpellingTargetRequirements(const Record &Attr, + const std::vector<Record *> &TargetSpellings, + raw_ostream &OS) { + // If there are no target specific spellings, use the default target handler. + if (TargetSpellings.empty()) + return; + + std::string Test; + bool UsesT = false; + const std::vector<FlattenedSpelling> SpellingList = + GetFlattenedSpellings(Attr); + for (unsigned TargetIndex = 0; TargetIndex < TargetSpellings.size(); + ++TargetIndex) { + const auto &TargetSpelling = TargetSpellings[TargetIndex]; + std::vector<FlattenedSpelling> Spellings = + GetFlattenedSpellings(*TargetSpelling); + + Test += "((SpellingListIndex == "; + for (unsigned Index = 0; Index < Spellings.size(); ++Index) { + Test += + llvm::itostr(getSpellingListIndex(SpellingList, Spellings[Index])); + if (Index != Spellings.size() - 1) + Test += " ||\n SpellingListIndex == "; + else + Test += ") && "; + } + + const Record *Target = TargetSpelling->getValueAsDef("Target"); + std::vector<StringRef> Arches = Target->getValueAsListOfStrings("Arches"); + std::string FnName = "isTargetSpelling"; + UsesT |= GenerateTargetSpecificAttrChecks(Target, Arches, Test, &FnName); + Test += ")"; + if (TargetIndex != TargetSpellings.size() - 1) + Test += " || "; + } + + OS << "bool spellingExistsInTarget(const TargetInfo &Target,\n"; + OS << " const unsigned SpellingListIndex) const " + "override {\n"; + if (UsesT) + OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n"; + OS << " return " << Test << ";\n", OS << "}\n\n"; +} + static void GenerateSpellingIndexToSemanticSpelling(const Record &Attr, raw_ostream &OS) { // If the attribute does not have a semantic form, we can bail out early. @@ -3895,6 +4437,55 @@ static void GenerateHandleDeclAttribute(const Record &Attr, raw_ostream &OS) { OS << "}\n\n"; } +static bool isParamExpr(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch<bool>( + Arg->getSuperClasses().back().first->getName()) + .Case("ExprArgument", true) + .Case("VariadicExprArgument", true) + .Default(false); +} + +void GenerateIsParamExpr(const Record &Attr, raw_ostream &OS) { + OS << "bool isParamExpr(size_t N) const override {\n"; + OS << " return "; + auto Args = Attr.getValueAsListOfDefs("Args"); + for (size_t I = 0; I < Args.size(); ++I) + if (isParamExpr(Args[I])) + OS << "(N == " << I << ") || "; + OS << "false;\n"; + OS << "}\n\n"; +} + +void GenerateHandleAttrWithDelayedArgs(RecordKeeper &Records, raw_ostream &OS) { + OS << "static void handleAttrWithDelayedArgs(Sema &S, Decl *D, "; + OS << "const ParsedAttr &Attr) {\n"; + OS << " SmallVector<Expr *, 4> ArgExprs;\n"; + OS << " ArgExprs.reserve(Attr.getNumArgs());\n"; + OS << " for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {\n"; + OS << " assert(!Attr.isArgIdent(I));\n"; + OS << " ArgExprs.push_back(Attr.getArgAsExpr(I));\n"; + OS << " }\n"; + OS << " clang::Attr *CreatedAttr = nullptr;\n"; + OS << " switch (Attr.getKind()) {\n"; + OS << " default:\n"; + OS << " llvm_unreachable(\"Attribute cannot hold delayed arguments.\");\n"; + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &R = *I.second; + if (!R.getValueAsBit("AcceptsExprPack")) + continue; + OS << " case ParsedAttr::AT_" << I.first << ": {\n"; + OS << " CreatedAttr = " << R.getName() << "Attr::CreateWithDelayedArgs"; + OS << "(S.Context, ArgExprs.data(), ArgExprs.size(), Attr);\n"; + OS << " break;\n"; + OS << " }\n"; + } + OS << " }\n"; + OS << " D->addAttr(CreatedAttr);\n"; + OS << "}\n\n"; +} + static bool IsKnownToGCC(const Record &Attr) { // Look at the spellings for this subject; if there are any spellings which // claim to be known to GCC, the attribute is known to GCC. @@ -3905,7 +4496,7 @@ static bool IsKnownToGCC(const Record &Attr) { /// Emits the parsed attribute helpers void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Parsed attribute helpers", OS); + emitSourceFileHeader("Parsed attribute helpers", OS, Records); OS << "#if !defined(WANT_DECL_MERGE_LOGIC) && " << "!defined(WANT_STMT_MERGE_LOGIC)\n"; @@ -3919,7 +4510,7 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // Generate all of the custom appertainsTo functions that the attributes // will be using. - for (auto I : Attrs) { + for (const auto &I : Attrs) { const Record &Attr = *I.second; if (Attr.isValueUnset("Subjects")) continue; @@ -3965,35 +4556,67 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } OS << "};\n"; } + + std::vector<std::string> ArgNames; + for (const auto &Arg : Attr.getValueAsListOfDefs("Args")) { + bool UnusedUnset; + if (Arg->getValueAsBitOrUnset("Fake", UnusedUnset)) + continue; + ArgNames.push_back(Arg->getValueAsString("Name").str()); + for (const auto &Class : Arg->getSuperClasses()) { + if (Class.first->getName().starts_with("Variadic")) { + ArgNames.back().append("..."); + break; + } + } + } + if (!ArgNames.empty()) { + OS << "static constexpr const char *" << I->first << "ArgNames[] = {\n"; + for (const auto &N : ArgNames) + OS << '"' << N << "\","; + OS << "};\n"; + } + OS << "struct ParsedAttrInfo" << I->first << " final : public ParsedAttrInfo {\n"; - OS << " ParsedAttrInfo" << I->first << "() {\n"; - OS << " AttrKind = ParsedAttr::AT_" << AttrName << ";\n"; + OS << " constexpr ParsedAttrInfo" << I->first << "() : ParsedAttrInfo(\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 << " /*HasCustomParsing=*/"; + OS << Attr.getValueAsBit("HasCustomParsing") << ",\n"; + OS << " /*AcceptsExprPack=*/"; + OS << Attr.getValueAsBit("AcceptsExprPack") << ",\n"; + OS << " /*IsTargetSpecific=*/"; + OS << Attr.isSubClassOf("TargetSpecificAttr") << ",\n"; + OS << " /*IsType=*/"; + OS << (Attr.isSubClassOf("TypeAttr") || Attr.isSubClassOf("DeclOrTypeAttr")) + << ",\n"; + OS << " /*IsStmt=*/"; OS << (Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr")) - << ";\n"; - OS << " IsKnownToGCC = "; - OS << IsKnownToGCC(Attr) << ";\n"; - OS << " IsSupportedByPragmaAttribute = "; - OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n"; + << ",\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"; + OS << " /*Spellings=*/" << I->first << "Spellings,\n"; + else + OS << " /*Spellings=*/{},\n"; + if (!ArgNames.empty()) + OS << " /*ArgNames=*/" << I->first << "ArgNames"; + else + OS << " /*ArgNames=*/{}"; + OS << ") {}\n"; GenerateAppertainsTo(Attr, OS); GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS); GenerateLangOptRequirements(Attr, OS); GenerateTargetRequirements(Attr, Dupes, OS); + GenerateSpellingTargetRequirements( + Attr, Attr.getValueAsListOfDefs("TargetSpecificSpellings"), OS); GenerateSpellingIndexToSemanticSpelling(Attr, OS); PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); GenerateHandleDeclAttribute(Attr, OS); + GenerateIsParamExpr(Attr, OS); OS << "static const ParsedAttrInfo" << I->first << " Instance;\n"; OS << "};\n"; OS << "const ParsedAttrInfo" << I->first << " ParsedAttrInfo" << I->first @@ -4006,6 +4629,9 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } OS << "};\n\n"; + // Generate function for handling attributes with delayed arguments + GenerateHandleAttrWithDelayedArgs(Records, OS); + // Generate the attribute match rules. emitAttributeMatchRules(PragmaAttributeSupport, OS); @@ -4034,11 +4660,11 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // Emits the kind list of parsed attributes void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute name matcher", OS); + emitSourceFileHeader("Attribute name matcher", OS, Records); std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11, - Keywords, Pragma, C2x; + Keywords, Pragma, C23, HLSLSemantic; std::set<std::string> Seen; for (const auto *A : Attrs) { const Record &Attr = *A; @@ -4060,9 +4686,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { if (Attr.isSubClassOf("TargetSpecificAttr") && !Attr.isValueUnset("ParseKind")) { AttrName = std::string(Attr.getValueAsString("ParseKind")); - if (Seen.find(AttrName) != Seen.end()) + if (!Seen.insert(AttrName).second) continue; - Seen.insert(AttrName); } else AttrName = NormalizeAttrName(StringRef(Attr.getName())).str(); @@ -4076,8 +4701,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { Matches = &CXX11; if (!S.nameSpace().empty()) Spelling += S.nameSpace() + "::"; - } else if (Variety == "C2x") { - Matches = &C2x; + } else if (Variety == "C23") { + Matches = &C23; if (!S.nameSpace().empty()) Spelling += S.nameSpace() + "::"; } else if (Variety == "GNU") @@ -4090,6 +4715,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { Matches = &Keywords; else if (Variety == "Pragma") Matches = &Pragma; + else if (Variety == "HLSLSemantic") + Matches = &HLSLSemantic; assert(Matches && "Unsupported spelling variety found"); @@ -4118,13 +4745,15 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { StringMatcher("Name", Microsoft, OS).Emit(); OS << " } else if (AttributeCommonInfo::AS_CXX11 == Syntax) {\n"; StringMatcher("Name", CXX11, OS).Emit(); - OS << " } else if (AttributeCommonInfo::AS_C2x == Syntax) {\n"; - StringMatcher("Name", C2x, OS).Emit(); + OS << " } else if (AttributeCommonInfo::AS_C23 == Syntax) {\n"; + StringMatcher("Name", C23, OS).Emit(); OS << " } else if (AttributeCommonInfo::AS_Keyword == Syntax || "; OS << "AttributeCommonInfo::AS_ContextSensitiveKeyword == Syntax) {\n"; StringMatcher("Name", Keywords, OS).Emit(); OS << " } else if (AttributeCommonInfo::AS_Pragma == Syntax) {\n"; StringMatcher("Name", Pragma, OS).Emit(); + OS << " } else if (AttributeCommonInfo::AS_HLSLSemantic == Syntax) {\n"; + StringMatcher("Name", HLSLSemantic, OS).Emit(); OS << " }\n"; OS << " return AttributeCommonInfo::UnknownAttribute;\n" << "}\n"; @@ -4132,7 +4761,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { // Emits the code to dump an attribute. void EmitClangAttrTextNodeDump(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute text node dumper", OS); + emitSourceFileHeader("Attribute text node dumper", OS, Records); std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; for (const auto *Attr : Attrs) { @@ -4155,6 +4784,9 @@ void EmitClangAttrTextNodeDump(RecordKeeper &Records, raw_ostream &OS) { for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writeDump(SS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writeDump(OS); + if (SS.tell()) { OS << " void Visit" << R.getName() << "Attr(const " << R.getName() << "Attr *A) {\n"; @@ -4168,7 +4800,7 @@ void EmitClangAttrTextNodeDump(RecordKeeper &Records, raw_ostream &OS) { } void EmitClangAttrNodeTraverse(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("Attribute text node traverser", OS); + emitSourceFileHeader("Attribute text node traverser", OS, Records); std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; for (const auto *Attr : Attrs) { @@ -4182,6 +4814,8 @@ void EmitClangAttrNodeTraverse(RecordKeeper &Records, raw_ostream &OS) { Args = R.getValueAsListOfDefs("Args"); for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writeDumpChildren(SS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writeDumpChildren(SS); if (SS.tell()) { OS << " void Visit" << R.getName() << "Attr(const " << R.getName() << "Attr *A) {\n"; @@ -4194,13 +4828,14 @@ void EmitClangAttrNodeTraverse(RecordKeeper &Records, raw_ostream &OS) { } } -void EmitClangAttrParserStringSwitches(RecordKeeper &Records, - raw_ostream &OS) { - emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS); +void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS, Records); emitClangAttrArgContextList(Records, OS); emitClangAttrIdentifierArgList(Records, OS); + emitClangAttrUnevaluatedStringLiteralList(Records, OS); emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrThisIsaIdentifierArgList(Records, OS); + emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); } @@ -4210,16 +4845,36 @@ void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records, getPragmaAttributeSupport(Records).generateParsingHelpers(OS); } -enum class SpellingKind { +void EmitClangAttrDocTable(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Clang attribute documentation", OS, Records); + + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + for (const auto *A : Attrs) { + if (!A->getValueAsBit("ASTNode")) + continue; + std::vector<Record *> Docs = A->getValueAsListOfDefs("Documentation"); + assert(!Docs.empty()); + // Only look at the first documentation if there are several. + // (Currently there's only one such attr, revisit if this becomes common). + StringRef Text = + Docs.front()->getValueAsOptionalString("Content").value_or(""); + OS << "\nstatic const char AttrDoc_" << A->getName() << "[] = " + << "R\"reST(" << Text.trim() << ")reST\";\n"; + } +} + +enum class SpellingKind : size_t { GNU, CXX11, - C2x, + C23, Declspec, Microsoft, Keyword, Pragma, + HLSLSemantic, + NumSpellingKinds }; -static const size_t NumSpellingKinds = (size_t)SpellingKind::Pragma + 1; +static const size_t NumSpellingKinds = (size_t)SpellingKind::NumSpellingKinds; class SpellingList { std::vector<std::string> Spellings[NumSpellingKinds]; @@ -4233,16 +4888,17 @@ public: SpellingKind Kind = StringSwitch<SpellingKind>(Spelling.variety()) .Case("GNU", SpellingKind::GNU) .Case("CXX11", SpellingKind::CXX11) - .Case("C2x", SpellingKind::C2x) + .Case("C23", SpellingKind::C23) .Case("Declspec", SpellingKind::Declspec) .Case("Microsoft", SpellingKind::Microsoft) .Case("Keyword", SpellingKind::Keyword) - .Case("Pragma", SpellingKind::Pragma); + .Case("Pragma", SpellingKind::Pragma) + .Case("HLSLSemantic", SpellingKind::HLSLSemantic); std::string Name; if (!Spelling.nameSpace().empty()) { switch (Kind) { case SpellingKind::CXX11: - case SpellingKind::C2x: + case SpellingKind::C23: Name = Spelling.nameSpace() + "::"; break; case SpellingKind::Pragma: @@ -4287,7 +4943,8 @@ static void WriteCategoryHeader(const Record *DocCategory, static std::pair<std::string, SpellingList> GetAttributeHeadingAndSpellings(const Record &Documentation, - const Record &Attribute) { + const Record &Attribute, + StringRef Cat) { // FIXME: there is no way to have a per-spelling category for the attribute // documentation. This may not be a limiting factor since the spellings // should generally be consistently applied across the category. @@ -4307,7 +4964,7 @@ GetAttributeHeadingAndSpellings(const Record &Documentation, else { std::set<std::string> Uniques; for (auto I = Spellings.begin(), E = Spellings.end(); - I != E && Uniques.size() <= 1; ++I) { + I != E; ++I) { std::string Spelling = std::string(NormalizeNameForSpellingComparison(I->name())); Uniques.insert(Spelling); @@ -4316,6 +4973,11 @@ GetAttributeHeadingAndSpellings(const Record &Documentation, // needs. if (Uniques.size() == 1) Heading = *Uniques.begin(); + // If it's in the undocumented category, just construct a header by + // concatenating all the spellings. Might not be great, but better than + // nothing. + else if (Cat == "Undocumented") + Heading = llvm::join(Uniques.begin(), Uniques.end(), ", "); } } @@ -4336,10 +4998,12 @@ static void WriteDocumentation(RecordKeeper &Records, OS << Doc.Heading << "\n" << std::string(Doc.Heading.length(), '-') << "\n"; // List what spelling syntaxes the attribute supports. + // Note: "#pragma clang attribute" is handled outside the spelling kinds loop + // so it must be last. OS << ".. csv-table:: Supported Syntaxes\n"; - OS << " :header: \"GNU\", \"C++11\", \"C2x\", \"``__declspec``\","; - OS << " \"Keyword\", \"``#pragma``\", \"``#pragma clang attribute``\"\n\n"; - OS << " \""; + OS << " :header: \"GNU\", \"C++11\", \"C23\", \"``__declspec``\","; + OS << " \"Keyword\", \"``#pragma``\", \"HLSL Semantic\", \"``#pragma clang "; + OS << "attribute``\"\n\n \""; for (size_t Kind = 0; Kind != NumSpellingKinds; ++Kind) { SpellingKind K = (SpellingKind)Kind; // TODO: List Microsoft (IDL-style attribute) spellings once we fully @@ -4397,26 +5061,32 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) { // Gather the Documentation lists from each of the attributes, based on the // category provided. std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); - std::map<const Record *, std::vector<DocumentationData>> SplitDocs; + struct CategoryLess { + bool operator()(const Record *L, const Record *R) const { + return L->getValueAsString("Name") < R->getValueAsString("Name"); + } + }; + std::map<const Record *, std::vector<DocumentationData>, CategoryLess> + SplitDocs; for (const auto *A : Attrs) { const Record &Attr = *A; std::vector<Record *> Docs = Attr.getValueAsListOfDefs("Documentation"); for (const auto *D : Docs) { const Record &Doc = *D; const Record *Category = Doc.getValueAsDef("Category"); - // If the category is "undocumented", then there cannot be any other - // documentation categories (otherwise, the attribute would become - // documented). + // If the category is "InternalOnly", then there cannot be any other + // documentation categories (otherwise, the attribute would be + // emitted into the docs). const StringRef Cat = Category->getValueAsString("Name"); - bool Undocumented = Cat == "Undocumented"; - if (Undocumented && Docs.size() > 1) + bool InternalOnly = Cat == "InternalOnly"; + if (InternalOnly && Docs.size() > 1) PrintFatalError(Doc.getLoc(), - "Attribute is \"Undocumented\", but has multiple " + "Attribute is \"InternalOnly\", but has multiple " "documentation categories"); - if (!Undocumented) + if (!InternalOnly) SplitDocs[Category].push_back(DocumentationData( - Doc, Attr, GetAttributeHeadingAndSpellings(Doc, Attr))); + Doc, Attr, GetAttributeHeadingAndSpellings(Doc, Attr, Cat))); } } diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp index eb2f23191c55..a113b02e1999 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -20,9 +20,10 @@ using namespace llvm; -void clang::EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("A list of commands useable in documentation " - "comments", OS); +void clang::EmitClangCommentCommandInfo(RecordKeeper &Records, + raw_ostream &OS) { + emitSourceFileHeader("A list of commands useable in documentation comments", + OS, Records); OS << "namespace {\n" "const CommandInfo Commands[] = {\n"; @@ -83,6 +84,12 @@ static std::string MangleName(StringRef Str) { default: Mangled += Str[i]; break; + case '(': + Mangled += "lparen"; + break; + case ')': + Mangled += "rparen"; + break; case '[': Mangled += "lsquare"; break; @@ -106,9 +113,10 @@ static std::string MangleName(StringRef Str) { return Mangled; } -void clang::EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("A list of commands useable in documentation " - "comments", OS); +void clang::EmitClangCommentCommandList(RecordKeeper &Records, + raw_ostream &OS) { + emitSourceFileHeader("A list of commands useable in documentation comments", + OS, Records); OS << "#ifndef COMMENT_COMMAND\n" << "# define COMMENT_COMMAND(NAME)\n" diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp index 15671a99a3fc..f1cd9af0519d 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp @@ -66,12 +66,12 @@ void clang::EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, } CLiteral.append(";"); - StringMatcher::StringPair Match(Spelling, std::string(CLiteral.str())); + StringMatcher::StringPair Match(Spelling, std::string(CLiteral)); NameToUTF8.push_back(Match); } - emitSourceFileHeader("HTML named character reference to UTF-8 " - "translation", OS); + emitSourceFileHeader("HTML named character reference to UTF-8 translation", + OS, Records); OS << "StringRef translateHTMLNamedCharacterReferenceToUTF8(\n" " StringRef Name) {\n"; diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp index 78bbbd1cba57..3dc1098753e0 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp @@ -27,7 +27,7 @@ void clang::EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) { "return true;"); } - emitSourceFileHeader("HTML tag name matcher", OS); + emitSourceFileHeader("HTML tag name matcher", OS, Records); OS << "bool isHTMLTagName(StringRef Name) {\n"; StringMatcher("Name", Matches, OS).Emit(); @@ -49,7 +49,7 @@ void clang::EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, MatchesEndTagForbidden.push_back(Match); } - emitSourceFileHeader("HTML tag properties", OS); + emitSourceFileHeader("HTML tag properties", OS, Records); OS << "bool isHTMLEndTagOptional(StringRef Name) {\n"; StringMatcher("Name", MatchesEndTagOptional, OS).Emit(); @@ -61,4 +61,3 @@ void clang::EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, OS << " return false;\n" << "}\n\n"; } - diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index 014c1adcd809..480c7c83f5f8 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -12,7 +12,6 @@ #include "TableGenBackends.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -30,6 +29,7 @@ #include <cctype> #include <functional> #include <map> +#include <optional> #include <set> using namespace llvm; @@ -129,13 +129,14 @@ namespace { }; struct GroupInfo { + llvm::StringRef GroupName; std::vector<const Record*> DiagsInGroup; std::vector<std::string> SubGroups; - unsigned IDNo; + unsigned IDNo = 0; llvm::SmallVector<const Record *, 1> Defs; - GroupInfo() : IDNo(0) {} + GroupInfo() = default; }; } // end anonymous namespace. @@ -174,6 +175,7 @@ static void groupDiagnostics(const std::vector<Record*> &Diags, Record *Group = DiagGroups[i]; GroupInfo &GI = DiagsInGroup[std::string(Group->getValueAsString("GroupName"))]; + GI.GroupName = Group->getName(); GI.Defs.push_back(Group); std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups"); @@ -248,8 +250,9 @@ typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet; namespace { class InferPedantic { - typedef llvm::DenseMap<const Record*, - std::pair<unsigned, Optional<unsigned> > > GMap; + typedef llvm::DenseMap<const Record *, + std::pair<unsigned, std::optional<unsigned>>> + GMap; DiagGroupParentMap &DiagGroupParents; const std::vector<Record*> &Diags; @@ -323,7 +326,7 @@ bool InferPedantic::isOffByDefault(const Record *Diag) { 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()) { + if (!V.second) { const GroupInfo &GI = DiagsInGroup[std::string(Group->getValueAsString("GroupName"))]; V.second = GI.SubGroups.size() + GI.DiagsInGroup.size(); @@ -335,7 +338,7 @@ bool InferPedantic::groupInPedantic(const Record *Group, bool increment) { // Consider a group in -Wpendatic IFF if has at least one diagnostic // or subgroup AND all of those diagnostics and subgroups are covered // by -Wpedantic via our computation. - return V.first != 0 && V.first == V.second.getValue(); + return V.first != 0 && V.first == *V.second; } void InferPedantic::markGroup(const Record *Group) { @@ -402,17 +405,14 @@ void InferPedantic::compute(VecOrSet DiagsInPedantic, if (!groupInPedantic(Group)) continue; - unsigned ParentsInPedantic = 0; const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group); - for (unsigned j = 0, ej = Parents.size(); j != ej; ++j) { - if (groupInPedantic(Parents[j])) - ++ParentsInPedantic; - } + bool AllParentsInPedantic = + llvm::all_of(Parents, [&](Record *R) { return groupInPedantic(R); }); // If all the parents are in -Wpedantic, this means that this diagnostic // group will be indirectly included by -Wpedantic already. In that // case, do not add it directly to -Wpedantic. If the group has no // parents, obviously it should go into -Wpedantic. - if (Parents.size() > 0 && ParentsInPedantic == Parents.size()) + if (Parents.size() > 0 && AllParentsInPedantic) continue; if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>()) @@ -614,7 +614,7 @@ struct DiagnosticTextBuilder { return It->second.Root; } - LLVM_ATTRIBUTE_NORETURN void PrintFatalError(llvm::Twine const &Msg) const { + [[noreturn]] void PrintFatalError(llvm::Twine const &Msg) const { assert(EvaluatingRecord && "not evaluating a record?"); llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg); } @@ -653,6 +653,14 @@ private: Root(O.Root) { O.Root = nullptr; } + // The move assignment operator is defined as deleted pending further + // motivation. + DiagText &operator=(DiagText &&) = delete; + + // The copy constrcutor and copy assignment operator is defined as deleted + // pending further motivation. + DiagText(const DiagText &) = delete; + DiagText &operator=(const DiagText &) = delete; ~DiagText() { for (Piece *P : AllocatedPieces) @@ -676,7 +684,7 @@ private: }; template <class Derived> struct DiagTextVisitor { - using ModifierMappingsType = Optional<std::vector<int>>; + using ModifierMappingsType = std::optional<std::vector<int>>; private: Derived &getDerived() { return static_cast<Derived &>(*this); } @@ -707,7 +715,7 @@ public: private: DiagTextVisitor &Visitor; - Optional<std::vector<int>> OldMappings; + std::optional<std::vector<int>> OldMappings; public: Piece *Substitution; @@ -1166,7 +1174,7 @@ std::vector<std::string> DiagnosticTextBuilder::buildForDocumentation(StringRef Severity, const Record *R) { EvaluatingRecordGuard Guard(&EvaluatingRecord, R); - StringRef Text = R->getValueAsString("Text"); + StringRef Text = R->getValueAsString("Summary"); DiagText D(*this, Text); TextPiece *Prefix = D.New<TextPiece>(Severity, Severity); @@ -1185,7 +1193,7 @@ DiagnosticTextBuilder::buildForDocumentation(StringRef Severity, std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { EvaluatingRecordGuard Guard(&EvaluatingRecord, R); - StringRef Text = R->getValueAsString("Text"); + StringRef Text = R->getValueAsString("Summary"); DiagText D(*this, Text); std::string Result; DiagTextPrinter{*this, Result}.Visit(D.Root); @@ -1279,8 +1287,8 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, OS << ", \""; OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"'; - // Warning associated with the diagnostic. This is stored as an index into - // the alphabetically sorted warning table. + // Warning group associated with the diagnostic. This is stored as an index + // into the alphabetically sorted warning group table. if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) { std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find( std::string(DI->getDef()->getValueAsString("GroupName"))); @@ -1309,6 +1317,11 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, else OS << ", false"; + if (R.getValueAsBit("ShowInSystemMacro")) + OS << ", true"; + else + OS << ", false"; + if (R.getValueAsBit("Deferrable")) OS << ", true"; else @@ -1330,7 +1343,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 std::string(enumName.str()); + return std::string(enumName); } /// Emit the array of diagnostic subgroups. @@ -1487,18 +1500,20 @@ static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup, for (auto const &I: DiagsInGroup) MaxLen = std::max(MaxLen, (unsigned)I.first.size()); - OS << "\n#ifdef GET_DIAG_TABLE\n"; + OS << "\n#ifdef DIAG_ENTRY\n"; unsigned SubGroupIndex = 1, DiagArrayIndex = 1; for (auto const &I: DiagsInGroup) { // Group option string. - OS << " { /* "; + OS << "DIAG_ENTRY("; + OS << I.second.GroupName << " /* "; + if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789!@#$%^*-+=:?") != std::string::npos) PrintFatalError("Invalid character in diagnostic group '" + I.first + "'"); - OS << I.first << " */ " << std::string(MaxLen - I.first.size(), ' '); + OS << I.first << " */, "; // Store a pascal-style length byte at the beginning of the string. std::string Name = char(I.first.size()) + I.first; OS << GroupNames.GetOrAddStringOffset(Name, false) << ", "; @@ -1517,7 +1532,7 @@ static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup, DiagArrayIndex += DiagsInPedantic.size(); DiagArrayIndex += V.size() + 1; } else { - OS << "/* Empty */ 0, "; + OS << "0, "; } // Subgroups. @@ -1525,17 +1540,25 @@ static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup, const bool hasSubGroups = !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty()); if (hasSubGroups) { - OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex; + OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex + << ", "; if (IsPedantic) SubGroupIndex += GroupsInPedantic.size(); SubGroupIndex += SubGroups.size() + 1; } else { - OS << "/* Empty */ 0"; + OS << "0, "; } - OS << " },\n"; + std::string Documentation = I.second.Defs.back() + ->getValue("Documentation") + ->getValue() + ->getAsUnquotedString(); + + OS << "R\"(" << StringRef(Documentation).trim() << ")\""; + + OS << ")\n"; } - OS << "#endif // GET_DIAG_TABLE\n\n"; + OS << "#endif // DIAG_ENTRY\n\n"; } /// Emit the table of diagnostic categories. @@ -1688,7 +1711,7 @@ void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') { void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R, StringRef Role, raw_ostream &OS) { - StringRef Text = R->getValueAsString("Text"); + StringRef Text = R->getValueAsString("Summary"); if (Text == "%0") OS << "The text of this diagnostic is not controlled by Clang.\n\n"; else { diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangOpcodesEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangOpcodesEmitter.cpp index ffeedcdf0ee2..db88c990d5f9 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangOpcodesEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangOpcodesEmitter.cpp @@ -21,7 +21,7 @@ using namespace llvm; namespace { class ClangOpcodesEmitter { RecordKeeper &Records; - Record Root; + const Record Root; unsigned NumTypes; public: @@ -34,33 +34,32 @@ public: private: /// Emits the opcode name for the opcode enum. /// The name is obtained by concatenating the name with the list of types. - void EmitEnum(raw_ostream &OS, StringRef N, Record *R); + void EmitEnum(raw_ostream &OS, StringRef N, const Record *R); /// Emits the switch case and the invocation in the interpreter. - void EmitInterp(raw_ostream &OS, StringRef N, Record *R); + void EmitInterp(raw_ostream &OS, StringRef N, const Record *R); /// Emits the disassembler. - void EmitDisasm(raw_ostream &OS, StringRef N, Record *R); + void EmitDisasm(raw_ostream &OS, StringRef N, const Record *R); /// Emits the byte code emitter method. - void EmitEmitter(raw_ostream &OS, StringRef N, Record *R); + void EmitEmitter(raw_ostream &OS, StringRef N, const Record *R); /// Emits the prototype. - void EmitProto(raw_ostream &OS, StringRef N, Record *R); + void EmitProto(raw_ostream &OS, StringRef N, const Record *R); /// Emits the prototype to dispatch from a type. - void EmitGroup(raw_ostream &OS, StringRef N, Record *R); + void EmitGroup(raw_ostream &OS, StringRef N, const Record *R); /// Emits the evaluator method. - void EmitEval(raw_ostream &OS, StringRef N, Record *R); + void EmitEval(raw_ostream &OS, StringRef N, const Record *R); - void PrintTypes(raw_ostream &OS, ArrayRef<Record *> Types); + void PrintTypes(raw_ostream &OS, ArrayRef<const Record *> Types); }; -void Enumerate(const Record *R, - StringRef N, - std::function<void(ArrayRef<Record *>, Twine)> &&F) { - llvm::SmallVector<Record *, 2> TypePath; +void Enumerate(const Record *R, StringRef N, + std::function<void(ArrayRef<const Record *>, Twine)> &&F) { + llvm::SmallVector<const Record *, 2> TypePath; auto *Types = R->getValueAsListInit("Types"); std::function<void(size_t, const Twine &)> Rec; @@ -102,67 +101,80 @@ void ClangOpcodesEmitter::run(raw_ostream &OS) { } } -void ClangOpcodesEmitter::EmitEnum(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitEnum(raw_ostream &OS, StringRef N, + const Record *R) { OS << "#ifdef GET_OPCODE_NAMES\n"; - Enumerate(R, N, [&OS](ArrayRef<Record *>, const Twine &ID) { + Enumerate(R, N, [&OS](ArrayRef<const Record *>, const Twine &ID) { OS << "OP_" << ID << ",\n"; }); OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N, + const Record *R) { OS << "#ifdef GET_INTERP\n"; - Enumerate(R, N, [this, R, &OS, &N](ArrayRef<Record *> TS, const Twine &ID) { - bool CanReturn = R->getValueAsBit("CanReturn"); - bool ChangesPC = R->getValueAsBit("ChangesPC"); - auto Args = R->getValueAsListOfDefs("Args"); - - OS << "case OP_" << ID << ": {\n"; - - // Emit calls to read arguments. - for (size_t I = 0, N = Args.size(); I < N; ++I) { - OS << " auto V" << I; - OS << " = "; - OS << "PC.read<" << Args[I]->getValueAsString("Name") << ">();\n"; - } - - // Emit a call to the template method and pass arguments. - OS << " if (!" << N; - PrintTypes(OS, TS); - OS << "(S"; - if (ChangesPC) - OS << ", PC"; - else - OS << ", OpPC"; - if (CanReturn) - OS << ", Result"; - for (size_t I = 0, N = Args.size(); I < N; ++I) - OS << ", V" << I; - OS << "))\n"; - OS << " return false;\n"; - - // Bail out if interpreter returned. - if (CanReturn) { - OS << " if (!S.Current || S.Current->isRoot())\n"; - OS << " return true;\n"; - } - - OS << " continue;\n"; - OS << "}\n"; - }); + Enumerate(R, N, + [this, R, &OS, &N](ArrayRef<const Record *> TS, const Twine &ID) { + bool CanReturn = R->getValueAsBit("CanReturn"); + bool ChangesPC = R->getValueAsBit("ChangesPC"); + auto Args = R->getValueAsListOfDefs("Args"); + + OS << "case OP_" << ID << ": {\n"; + + if (CanReturn) + OS << " bool DoReturn = (S.Current == StartFrame);\n"; + + // Emit calls to read arguments. + for (size_t I = 0, N = Args.size(); I < N; ++I) { + OS << " auto V" << I; + OS << " = "; + OS << "ReadArg<" << Args[I]->getValueAsString("Name") + << ">(S, PC);\n"; + } + + // Emit a call to the template method and pass arguments. + OS << " if (!" << N; + PrintTypes(OS, TS); + OS << "(S"; + if (ChangesPC) + OS << ", PC"; + else + OS << ", OpPC"; + if (CanReturn) + OS << ", Result"; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << ", V" << I; + OS << "))\n"; + OS << " return false;\n"; + + // Bail out if interpreter returned. + if (CanReturn) { + OS << " if (!S.Current || S.Current->isRoot())\n"; + OS << " return true;\n"; + + OS << " if (DoReturn)\n"; + OS << " return true;\n"; + } + + OS << " continue;\n"; + OS << "}\n"; + }); OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitDisasm(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitDisasm(raw_ostream &OS, StringRef N, + const Record *R) { OS << "#ifdef GET_DISASM\n"; - Enumerate(R, N, [R, &OS](ArrayRef<Record *>, const Twine &ID) { + Enumerate(R, N, [R, &OS](ArrayRef<const Record *>, const Twine &ID) { OS << "case OP_" << ID << ":\n"; OS << " PrintName(\"" << ID << "\");\n"; OS << " OS << \"\\t\""; - for (auto *Arg : R->getValueAsListOfDefs("Args")) - OS << " << PC.read<" << Arg->getValueAsString("Name") << ">() << \" \""; + for (auto *Arg : R->getValueAsListOfDefs("Args")) { + OS << " << ReadArg<" << Arg->getValueAsString("Name") << ">(P, PC)"; + OS << " << \" \""; + } OS << " << \"\\n\";\n"; OS << " continue;\n"; @@ -170,12 +182,13 @@ void ClangOpcodesEmitter::EmitDisasm(raw_ostream &OS, StringRef N, Record *R) { OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitEmitter(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitEmitter(raw_ostream &OS, StringRef N, + const Record *R) { if (R->getValueAsBit("HasCustomLink")) return; OS << "#ifdef GET_LINK_IMPL\n"; - Enumerate(R, N, [R, &OS](ArrayRef<Record *>, const Twine &ID) { + Enumerate(R, N, [R, &OS](ArrayRef<const Record *>, const Twine &ID) { auto Args = R->getValueAsListOfDefs("Args"); // Emit the list of arguments. @@ -200,10 +213,11 @@ void ClangOpcodesEmitter::EmitEmitter(raw_ostream &OS, StringRef N, Record *R) { OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitProto(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitProto(raw_ostream &OS, StringRef N, + const Record *R) { OS << "#if defined(GET_EVAL_PROTO) || defined(GET_LINK_PROTO)\n"; auto Args = R->getValueAsListOfDefs("Args"); - Enumerate(R, N, [&OS, &Args](ArrayRef<Record *> TS, const Twine &ID) { + Enumerate(R, N, [&OS, &Args](ArrayRef<const Record *> TS, const Twine &ID) { OS << "bool emit" << ID << "("; for (auto *Arg : Args) OS << Arg->getValueAsString("Name") << ", "; @@ -231,16 +245,19 @@ void ClangOpcodesEmitter::EmitProto(raw_ostream &OS, StringRef N, Record *R) { OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, + const Record *R) { if (!R->getValueAsBit("HasGroup")) return; auto *Types = R->getValueAsListInit("Types"); auto Args = R->getValueAsListOfDefs("Args"); + Twine EmitFuncName = "emit" + N; + // Emit the prototype of the group emitter in the header. OS << "#if defined(GET_EVAL_PROTO) || defined(GET_LINK_PROTO)\n"; - OS << "bool emit" << N << "("; + OS << "bool " << EmitFuncName << "("; for (size_t I = 0, N = Types->size(); I < N; ++I) OS << "PrimType, "; for (auto *Arg : Args) @@ -256,7 +273,7 @@ void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { OS << "#else\n"; OS << "ByteCodeEmitter\n"; OS << "#endif\n"; - OS << "::emit" << N << "("; + OS << "::" << EmitFuncName << "("; for (size_t I = 0, N = Types->size(); I < N; ++I) OS << "PrimType T" << I << ", "; for (size_t I = 0, N = Args.size(); I < N; ++I) @@ -264,8 +281,9 @@ void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { OS << "const SourceInfo &I) {\n"; std::function<void(size_t, const Twine &)> Rec; - llvm::SmallVector<Record *, 2> TS; - Rec = [this, &Rec, &OS, Types, &Args, R, &TS, N](size_t I, const Twine &ID) { + llvm::SmallVector<const Record *, 2> TS; + Rec = [this, &Rec, &OS, Types, &Args, R, &TS, N, + EmitFuncName](size_t I, const Twine &ID) { if (I >= Types->size()) { // Print a call to the emitter method. // Custom evaluator methods dispatch to template methods. @@ -301,7 +319,8 @@ void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { } // Emit a default case if not all types are present. if (Cases.size() < NumTypes) - OS << " default: llvm_unreachable(\"invalid type\");\n"; + OS << " default: llvm_unreachable(\"invalid type: " << EmitFuncName + << "\");\n"; OS << " }\n"; OS << " llvm_unreachable(\"invalid enum value\");\n"; } else { @@ -314,34 +333,37 @@ void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { OS << "#endif\n"; } -void ClangOpcodesEmitter::EmitEval(raw_ostream &OS, StringRef N, Record *R) { +void ClangOpcodesEmitter::EmitEval(raw_ostream &OS, StringRef N, + const Record *R) { if (R->getValueAsBit("HasCustomEval")) return; OS << "#ifdef GET_EVAL_IMPL\n"; - Enumerate(R, N, [this, R, &N, &OS](ArrayRef<Record *> TS, const Twine &ID) { - auto Args = R->getValueAsListOfDefs("Args"); - - OS << "bool EvalEmitter::emit" << ID << "("; - for (size_t I = 0, N = Args.size(); I < N; ++I) - OS << Args[I]->getValueAsString("Name") << " A" << I << ", "; - OS << "const SourceInfo &L) {\n"; - OS << " if (!isActive()) return true;\n"; - OS << " CurrentSource = L;\n"; - - OS << " return " << N; - PrintTypes(OS, TS); - OS << "(S, OpPC"; - for (size_t I = 0, N = Args.size(); I < N; ++I) - OS << ", A" << I; - OS << ");\n"; - OS << "}\n"; - }); + Enumerate(R, N, + [this, R, &N, &OS](ArrayRef<const Record *> TS, const Twine &ID) { + auto Args = R->getValueAsListOfDefs("Args"); + + OS << "bool EvalEmitter::emit" << ID << "("; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << Args[I]->getValueAsString("Name") << " A" << I << ", "; + OS << "const SourceInfo &L) {\n"; + OS << " if (!isActive()) return true;\n"; + OS << " CurrentSource = L;\n"; + + OS << " return " << N; + PrintTypes(OS, TS); + OS << "(S, OpPC"; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << ", A" << I; + OS << ");\n"; + OS << "}\n"; + }); OS << "#endif\n"; } -void ClangOpcodesEmitter::PrintTypes(raw_ostream &OS, ArrayRef<Record *> Types) { +void ClangOpcodesEmitter::PrintTypes(raw_ostream &OS, + ArrayRef<const Record *> Types) { if (Types.empty()) return; OS << "<"; diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp index a4cb5b7cacd9..968b3e0661a8 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -17,6 +17,7 @@ #include "TableGenBackends.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" @@ -233,19 +234,18 @@ private: MapVector<BuiltinIndexListTy *, BuiltinTableEntries> SignatureListMap; }; -// OpenCL builtin test generator. This class processes the same TableGen input -// as BuiltinNameEmitter, but generates a .cl file that contains a call to each -// builtin function described in the .td input. -class OpenCLBuiltinTestEmitter { +/// Base class for emitting a file (e.g. header or test) from OpenCLBuiltins.td +class OpenCLBuiltinFileEmitterBase { public: - OpenCLBuiltinTestEmitter(RecordKeeper &Records, raw_ostream &OS) + OpenCLBuiltinFileEmitterBase(RecordKeeper &Records, raw_ostream &OS) : Records(Records), OS(OS) {} + virtual ~OpenCLBuiltinFileEmitterBase() = default; // Entrypoint to generate the functions for testing all OpenCL builtin // functions. - void emit(); + virtual void emit() = 0; -private: +protected: struct TypeFlags { TypeFlags() : IsConst(false), IsVolatile(false), IsPointer(false) {} bool IsConst : 1; @@ -282,6 +282,27 @@ private: expandTypesInSignature(const std::vector<Record *> &Signature, SmallVectorImpl<SmallVector<std::string, 2>> &Types); + // Emit extension enabling pragmas. + void emitExtensionSetup(); + + // Emit an #if guard for a Builtin's extension. Return the corresponding + // closing #endif, or an empty string if no extension #if guard was emitted. + std::string emitExtensionGuard(const Record *Builtin); + + // Emit an #if guard for a Builtin's language version. Return the + // corresponding closing #endif, or an empty string if no version #if guard + // was emitted. + std::string emitVersionGuard(const Record *Builtin); + + // Emit an #if guard for all type extensions required for the given type + // strings. Return the corresponding closing #endif, or an empty string + // if no extension #if guard was emitted. + StringRef + emitTypeExtensionGuards(const SmallVectorImpl<std::string> &Signature); + + // Map type strings to type extensions (e.g. "half2" -> "cl_khr_fp16"). + StringMap<StringRef> TypeExtMap; + // Contains OpenCL builtin functions and related information, stored as // Record instances. They are coming from the associated TableGen file. RecordKeeper &Records; @@ -290,10 +311,35 @@ private: raw_ostream &OS; }; +// OpenCL builtin test generator. This class processes the same TableGen input +// as BuiltinNameEmitter, but generates a .cl file that contains a call to each +// builtin function described in the .td input. +class OpenCLBuiltinTestEmitter : public OpenCLBuiltinFileEmitterBase { +public: + OpenCLBuiltinTestEmitter(RecordKeeper &Records, raw_ostream &OS) + : OpenCLBuiltinFileEmitterBase(Records, OS) {} + + // Entrypoint to generate the functions for testing all OpenCL builtin + // functions. + void emit() override; +}; + +// OpenCL builtin header generator. This class processes the same TableGen +// input as BuiltinNameEmitter, but generates a .h file that contains a +// prototype for each builtin function described in the .td input. +class OpenCLBuiltinHeaderEmitter : public OpenCLBuiltinFileEmitterBase { +public: + OpenCLBuiltinHeaderEmitter(RecordKeeper &Records, raw_ostream &OS) + : OpenCLBuiltinFileEmitterBase(Records, OS) {} + + // Entrypoint to generate the header. + void emit() override; +}; + } // namespace void BuiltinNameEmitter::Emit() { - emitSourceFileHeader("OpenCL Builtin handling", OS); + emitSourceFileHeader("OpenCL Builtin handling", OS, Records); OS << "#include \"llvm/ADT/StringRef.h\"\n"; OS << "using namespace clang;\n\n"; @@ -323,7 +369,7 @@ void BuiltinNameEmitter::ExtractEnumTypes(std::vector<Record *> &Types, raw_string_ostream SS(Output); for (const auto *T : Types) { - if (TypesSeen.find(T->getValueAsString("Name")) == TypesSeen.end()) { + if (!TypesSeen.contains(T->getValueAsString("Name"))) { SS << " OCLT_" + T->getValueAsString("Name") << ",\n"; // Save the type names in the same order as their enum value. Note that // the Record can be a VectorType or something else, only the name is @@ -464,7 +510,7 @@ void BuiltinNameEmitter::GetOverloads() { std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); for (const auto *B : Builtins) { StringRef BName = B->getValueAsString("Name"); - if (FctOverloadMap.find(BName) == FctOverloadMap.end()) { + if (!FctOverloadMap.contains(BName)) { FctOverloadMap.insert(std::make_pair( BName, std::vector<std::pair<const Record *, unsigned>>{})); } @@ -472,10 +518,10 @@ void BuiltinNameEmitter::GetOverloads() { auto Signature = B->getValueAsListOfDefs("Signature"); // Reuse signatures to avoid unnecessary duplicates. auto it = - std::find_if(SignaturesList.begin(), SignaturesList.end(), - [&](const std::pair<std::vector<Record *>, unsigned> &a) { - return a.first == Signature; - }); + llvm::find_if(SignaturesList, + [&](const std::pair<std::vector<Record *>, unsigned> &a) { + return a.first == Signature; + }); unsigned SignIndex; if (it == SignaturesList.end()) { VerifySignature(Signature, B); @@ -564,7 +610,7 @@ static unsigned short EncodeVersions(unsigned int MinVersion, } unsigned VersionIDs[] = {100, 110, 120, 200, 300}; - for (unsigned I = 0; I < sizeof(VersionIDs) / sizeof(VersionIDs[0]); I++) { + for (unsigned I = 0; I < std::size(VersionIDs); I++) { if (VersionIDs[I] >= MinVersion && VersionIDs[I] < MaxVersion) { Encoded |= 1 << I; } @@ -709,6 +755,20 @@ static std::pair<unsigned, unsigned> isOpenCLBuiltin(llvm::StringRef Name) { OS << "} // isOpenCLBuiltin\n"; } +// Emit an if-statement with an isMacroDefined call for each extension in +// the space-separated list of extensions. +static void EmitMacroChecks(raw_ostream &OS, StringRef Extensions) { + SmallVector<StringRef, 2> ExtVec; + Extensions.split(ExtVec, " "); + OS << " if ("; + for (StringRef Ext : ExtVec) { + if (Ext != ExtVec.front()) + OS << " && "; + OS << "S.getPreprocessor().isMacroDefined(\"" << Ext << "\")"; + } + OS << ") {\n "; +} + void BuiltinNameEmitter::EmitQualTypeFinder() { OS << R"( @@ -774,15 +834,24 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, << " case OCLAQ_None:\n" << " llvm_unreachable(\"Image without access qualifier\");\n"; for (const auto &Image : ITE.getValue()) { + StringRef Exts = + Image->getValueAsDef("Extension")->getValueAsString("ExtName"); OS << StringSwitch<const char *>( Image->getValueAsString("AccessQualifier")) .Case("RO", " case OCLAQ_ReadOnly:\n") .Case("WO", " case OCLAQ_WriteOnly:\n") - .Case("RW", " case OCLAQ_ReadWrite:\n") - << " QT.push_back(" + .Case("RW", " case OCLAQ_ReadWrite:\n"); + if (!Exts.empty()) { + OS << " "; + EmitMacroChecks(OS, Exts); + } + OS << " QT.push_back(" << Image->getValueAsDef("QTExpr")->getValueAsString("TypeExpr") - << ");\n" - << " break;\n"; + << ");\n"; + if (!Exts.empty()) { + OS << " }\n"; + } + OS << " break;\n"; } OS << " }\n" << " break;\n"; @@ -801,15 +870,14 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, // Collect all QualTypes for a single vector size into TypeList. OS << " SmallVector<QualType, " << BaseTypes.size() << "> TypeList;\n"; for (const auto *T : BaseTypes) { - StringRef Ext = + StringRef Exts = T->getValueAsDef("Extension")->getValueAsString("ExtName"); - if (!Ext.empty()) { - OS << " if (S.getPreprocessor().isMacroDefined(\"" << Ext - << "\")) {\n "; + if (!Exts.empty()) { + EmitMacroChecks(OS, Exts); } OS << " TypeList.push_back(" << T->getValueAsDef("QTExpr")->getValueAsString("TypeExpr") << ");\n"; - if (!Ext.empty()) { + if (!Exts.empty()) { OS << " }\n"; } } @@ -839,10 +907,10 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, for (const auto *T : Types) { // Check this is not an image type - if (ImageTypesMap.find(T->getValueAsString("Name")) != ImageTypesMap.end()) + if (ImageTypesMap.contains(T->getValueAsString("Name"))) continue; // Check we have not seen this Type - if (TypesSeen.find(T->getValueAsString("Name")) != TypesSeen.end()) + if (TypesSeen.contains(T->getValueAsString("Name"))) continue; TypesSeen.insert(std::make_pair(T->getValueAsString("Name"), true)); @@ -853,15 +921,14 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, // Emit the cases for non generic, non image types. OS << " case OCLT_" << T->getValueAsString("Name") << ":\n"; - StringRef Ext = T->getValueAsDef("Extension")->getValueAsString("ExtName"); - // If this type depends on an extension, ensure the extension macro is + StringRef Exts = T->getValueAsDef("Extension")->getValueAsString("ExtName"); + // If this type depends on an extension, ensure the extension macros are // defined. - if (!Ext.empty()) { - OS << " if (S.getPreprocessor().isMacroDefined(\"" << Ext - << "\")) {\n "; + if (!Exts.empty()) { + EmitMacroChecks(OS, Exts); } OS << " QT.push_back(" << QT->getValueAsString("TypeExpr") << ");\n"; - if (!Ext.empty()) { + if (!Exts.empty()) { OS << " }\n"; } OS << " break;\n"; @@ -923,9 +990,9 @@ static void OCL2Qual(Sema &S, const OpenCLTypeStruct &Ty, OS << "\n} // OCL2Qual\n"; } -std::string OpenCLBuiltinTestEmitter::getTypeString(const Record *Type, - TypeFlags Flags, - int VectorSize) const { +std::string OpenCLBuiltinFileEmitterBase::getTypeString(const Record *Type, + TypeFlags Flags, + int VectorSize) const { std::string S; if (Type->getValueAsBit("IsConst") || Flags.IsConst) { S += "const "; @@ -970,7 +1037,7 @@ std::string OpenCLBuiltinTestEmitter::getTypeString(const Record *Type, return S; } -void OpenCLBuiltinTestEmitter::getTypeLists( +void OpenCLBuiltinFileEmitterBase::getTypeLists( Record *Type, TypeFlags &Flags, std::vector<Record *> &TypeList, std::vector<int64_t> &VectorList) const { bool isGenType = Type->isSubClassOf("GenericType"); @@ -1003,7 +1070,7 @@ void OpenCLBuiltinTestEmitter::getTypeLists( VectorList.push_back(Type->getValueAsInt("VecWidth")); } -void OpenCLBuiltinTestEmitter::expandTypesInSignature( +void OpenCLBuiltinFileEmitterBase::expandTypesInSignature( const std::vector<Record *> &Signature, SmallVectorImpl<SmallVector<std::string, 2>> &Types) { // Find out if there are any GenTypes in this signature, and if so, calculate @@ -1021,7 +1088,16 @@ void OpenCLBuiltinTestEmitter::expandTypesInSignature( // Insert the Cartesian product of the types and vector sizes. for (const auto &Vector : VectorList) { for (const auto &Type : TypeList) { - ExpandedArg.push_back(getTypeString(Type, Flags, Vector)); + std::string FullType = getTypeString(Type, Flags, Vector); + ExpandedArg.push_back(FullType); + + // If the type requires an extension, add a TypeExtMap entry mapping + // the full type name to the extension. + StringRef Ext = + Type->getValueAsDef("Extension")->getValueAsString("ExtName"); + if (!Ext.empty() && !TypeExtMap.contains(FullType)) { + TypeExtMap.insert({FullType, Ext}); + } } } NumSignatures = std::max<unsigned>(NumSignatures, ExpandedArg.size()); @@ -1044,10 +1120,7 @@ void OpenCLBuiltinTestEmitter::expandTypesInSignature( } } -void OpenCLBuiltinTestEmitter::emit() { - emitSourceFileHeader("OpenCL Builtin exhaustive testing", OS); - - // Enable some extensions for testing. +void OpenCLBuiltinFileEmitterBase::emitExtensionSetup() { OS << R"( #pragma OPENCL EXTENSION cl_khr_fp16 : enable #pragma OPENCL EXTENSION cl_khr_fp64 : enable @@ -1058,6 +1131,93 @@ void OpenCLBuiltinTestEmitter::emit() { #pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable )"; +} + +std::string +OpenCLBuiltinFileEmitterBase::emitExtensionGuard(const Record *Builtin) { + StringRef Extensions = + Builtin->getValueAsDef("Extension")->getValueAsString("ExtName"); + if (Extensions.empty()) + return ""; + + OS << "#if"; + + SmallVector<StringRef, 2> ExtVec; + Extensions.split(ExtVec, " "); + bool isFirst = true; + for (StringRef Ext : ExtVec) { + if (!isFirst) { + OS << " &&"; + } + OS << " defined(" << Ext << ")"; + isFirst = false; + } + OS << "\n"; + + return "#endif // Extension\n"; +} + +std::string +OpenCLBuiltinFileEmitterBase::emitVersionGuard(const Record *Builtin) { + std::string OptionalEndif; + auto PrintOpenCLVersion = [this](int Version) { + OS << "CL_VERSION_" << (Version / 100) << "_" << ((Version % 100) / 10); + }; + int MinVersion = Builtin->getValueAsDef("MinVersion")->getValueAsInt("ID"); + if (MinVersion != 100) { + // OpenCL 1.0 is the default minimum version. + OS << "#if __OPENCL_C_VERSION__ >= "; + PrintOpenCLVersion(MinVersion); + OS << "\n"; + OptionalEndif = "#endif // MinVersion\n" + OptionalEndif; + } + int MaxVersion = Builtin->getValueAsDef("MaxVersion")->getValueAsInt("ID"); + if (MaxVersion) { + OS << "#if __OPENCL_C_VERSION__ < "; + PrintOpenCLVersion(MaxVersion); + OS << "\n"; + OptionalEndif = "#endif // MaxVersion\n" + OptionalEndif; + } + return OptionalEndif; +} + +StringRef OpenCLBuiltinFileEmitterBase::emitTypeExtensionGuards( + const SmallVectorImpl<std::string> &Signature) { + SmallSet<StringRef, 2> ExtSet; + + // Iterate over all types to gather the set of required TypeExtensions. + for (const auto &Ty : Signature) { + StringRef TypeExt = TypeExtMap.lookup(Ty); + if (!TypeExt.empty()) { + // The TypeExtensions are space-separated in the .td file. + SmallVector<StringRef, 2> ExtVec; + TypeExt.split(ExtVec, " "); + for (const auto Ext : ExtVec) { + ExtSet.insert(Ext); + } + } + } + + // Emit the #if only when at least one extension is required. + if (ExtSet.empty()) + return ""; + + OS << "#if "; + bool isFirst = true; + for (const auto Ext : ExtSet) { + if (!isFirst) + OS << " && "; + OS << "defined(" << Ext << ")"; + isFirst = false; + } + OS << "\n"; + return "#endif // TypeExtension\n"; +} + +void OpenCLBuiltinTestEmitter::emit() { + emitSourceFileHeader("OpenCL Builtin exhaustive testing", OS, Records); + + emitExtensionSetup(); // Ensure each test has a unique name by numbering them. unsigned TestID = 0; @@ -1071,44 +1231,13 @@ void OpenCLBuiltinTestEmitter::emit() { expandTypesInSignature(B->getValueAsListOfDefs("Signature"), FTypes); OS << "// Test " << Name << "\n"; - std::string OptionalEndif; - StringRef Extensions = - B->getValueAsDef("Extension")->getValueAsString("ExtName"); - if (!Extensions.empty()) { - OS << "#if"; - OptionalEndif = "#endif // Extension\n"; - SmallVector<StringRef, 2> ExtVec; - Extensions.split(ExtVec, " "); - bool isFirst = true; - for (StringRef Ext : ExtVec) { - if (!isFirst) { - OS << " &&"; - } - OS << " defined(" << Ext << ")"; - isFirst = false; - } - OS << "\n"; - } - auto PrintOpenCLVersion = [this](int Version) { - OS << "CL_VERSION_" << (Version / 100) << "_" << ((Version % 100) / 10); - }; - int MinVersion = B->getValueAsDef("MinVersion")->getValueAsInt("ID"); - if (MinVersion != 100) { - // OpenCL 1.0 is the default minimum version. - OS << "#if __OPENCL_C_VERSION__ >= "; - PrintOpenCLVersion(MinVersion); - OS << "\n"; - OptionalEndif = "#endif // MinVersion\n" + OptionalEndif; - } - int MaxVersion = B->getValueAsDef("MaxVersion")->getValueAsInt("ID"); - if (MaxVersion) { - OS << "#if __OPENCL_C_VERSION__ < "; - PrintOpenCLVersion(MaxVersion); - OS << "\n"; - OptionalEndif = "#endif // MaxVersion\n" + OptionalEndif; - } + std::string OptionalExtensionEndif = emitExtensionGuard(B); + std::string OptionalVersionEndif = emitVersionGuard(B); + for (const auto &Signature : FTypes) { + StringRef OptionalTypeExtEndif = emitTypeExtensionGuards(Signature); + // Emit function declaration. OS << Signature[0] << " test" << TestID++ << "_" << Name << "("; if (Signature.size() > 1) { @@ -1135,16 +1264,84 @@ void OpenCLBuiltinTestEmitter::emit() { // End of function body. OS << "}\n"; + OS << OptionalTypeExtEndif; } - OS << OptionalEndif << "\n"; + + OS << OptionalVersionEndif; + OS << OptionalExtensionEndif; } } +void OpenCLBuiltinHeaderEmitter::emit() { + emitSourceFileHeader("OpenCL Builtin declarations", OS, Records); + + emitExtensionSetup(); + + OS << R"( +#define __ovld __attribute__((overloadable)) +#define __conv __attribute__((convergent)) +#define __purefn __attribute__((pure)) +#define __cnfn __attribute__((const)) + +)"; + + // Iterate over all builtins; sort to follow order of definition in .td file. + std::vector<Record *> Builtins = Records.getAllDerivedDefinitions("Builtin"); + llvm::sort(Builtins, LessRecord()); + + for (const auto *B : Builtins) { + StringRef Name = B->getValueAsString("Name"); + + std::string OptionalExtensionEndif = emitExtensionGuard(B); + std::string OptionalVersionEndif = emitVersionGuard(B); + + SmallVector<SmallVector<std::string, 2>, 4> FTypes; + expandTypesInSignature(B->getValueAsListOfDefs("Signature"), FTypes); + + for (const auto &Signature : FTypes) { + StringRef OptionalTypeExtEndif = emitTypeExtensionGuards(Signature); + + // Emit function declaration. + OS << Signature[0] << " __ovld "; + if (B->getValueAsBit("IsConst")) + OS << "__cnfn "; + if (B->getValueAsBit("IsPure")) + OS << "__purefn "; + if (B->getValueAsBit("IsConv")) + OS << "__conv "; + + OS << Name << "("; + if (Signature.size() > 1) { + for (unsigned I = 1; I < Signature.size(); I++) { + if (I != 1) + OS << ", "; + OS << Signature[I]; + } + } + OS << ");\n"; + + OS << OptionalTypeExtEndif; + } + + OS << OptionalVersionEndif; + OS << OptionalExtensionEndif; + } + + OS << "\n// Disable any extensions we may have enabled previously.\n" + "#pragma OPENCL EXTENSION all : disable\n"; +} + void clang::EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { BuiltinNameEmitter NameChecker(Records, OS); NameChecker.Emit(); } +void clang::EmitClangOpenCLBuiltinHeader(RecordKeeper &Records, + raw_ostream &OS) { + OpenCLBuiltinHeaderEmitter HeaderFileGenerator(Records, OS); + HeaderFileGenerator.emit(); +} + void clang::EmitClangOpenCLBuiltinTests(RecordKeeper &Records, raw_ostream &OS) { OpenCLBuiltinTestEmitter TestFileGenerator(Records, OS); diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangOptionDocEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangOptionDocEmitter.cpp index 0e079b6b505a..a4095950ca97 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangOptionDocEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangOptionDocEmitter.cpp @@ -31,13 +31,40 @@ struct DocumentedGroup; struct Documentation { std::vector<DocumentedGroup> Groups; std::vector<DocumentedOption> Options; + + bool empty() { + return Groups.empty() && Options.empty(); + } }; struct DocumentedGroup : Documentation { Record *Group; }; +static bool hasFlag(const Record *Option, StringRef OptionFlag, + StringRef FlagsField) { + for (const Record *Flag : Option->getValueAsListOfDefs(FlagsField)) + if (Flag->getName() == OptionFlag) + return true; + if (const DefInit *DI = dyn_cast<DefInit>(Option->getValueInit("Group"))) + for (const Record *Flag : DI->getDef()->getValueAsListOfDefs(FlagsField)) + if (Flag->getName() == OptionFlag) + return true; + return false; +} + +static bool isOptionVisible(const Record *Option, const Record *DocInfo) { + for (StringRef IgnoredFlag : DocInfo->getValueAsListOfStrings("IgnoreFlags")) + if (hasFlag(Option, IgnoredFlag, "Flags")) + return false; + for (StringRef Mask : DocInfo->getValueAsListOfStrings("VisibilityMask")) + if (hasFlag(Option, Mask, "Visibility")) + return true; + return false; +} + // Reorganize the records into a suitable form for emitting documentation. -Documentation extractDocumentation(RecordKeeper &Records) { +Documentation extractDocumentation(RecordKeeper &Records, + const Record *DocInfo) { Documentation Result; // Build the tree of groups. The root in the tree is the fake option group @@ -124,12 +151,15 @@ Documentation extractDocumentation(RecordKeeper &Records) { D.Groups.back().Group = G; Documentation &Base = D.Groups.back(); Base = DocumentationForGroup(G); + if (Base.empty()) + D.Groups.pop_back(); } auto &Options = OptionsInGroup[R]; llvm::sort(Options, CompareByName); for (Record *O : Options) - D.Options.push_back(DocumentationForOption(O)); + if (isOptionVisible(O, DocInfo)) + D.Options.push_back(DocumentationForOption(O)); return D; }; @@ -161,25 +191,10 @@ unsigned getNumArgsForKind(Record *OptionKind, const Record *Option) { .Default(0); } -bool hasFlag(const Record *OptionOrGroup, StringRef OptionFlag) { - for (const Record *Flag : OptionOrGroup->getValueAsListOfDefs("Flags")) - if (Flag->getName() == OptionFlag) - return true; - return false; -} - -bool isExcluded(const Record *OptionOrGroup, const Record *DocInfo) { - // FIXME: Provide a flag to specify the set of exclusions. - for (StringRef Exclusion : DocInfo->getValueAsListOfStrings("ExcludedFlags")) - if (hasFlag(OptionOrGroup, Exclusion)) - return true; - return false; -} - std::string escapeRST(StringRef Str) { std::string Out; for (auto K : Str) { - if (StringRef("`*|_[]\\").count(K)) + if (StringRef("`*|[]\\").count(K)) Out.push_back('\\'); Out.push_back(K); } @@ -238,6 +253,8 @@ void emitOptionWithArgs(StringRef Prefix, const Record *Option, } } +constexpr StringLiteral DefaultMetaVarName = "<arg>"; + void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) { // Find the arguments to list after the option. unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option); @@ -247,7 +264,7 @@ void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) { if (HasMetaVarName) Args.push_back(std::string(Option->getValueAsString("MetaVarName"))); else if (NumArgs == 1) - Args.push_back("<arg>"); + Args.push_back(DefaultMetaVarName.str()); // Fill up arguments if this option didn't provide a meta var name or it // supports an unlimited number of arguments. We can't see how many arguments @@ -294,14 +311,13 @@ void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo, F(Option.Option); for (auto *Alias : Option.Aliases) - if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option)) + if (isOptionVisible(Alias, DocInfo) && + canSphinxCopeWithOption(Option.Option)) F(Alias); } void emitOption(const DocumentedOption &Option, const Record *DocInfo, raw_ostream &OS) { - if (isExcluded(Option.Option, DocInfo)) - return; if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" || Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT") return; @@ -341,8 +357,30 @@ void emitOption(const DocumentedOption &Option, const Record *DocInfo, OS << "\n\n"; // Emit the description, if we have one. + const Record *R = Option.Option; std::string Description = - getRSTStringWithTextFallback(Option.Option, "DocBrief", "HelpText"); + getRSTStringWithTextFallback(R, "DocBrief", "HelpText"); + + if (!isa<UnsetInit>(R->getValueInit("Values"))) { + if (!Description.empty() && Description.back() != '.') + Description.push_back('.'); + + StringRef MetaVarName; + if (!isa<UnsetInit>(R->getValueInit("MetaVarName"))) + MetaVarName = R->getValueAsString("MetaVarName"); + else + MetaVarName = DefaultMetaVarName; + + SmallVector<StringRef> Values; + SplitString(R->getValueAsString("Values"), Values, ","); + Description += (" " + MetaVarName + " must be '").str(); + if (Values.size() > 1) { + Description += join(Values.begin(), Values.end() - 1, "', '"); + Description += "' or '"; + } + Description += (Values.back() + "'.").str(); + } + if (!Description.empty()) OS << Description << "\n\n"; } @@ -352,9 +390,6 @@ void emitDocumentation(int Depth, const Documentation &Doc, void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo, raw_ostream &OS) { - if (isExcluded(Group.Group, DocInfo)) - return; - emitHeading(Depth, getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS); @@ -388,5 +423,5 @@ void clang::EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) { OS << DocInfo->getValueAsString("Intro") << "\n"; OS << ".. program:: " << DocInfo->getValueAsString("Program") << "\n"; - emitDocumentation(0, extractDocumentation(Records), DocInfo, OS); + emitDocumentation(0, extractDocumentation(Records, DocInfo), DocInfo, OS); } diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp index 00d88274fc38..2a2e466ae197 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp @@ -24,28 +24,29 @@ using namespace llvm; // Static Analyzer Checkers Tables generation //===----------------------------------------------------------------------===// -static std::string getPackageFullName(const Record *R); +static std::string getPackageFullName(const Record *R, StringRef Sep = "."); -static std::string getParentPackageFullName(const Record *R) { +static std::string getParentPackageFullName(const Record *R, + StringRef Sep = ".") { std::string name; if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) - name = getPackageFullName(DI->getDef()); + name = getPackageFullName(DI->getDef(), Sep); return name; } -static std::string getPackageFullName(const Record *R) { - std::string name = getParentPackageFullName(R); +static std::string getPackageFullName(const Record *R, StringRef Sep) { + std::string name = getParentPackageFullName(R, Sep); if (!name.empty()) - name += "."; + name += Sep; assert(!R->getValueAsString("PackageName").empty()); name += R->getValueAsString("PackageName"); return name; } -static std::string getCheckerFullName(const Record *R) { - std::string name = getParentPackageFullName(R); +static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") { + std::string name = getParentPackageFullName(R, Sep); if (!name.empty()) - name += "."; + name += Sep; assert(!R->getValueAsString("CheckerName").empty()); name += R->getValueAsString("CheckerName"); return name; @@ -74,20 +75,18 @@ static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) } static std::string getCheckerDocs(const Record &R) { - StringRef LandingPage; - if (BitsInit *BI = R.getValueAsBitsInit("Documentation")) { - uint64_t V = getValueFromBitsInit(BI, R); - if (V == 1) - LandingPage = "available_checks.html"; - else if (V == 2) - LandingPage = "alpha_checks.html"; - } - - if (LandingPage.empty()) + const BitsInit *BI = R.getValueAsBitsInit("Documentation"); + if (!BI) + PrintFatalError(R.getLoc(), "missing Documentation<...> member for " + + getCheckerFullName(&R)); + + // Ignore 'Documentation<NotDocumented>' checkers. + if (getValueFromBitsInit(BI, R) == 0) return ""; - return (llvm::Twine("https://clang-analyzer.llvm.org/") + LandingPage + "#" + - getCheckerFullName(&R)) + std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower(); + return (llvm::Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") + + CheckerFullName) .str(); } @@ -220,7 +219,7 @@ void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { // - DESCRIPTION // - DEFAULT: The default value for this option. // - // The full option can be specified in the command like like this: + // The full option can be specified in the command like this: // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE OS << "\n" "#ifdef GET_PACKAGE_OPTIONS\n"; @@ -320,7 +319,7 @@ void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { // - DESCRIPTION // - DEFAULT: The default value for this option. // - // The full option can be specified in the command like like this: + // The full option can be specified in the command like this: // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE OS << "\n" "#ifdef GET_CHECKER_OPTIONS\n"; diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp index a940edbb1d24..9720d5873184 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangSyntaxEmitter.cpp @@ -129,7 +129,7 @@ struct SyntaxConstraint { void clang::EmitClangSyntaxNodeList(llvm::RecordKeeper &Records, llvm::raw_ostream &OS) { - llvm::emitSourceFileHeader("Syntax tree node list", OS); + llvm::emitSourceFileHeader("Syntax tree node list", OS, Records); Hierarchy H(Records); OS << R"cpp( #ifndef NODE @@ -188,7 +188,7 @@ static void printDoc(llvm::StringRef Doc, llvm::raw_ostream &OS) { void clang::EmitClangSyntaxNodeClasses(llvm::RecordKeeper &Records, llvm::raw_ostream &OS) { - llvm::emitSourceFileHeader("Syntax tree node list", OS); + llvm::emitSourceFileHeader("Syntax tree node list", OS, Records); Hierarchy H(Records); OS << "\n// Forward-declare node types so we don't have to carefully " diff --git a/contrib/llvm-project/clang/utils/TableGen/ClangTypeNodesEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/ClangTypeNodesEmitter.cpp index 690042f3200e..66bdf5e67602 100644 --- a/contrib/llvm-project/clang/utils/TableGen/ClangTypeNodesEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/ClangTypeNodesEmitter.cpp @@ -104,7 +104,7 @@ void TypeNodeEmitter::emit() { if (Types.empty()) PrintFatalError("no Type records in input!"); - emitSourceFileHeader("An x-macro database of Clang type nodes", Out); + emitSourceFileHeader("An x-macro database of Clang type nodes", Out, Records); // Preamble addMacroToUndef(TypeMacroName); diff --git a/contrib/llvm-project/clang/utils/TableGen/MveEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/MveEmitter.cpp index 091af2dc52a1..496cb10d14f2 100644 --- a/contrib/llvm-project/clang/utils/TableGen/MveEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/MveEmitter.cpp @@ -212,6 +212,7 @@ public: std::string llvmName() const override { return "llvm::PointerType::getUnqual(" + Pointee->llvmName() + ")"; } + const Type *getPointeeType() const { return Pointee; } static bool classof(const Type *T) { return T->typeKind() == TypeKind::Pointer; @@ -349,13 +350,8 @@ public: 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::FixedVectorType::get(Builder.getInt1Ty(), " + - utostr(ModifiedLanes) + ")"; + return "llvm::FixedVectorType::get(Builder.getInt1Ty(), " + utostr(Lanes) + + ")"; } static bool classof(const Type *T) { @@ -707,12 +703,14 @@ public: class AddressResult : public Result { public: Ptr Arg; + const Type *Ty; unsigned Align; - AddressResult(Ptr Arg, unsigned Align) : Arg(Arg), Align(Align) {} + AddressResult(Ptr Arg, const Type *Ty, unsigned Align) + : Arg(Arg), Ty(Ty), Align(Align) {} void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc) const override { - OS << "Address(" << Arg->varname() << ", CharUnits::fromQuantity(" - << Align << "))"; + OS << "Address(" << Arg->varname() << ", " << Ty->llvmName() + << ", CharUnits::fromQuantity(" << Align << "))"; } std::string typeName() const override { return "Address"; @@ -884,7 +882,7 @@ public: } else if (V->varnameUsed()) { std::string Type = V->typeName(); OS << V->typeName(); - if (!StringRef(Type).endswith("*")) + if (!StringRef(Type).ends_with("*")) OS << " "; OS << V->varname() << " = "; } @@ -898,7 +896,7 @@ public: llvm::APInt i = iOrig.trunc(64); SmallString<40> s; i.toString(s, 16, true, true); - return std::string(s.str()); + return std::string(s); } std::string genSema() const { @@ -1194,13 +1192,21 @@ Result::Ptr EmitterBase::getCodeForDag(DagInit *D, const Result::Scope &Scope, if (D->getNumArgs() != 2) PrintFatalError("'address' should have two arguments"); Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param); + + const Type *Ty = nullptr; + if (auto *DI = dyn_cast<DagInit>(D->getArg(0))) + if (auto *PTy = dyn_cast<PointerType>(getType(DI->getOperator(), Param))) + Ty = PTy->getPointeeType(); + if (!Ty) + PrintFatalError("'address' pointer argument should be a pointer"); + unsigned Alignment; if (auto *II = dyn_cast<IntInit>(D->getArg(1))) { Alignment = II->getValue(); } else { PrintFatalError("'address' alignment argument should be an integer"); } - return std::make_shared<AddressResult>(Arg, Alignment); + return std::make_shared<AddressResult>(Arg, Ty, Alignment); } else if (Op->getName() == "unsignedflag") { if (D->getNumArgs() != 1) PrintFatalError("unsignedflag should have exactly one argument"); @@ -1494,8 +1500,7 @@ protected: class raw_self_contained_string_ostream : private string_holder, public raw_string_ostream { public: - raw_self_contained_string_ostream() - : string_holder(), raw_string_ostream(S) {} + raw_self_contained_string_ostream() : raw_string_ostream(S) {} }; const char LLVMLicenseHeader[] = @@ -1675,7 +1680,7 @@ void EmitterBase::EmitBuiltinCG(raw_ostream &OS) { for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) { StringRef Type = MG.ParamTypes[i]; OS << " " << Type; - if (!Type.endswith("*")) + if (!Type.ends_with("*")) OS << " "; OS << " Param" << utostr(i) << ";\n"; } @@ -1828,7 +1833,7 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { // prototype. std::string RetTypeName = Int.returnType()->cName(); - if (!StringRef(RetTypeName).endswith("*")) + if (!StringRef(RetTypeName).ends_with("*")) RetTypeName += " "; std::vector<std::string> ArgTypeNames; @@ -1941,8 +1946,8 @@ void MveEmitter::EmitHeader(raw_ostream &OS) { void MveEmitter::EmitBuiltinDef(raw_ostream &OS) { for (const auto &kv : ACLEIntrinsics) { const ACLEIntrinsic &Int = *kv.second; - OS << "TARGET_HEADER_BUILTIN(__builtin_arm_mve_" << Int.fullName() - << ", \"\", \"n\", \"arm_mve.h\", ALL_LANGUAGES, \"\")\n"; + OS << "BUILTIN(__builtin_arm_mve_" << Int.fullName() + << ", \"\", \"n\")\n"; } std::set<std::string> ShortNamesSeen; @@ -2073,7 +2078,7 @@ void CdeEmitter::EmitHeader(raw_ostream &OS) { // Make strings for the types involved in the function's // prototype. std::string RetTypeName = Int.returnType()->cName(); - if (!StringRef(RetTypeName).endswith("*")) + if (!StringRef(RetTypeName).ends_with("*")) RetTypeName += " "; std::vector<std::string> ArgTypeNames; @@ -2151,8 +2156,8 @@ void CdeEmitter::EmitBuiltinDef(raw_ostream &OS) { if (kv.second->headerOnly()) continue; const ACLEIntrinsic &Int = *kv.second; - OS << "TARGET_HEADER_BUILTIN(__builtin_arm_cde_" << Int.fullName() - << ", \"\", \"ncU\", \"arm_cde.h\", ALL_LANGUAGES, \"\")\n"; + OS << "BUILTIN(__builtin_arm_cde_" << Int.fullName() + << ", \"\", \"ncU\")\n"; } } diff --git a/contrib/llvm-project/clang/utils/TableGen/NeonEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/NeonEmitter.cpp index f0da1a7d2f4e..53334016c180 100644 --- a/contrib/llvm-project/clang/utils/TableGen/NeonEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/NeonEmitter.cpp @@ -26,8 +26,6 @@ #include "TableGenBackends.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -45,6 +43,7 @@ #include <cstdint> #include <deque> #include <map> +#include <optional> #include <set> #include <sstream> #include <string> @@ -292,7 +291,7 @@ class Variable { std::string N; public: - Variable() : T(Type::getVoid()), N("") {} + Variable() : T(Type::getVoid()) {} Variable(Type T, std::string N) : T(std::move(T)), N(std::move(N)) {} Type getType() const { return T; } @@ -321,8 +320,10 @@ class Intrinsic { /// The list of DAGs for the body. May be empty, in which case we should /// emit a builtin call. ListInit *Body; - /// The architectural #ifdef guard. - std::string Guard; + /// The architectural ifdef guard. + std::string ArchGuard; + /// The architectural target() guard. + std::string TargetGuard; /// Set if the Unavailable bit is 1. This means we don't generate a body, /// just an "unavailable" attribute on a declaration. bool IsUnavailable; @@ -368,9 +369,9 @@ class Intrinsic { public: Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS, TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter, - StringRef Guard, bool IsUnavailable, bool BigEndianSafe) + StringRef ArchGuard, StringRef TargetGuard, bool IsUnavailable, bool BigEndianSafe) : R(R), Name(Name.str()), OutTS(OutTS), InTS(InTS), CK(CK), Body(Body), - Guard(Guard.str()), IsUnavailable(IsUnavailable), + ArchGuard(ArchGuard.str()), TargetGuard(TargetGuard.str()), IsUnavailable(IsUnavailable), BigEndianSafe(BigEndianSafe), PolymorphicKeyType(0), NeededEarly(false), UseMacro(false), BaseType(OutTS, "."), InBaseType(InTS, "."), Emitter(Emitter) { @@ -382,13 +383,13 @@ public: StringRef Mods = getNextModifiers(Proto, Pos); while (!Mods.empty()) { Types.emplace_back(InTS, Mods); - if (Mods.find('!') != StringRef::npos) + if (Mods.contains('!')) PolymorphicKeyType = Types.size() - 1; Mods = getNextModifiers(Proto, Pos); } - for (auto Type : Types) { + for (const auto &Type : Types) { // If this builtin takes an immediate argument, we need to #define it rather // than use a standard declaration, so that SemaChecking can range check // the immediate passed by the user. @@ -411,14 +412,14 @@ public: /// transitive closure. const std::set<Intrinsic *> &getDependencies() const { return Dependencies; } /// Get the architectural guard string (#ifdef). - std::string getGuard() const { return Guard; } + std::string getArchGuard() const { return ArchGuard; } + std::string getTargetGuard() const { return TargetGuard; } /// Get the non-mangled name. std::string getName() const { return Name; } /// Return true if the intrinsic takes an immediate operand. bool hasImmediate() const { - return std::any_of(Types.begin(), Types.end(), - [](const Type &T) { return T.isImmediate(); }); + return llvm::any_of(Types, [](const Type &T) { return T.isImmediate(); }); } /// Return the parameter index of the immediate operand. @@ -442,7 +443,7 @@ public: /// Return the index that parameter PIndex will sit at /// in a generated function call. This is often just PIndex, /// but may not be as things such as multiple-vector operands - /// and sret parameters need to be taken into accont. + /// and sret parameters need to be taken into account. unsigned getGeneratedParamIdx(unsigned PIndex) { unsigned Idx = 0; if (getReturnType().getNumVectors() > 1) @@ -460,9 +461,11 @@ public: void setNeededEarly() { NeededEarly = true; } bool operator<(const Intrinsic &Other) const { - // Sort lexicographically on a two-tuple (Guard, Name) - if (Guard != Other.Guard) - return Guard < Other.Guard; + // Sort lexicographically on a three-tuple (ArchGuard, TargetGuard, Name) + if (ArchGuard != Other.ArchGuard) + return ArchGuard < Other.ArchGuard; + if (TargetGuard != Other.TargetGuard) + return TargetGuard < Other.TargetGuard; return Name < Other.Name; } @@ -503,6 +506,7 @@ private: void emitBody(StringRef CallPrefix); void emitShadowedArgs(); void emitArgumentReversal(); + void emitReturnVarDecl(); void emitReturnReversal(); void emitReverseVariable(Variable &Dest, Variable &Src); void emitNewLine(); @@ -546,6 +550,8 @@ class NeonEmitter { void createIntrinsic(Record *R, SmallVectorImpl<Intrinsic *> &Out); void genBuiltinsDef(raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs); + void genStreamingSVECompatibleList(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs); void genOverloadTypeCheckCode(raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs); void genIntrinsicRangeCheckCode(raw_ostream &OS, @@ -555,7 +561,7 @@ public: /// Called by Intrinsic - this attempts to get an intrinsic that takes /// the given types as arguments. Intrinsic &getIntrinsic(StringRef Name, ArrayRef<Type> Types, - Optional<std::string> MangledName); + std::optional<std::string> MangledName); /// Called by Intrinsic - returns a globally-unique number. unsigned getUniqueNumber() { return UniqueNumber++; } @@ -589,6 +595,8 @@ public: // Emit arm_bf16.h.inc void runBF16(raw_ostream &o); + void runVectorTypes(raw_ostream &o); + // Emit all the __builtin prototypes used in arm_neon.h, arm_fp16.h and // arm_bf16.h void runHeader(raw_ostream &o); @@ -732,17 +740,17 @@ Type Type::fromTypedefName(StringRef Name) { Name = Name.drop_front(); } - if (Name.startswith("float")) { + if (Name.starts_with("float")) { T.Kind = Float; Name = Name.drop_front(5); - } else if (Name.startswith("poly")) { + } else if (Name.starts_with("poly")) { T.Kind = Poly; Name = Name.drop_front(4); - } else if (Name.startswith("bfloat")) { + } else if (Name.starts_with("bfloat")) { T.Kind = BFloat16; Name = Name.drop_front(6); } else { - assert(Name.startswith("int")); + assert(Name.starts_with("int")); Name = Name.drop_front(3); } @@ -783,7 +791,7 @@ Type Type::fromTypedefName(StringRef Name) { Name = Name.drop_front(I); } - assert(Name.startswith("_t") && "Malformed typedef!"); + assert(Name.starts_with("_t") && "Malformed typedef!"); return T; } @@ -817,19 +825,19 @@ void Type::applyTypespec(bool &Quad) { break; case 'h': Kind = Float; - LLVM_FALLTHROUGH; + [[fallthrough]]; case 's': ElementBitwidth = 16; break; case 'f': Kind = Float; - LLVM_FALLTHROUGH; + [[fallthrough]]; case 'i': ElementBitwidth = 32; break; case 'd': Kind = Float; - LLVM_FALLTHROUGH; + [[fallthrough]]; case 'l': ElementBitwidth = 64; break; @@ -951,7 +959,7 @@ std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const { char typeCode = '\0'; bool printNumber = true; - if (CK == ClassB) + if (CK == ClassB && TargetGuard == "") return ""; if (T.isBFloat16()) @@ -975,7 +983,7 @@ std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const { break; } } - if (CK == ClassB) { + if (CK == ClassB && TargetGuard == "") { typeCode = '\0'; } @@ -1077,7 +1085,7 @@ std::string Intrinsic::mangleName(std::string Name, ClassKind LocalCK) const { S += "_" + getInstTypeCode(InBaseType, LocalCK); } - if (LocalCK == ClassB) + if (LocalCK == ClassB && TargetGuard == "") S += "_v"; // Insert a 'q' before the first '_' character so that it ends up before @@ -1137,10 +1145,14 @@ void Intrinsic::initVariables() { } void Intrinsic::emitPrototype(StringRef NamePrefix) { - if (UseMacro) + if (UseMacro) { OS << "#define "; - else - OS << "__ai " << Types[0].str() << " "; + } else { + OS << "__ai "; + if (TargetGuard != "") + OS << "__attribute__((target(\"" << TargetGuard << "\"))) "; + OS << Types[0].str() << " "; + } OS << NamePrefix.str() << mangleName(Name, ClassS) << "("; @@ -1229,6 +1241,15 @@ void Intrinsic::emitArgumentReversal() { } } +void Intrinsic::emitReturnVarDecl() { + assert(RetVar.getType() == Types[0]); + // Create a return variable, if we're not void. + if (!RetVar.getType().isVoid()) { + OS << " " << RetVar.getType().str() << " " << RetVar.getName() << ";"; + emitNewLine(); + } +} + void Intrinsic::emitReturnReversal() { if (isBigEndianSafe()) return; @@ -1271,9 +1292,8 @@ void Intrinsic::emitShadowedArgs() { } bool Intrinsic::protoHasScalar() const { - return std::any_of(Types.begin(), Types.end(), [](const Type &T) { - return T.isScalar() && !T.isImmediate(); - }); + return llvm::any_of( + Types, [](const Type &T) { return T.isScalar() && !T.isImmediate(); }); } void Intrinsic::emitBodyAsBuiltinCall() { @@ -1308,7 +1328,7 @@ void Intrinsic::emitBodyAsBuiltinCall() { if (LocalCK == ClassB) { Type T2 = T; T2.makeOneVector(); - T2.makeInteger(8, /*Signed=*/true); + T2.makeInteger(8, /*Sign=*/true); Cast = "(" + T2.str() + ")"; } @@ -1355,13 +1375,6 @@ void Intrinsic::emitBodyAsBuiltinCall() { void Intrinsic::emitBody(StringRef CallPrefix) { std::vector<std::string> Lines; - assert(RetVar.getType() == Types[0]); - // Create a return variable, if we're not void. - if (!RetVar.getType().isVoid()) { - OS << " " << RetVar.getType().str() << " " << RetVar.getName() << ";"; - emitNewLine(); - } - if (!Body || Body->getValues().empty()) { // Nothing specific to output - must output a builtin. emitBodyAsBuiltinCall(); @@ -1462,7 +1475,7 @@ Intrinsic::DagEmitter::emitDagCall(DagInit *DI, bool MatchMangledName) { N = SI->getAsUnquotedString(); else N = emitDagArg(DI->getArg(0), "").second; - Optional<std::string> MangledName; + std::optional<std::string> MangledName; if (MatchMangledName) { if (Intr.getRecord()->getValueAsBit("isLaneQ")) N += "q"; @@ -1475,7 +1488,7 @@ Intrinsic::DagEmitter::emitDagCall(DagInit *DI, bool MatchMangledName) { Intr.Dependencies.insert(&Callee); // Now create the call itself. - std::string S = ""; + std::string S; if (!Callee.isBigEndianSafe()) S += CallPrefix.str(); S += Callee.getMangledName(true) + "("; @@ -1641,12 +1654,12 @@ std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagShuffle(DagInit *DI){ std::make_unique<Rev>(Arg1.first.getElementSizeInBits())); ST.addExpander("MaskExpand", std::make_unique<MaskExpander>(Arg1.first.getNumElements())); - ST.evaluate(DI->getArg(2), Elts, None); + ST.evaluate(DI->getArg(2), Elts, std::nullopt); std::string S = "__builtin_shufflevector(" + Arg1.second + ", " + Arg2.second; for (auto &E : Elts) { StringRef Name = E->getName(); - assert_with_loc(Name.startswith("sv"), + assert_with_loc(Name.starts_with("sv"), "Incorrect element kind in shuffle mask!"); S += ", " + Name.drop_front(2).str(); } @@ -1851,6 +1864,9 @@ void Intrinsic::generateImpl(bool ReverseArguments, OS << " __attribute__((unavailable));"; } else { emitOpeningBrace(); + // Emit return variable declaration first as to not trigger + // -Wdeclaration-after-statement. + emitReturnVarDecl(); emitShadowedArgs(); if (ReverseArguments) emitArgumentReversal(); @@ -1869,6 +1885,9 @@ void Intrinsic::indexBody() { CurrentRecord = R; initVariables(); + // Emit return variable declaration first as to not trigger + // -Wdeclaration-after-statement. + emitReturnVarDecl(); emitBody(""); OS.str(""); @@ -1880,7 +1899,7 @@ void Intrinsic::indexBody() { //===----------------------------------------------------------------------===// Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types, - Optional<std::string> MangledName) { + std::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()); @@ -1916,10 +1935,9 @@ Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types, continue; unsigned ArgNum = 0; - bool MatchingArgumentTypes = - std::all_of(Types.begin(), Types.end(), [&](const auto &Type) { - return Type == I.getParamType(ArgNum++); - }); + bool MatchingArgumentTypes = llvm::all_of(Types, [&](const auto &Type) { + return Type == I.getParamType(ArgNum++); + }); if (MatchingArgumentTypes) GoodVec.push_back(&I); @@ -1939,7 +1957,8 @@ void NeonEmitter::createIntrinsic(Record *R, std::string Types = std::string(R->getValueAsString("Types")); Record *OperationRec = R->getValueAsDef("Operation"); bool BigEndianSafe = R->getValueAsBit("BigEndianSafe"); - std::string Guard = std::string(R->getValueAsString("ArchGuard")); + std::string ArchGuard = std::string(R->getValueAsString("ArchGuard")); + std::string TargetGuard = std::string(R->getValueAsString("TargetGuard")); bool IsUnavailable = OperationRec->getValueAsBit("Unavailable"); std::string CartesianProductWith = std::string(R->getValueAsString("CartesianProductWith")); @@ -1981,7 +2000,7 @@ void NeonEmitter::createIntrinsic(Record *R, for (auto &I : NewTypeSpecs) { Entry.emplace_back(R, Name, Proto, I.first, I.second, CK, Body, *this, - Guard, IsUnavailable, BigEndianSafe); + ArchGuard, TargetGuard, IsUnavailable, BigEndianSafe); Out.push_back(&Entry.back()); } @@ -1996,22 +2015,55 @@ void NeonEmitter::genBuiltinsDef(raw_ostream &OS, // We only want to emit a builtin once, and we want to emit them in // alphabetical order, so use a std::set. - std::set<std::string> Builtins; + std::set<std::pair<std::string, std::string>> Builtins; for (auto *Def : Defs) { if (Def->hasBody()) continue; - std::string S = "BUILTIN(__builtin_neon_" + Def->getMangledName() + ", \""; - + std::string S = "__builtin_neon_" + Def->getMangledName() + ", \""; S += Def->getBuiltinTypeStr(); - S += "\", \"n\")"; + S += "\", \"n\""; - Builtins.insert(S); + Builtins.emplace(S, Def->getTargetGuard()); + } + + for (auto &S : Builtins) { + if (S.second == "") + OS << "BUILTIN("; + else + OS << "TARGET_BUILTIN("; + OS << S.first; + if (S.second == "") + OS << ")\n"; + else + OS << ", \"" << S.second << "\")\n"; } - for (auto &S : Builtins) - OS << S << "\n"; + OS << "#endif\n\n"; +} + +void NeonEmitter::genStreamingSVECompatibleList( + raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs) { + OS << "#ifdef GET_NEON_STREAMING_COMPAT_FLAG\n"; + + std::set<std::string> Emitted; + for (auto *Def : Defs) { + // If the def has a body (that is, it has Operation DAGs), it won't call + // __builtin_neon_* so we don't need to generate a definition for it. + if (Def->hasBody()) + continue; + + std::string Name = Def->getMangledName(); + if (Emitted.find(Name) != Emitted.end()) + continue; + + // FIXME: We should make exceptions here for some NEON builtins that are + // permitted in streaming mode. + OS << "case NEON::BI__builtin_neon_" << Name + << ": BuiltinType = ArmNonStreaming; break;\n"; + Emitted.insert(Name); + } OS << "#endif\n\n"; } @@ -2025,10 +2077,10 @@ void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS, // definitions may extend the number of permitted types (i.e. augment the // Mask). Use std::map to avoid sorting the table by hash number. struct OverloadInfo { - uint64_t Mask; - int PtrArgNum; - bool HasConstPtr; - OverloadInfo() : Mask(0ULL), PtrArgNum(0), HasConstPtr(false) {} + uint64_t Mask = 0ULL; + int PtrArgNum = 0; + bool HasConstPtr = false; + OverloadInfo() = default; }; std::map<std::string, OverloadInfo> OverloadMap; @@ -2062,12 +2114,13 @@ void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS, std::string Name = Def->getName(); // Omit type checking for the pointer arguments of vld1_lane, vld1_dup, - // and vst1_lane intrinsics. Using a pointer to the vector element - // type with one of those operations causes codegen to select an aligned - // load/store instruction. If you want an unaligned operation, - // the pointer argument needs to have less alignment than element type, - // so just accept any pointer type. - if (Name == "vld1_lane" || Name == "vld1_dup" || Name == "vst1_lane") { + // vst1_lane, vldap1_lane, and vstl1_lane intrinsics. Using a pointer to + // the vector element type with one of those operations causes codegen to + // select an aligned load/store instruction. If you want an unaligned + // operation, the pointer argument needs to have less alignment than element + // type, so just accept any pointer type. + if (Name == "vld1_lane" || Name == "vld1_dup" || Name == "vst1_lane" || + Name == "vldap1_lane" || Name == "vstl1_lane") { PtrArgNum = -1; HasConstPtr = false; } @@ -2197,6 +2250,8 @@ void NeonEmitter::runHeader(raw_ostream &OS) { // Generate ARM overloaded type checking code for SemaChecking.cpp genOverloadTypeCheckCode(OS, Defs); + genStreamingSVECompatibleList(OS, Defs); + // Generate ARM range checking code for shift/lane immediates. genIntrinsicRangeCheckCode(OS, Defs); } @@ -2328,18 +2383,9 @@ void NeonEmitter::run(raw_ostream &OS) { 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"; - - OS << "#ifdef __aarch64__\n"; - OS << "typedef double float64_t;\n"; - OS << "#endif\n\n"; + OS << "#include <arm_vector_types.h>\n"; // For now, signedness of polynomial types depends on target OS << "#ifdef __aarch64__\n"; @@ -2352,12 +2398,7 @@ void NeonEmitter::run(raw_ostream &OS) { OS << "typedef int16_t poly16_t;\n"; OS << "typedef int64_t poly64_t;\n"; OS << "#endif\n"; - - emitNeonTypeDefs("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQdPcQPcPsQPsPlQPl", OS); - - OS << "#ifdef __ARM_FEATURE_BF16\n"; - emitNeonTypeDefs("bQb", OS); - OS << "#endif\n\n"; + emitNeonTypeDefs("PcQPcPsQPsPlQPl", OS); OS << "#define __ai static __inline__ __attribute__((__always_inline__, " "__nodebug__))\n\n"; @@ -2393,10 +2434,10 @@ void NeonEmitter::run(raw_ostream &OS) { } // Emit #endif/#if pair if needed. - if ((*I)->getGuard() != InGuard) { + if ((*I)->getArchGuard() != InGuard) { if (!InGuard.empty()) OS << "#endif\n"; - InGuard = (*I)->getGuard(); + InGuard = (*I)->getArchGuard(); if (!InGuard.empty()) OS << "#if " << InGuard << "\n"; } @@ -2502,10 +2543,10 @@ void NeonEmitter::runFP16(raw_ostream &OS) { } // Emit #endif/#if pair if needed. - if ((*I)->getGuard() != InGuard) { + if ((*I)->getArchGuard() != InGuard) { if (!InGuard.empty()) OS << "#endif\n"; - InGuard = (*I)->getGuard(); + InGuard = (*I)->getArchGuard(); if (!InGuard.empty()) OS << "#if " << InGuard << "\n"; } @@ -2526,6 +2567,38 @@ void NeonEmitter::runFP16(raw_ostream &OS) { OS << "#endif /* __ARM_FP16_H */\n"; } +void NeonEmitter::runVectorTypes(raw_ostream &OS) { + OS << "/*===---- arm_vector_types - ARM vector type " + "------===\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 << "#if !defined(__ARM_NEON_H) && !defined(__ARM_SVE_H)\n"; + OS << "#error \"This file should not be used standalone. Please include" + " arm_neon.h or arm_sve.h instead\"\n\n"; + OS << "#endif\n"; + OS << "#ifndef __ARM_NEON_TYPES_H\n"; + OS << "#define __ARM_NEON_TYPES_H\n"; + OS << "typedef float float32_t;\n"; + OS << "typedef __fp16 float16_t;\n"; + + OS << "#ifdef __aarch64__\n"; + OS << "typedef double float64_t;\n"; + OS << "#endif\n\n"; + + emitNeonTypeDefs("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQd", OS); + + emitNeonTypeDefs("bQb", OS); + OS << "#endif // __ARM_NEON_TYPES_H\n"; +} + void NeonEmitter::runBF16(raw_ostream &OS) { OS << "/*===---- arm_bf16.h - ARM BF16 intrinsics " "-----------------------------------===\n" @@ -2579,10 +2652,10 @@ void NeonEmitter::runBF16(raw_ostream &OS) { } // Emit #endif/#if pair if needed. - if ((*I)->getGuard() != InGuard) { + if ((*I)->getArchGuard() != InGuard) { if (!InGuard.empty()) OS << "#endif\n"; - InGuard = (*I)->getGuard(); + InGuard = (*I)->getArchGuard(); if (!InGuard.empty()) OS << "#if " << InGuard << "\n"; } @@ -2620,6 +2693,10 @@ void clang::EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { NeonEmitter(Records).runHeader(OS); } +void clang::EmitVectorTypes(RecordKeeper &Records, raw_ostream &OS) { + NeonEmitter(Records).runVectorTypes(OS); +} + void clang::EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { llvm_unreachable("Neon test generation no longer implemented!"); } diff --git a/contrib/llvm-project/clang/utils/TableGen/RISCVVEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/RISCVVEmitter.cpp index 24f2250c9ae0..2ca47f1ba59f 100644 --- a/contrib/llvm-project/clang/utils/TableGen/RISCVVEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/RISCVVEmitter.cpp @@ -14,205 +14,87 @@ // //===----------------------------------------------------------------------===// +#include "clang/Support/RISCVVIntrinsicUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include <numeric> +#include <optional> using namespace llvm; -using BasicType = char; -using VScaleVal = Optional<unsigned>; +using namespace clang::RISCV; namespace { +struct SemaRecord { + // Intrinsic name, e.g. vadd_vv + std::string Name; -// Exponential LMUL -struct LMULType { - int Log2LMUL; - LMULType(int Log2LMUL); - // Return the C/C++ string representation of LMUL - std::string str() const; - Optional<unsigned> getScale(unsigned ElementBitwidth) const; - void MulLog2LMUL(int Log2LMUL); - LMULType &operator*=(uint32_t RHS); -}; + // Overloaded intrinsic name, could be empty if can be computed from Name + // e.g. vadd + std::string OverloadedName; -// This class is compact representation of a valid and invalid RVVType. -class RVVType { - enum ScalarTypeKind : uint32_t { - Void, - Size_t, - Ptrdiff_t, - UnsignedLong, - SignedLong, - Boolean, - SignedInteger, - UnsignedInteger, - Float, - Invalid, - }; - BasicType BT; - ScalarTypeKind ScalarType = Invalid; - LMULType LMUL; - bool IsPointer = false; - // IsConstant indices are "int", but have the constant expression. - bool IsImmediate = false; - // Const qualifier for pointer to const object or object of const type. - bool IsConstant = false; - unsigned ElementBitwidth = 0; - VScaleVal Scale = 0; - bool Valid; - - std::string BuiltinStr; - std::string ClangBuiltinStr; - std::string Str; - std::string ShortStr; + // Supported type, mask of BasicType. + unsigned TypeRangeMask; -public: - RVVType() : RVVType(BasicType(), 0, StringRef()) {} - RVVType(BasicType BT, int Log2LMUL, StringRef prototype); - - // Return the string representation of a type, which is an encoded string for - // passing to the BUILTIN() macro in Builtins.def. - const std::string &getBuiltinStr() const { return BuiltinStr; } - - // Return the clang buitlin type for RVV vector type which are used in the - // riscv_vector.h header file. - const std::string &getClangBuiltinStr() const { return ClangBuiltinStr; } - - // Return the C/C++ string representation of a type for use in the - // riscv_vector.h header file. - const std::string &getTypeStr() const { return Str; } - - // Return the short name of a type for C/C++ name suffix. - const std::string &getShortStr() { - // Not all types are used in short name, so compute the short name by - // demanded. - if (ShortStr.empty()) - initShortStr(); - return ShortStr; - } + // Supported LMUL. + unsigned Log2LMULMask; - bool isValid() const { return Valid; } - bool isScalar() const { return Scale.hasValue() && Scale.getValue() == 0; } - bool isVector() const { return Scale.hasValue() && Scale.getValue() != 0; } - bool isFloat() const { return ScalarType == ScalarTypeKind::Float; } - bool isSignedInteger() const { - return ScalarType == ScalarTypeKind::SignedInteger; - } - bool isFloatVector(unsigned Width) const { - return isVector() && isFloat() && ElementBitwidth == Width; - } - bool isFloat(unsigned Width) const { - return isFloat() && ElementBitwidth == Width; - } + // Required extensions for this intrinsic. + uint32_t RequiredExtensions; -private: - // Verify RVV vector type and set Valid. - bool verifyType() const; - - // Creates a type based on basic types of TypeRange - void applyBasicType(); - - // Applies a prototype modifier to the current type. The result maybe an - // invalid type. - void applyModifier(StringRef prototype); - - // Compute and record a string for legal type. - void initBuiltinStr(); - // Compute and record a builtin RVV vector type string. - void initClangBuiltinStr(); - // Compute and record a type string for used in the header. - void initTypeStr(); - // Compute and record a short name of a type for C/C++ name suffix. - void initShortStr(); -}; + // Prototype for this intrinsic. + SmallVector<PrototypeDescriptor> Prototype; -using RVVTypePtr = RVVType *; -using RVVTypes = std::vector<RVVTypePtr>; + // Suffix of intrinsic name. + SmallVector<PrototypeDescriptor> Suffix; -enum RISCVExtension : uint8_t { - Basic = 0, - F = 1 << 1, - D = 1 << 2, - Zfh = 1 << 3, - Zvamo = 1 << 4, - Zvlsseg = 1 << 5, -}; + // Suffix of overloaded intrinsic name. + SmallVector<PrototypeDescriptor> OverloadedSuffix; -// TODO refactor RVVIntrinsic class design after support all intrinsic -// combination. This represents an instantiation of an intrinsic with a -// particular type and prototype -class RVVIntrinsic { + // Number of field, large than 1 if it's segment load/store. + unsigned NF; + bool HasMasked :1; + bool HasVL :1; + bool HasMaskedOffOperand :1; + bool HasTailPolicy : 1; + bool HasMaskPolicy : 1; + bool HasFRMRoundModeOp : 1; + bool IsTuple : 1; + uint8_t UnMaskedPolicyScheme : 2; + uint8_t MaskedPolicyScheme : 2; +}; + +// Compressed function signature table. +class SemaSignatureTable { private: - std::string Name; // Builtin name - std::string MangledName; - std::string IRName; - bool HasSideEffects; - bool IsMask; - bool HasMaskedOffOperand; - bool HasVL; - bool HasNoMaskedOverloaded; - bool HasAutoDef; // There is automiatic definition in header - std::string ManualCodegen; - RVVTypePtr OutputType; // Builtin output type - RVVTypes InputTypes; // Builtin input types - // The types we use to obtain the specific LLVM intrinsic. They are index of - // InputTypes. -1 means the return type. - std::vector<int64_t> IntrinsicTypes; - uint8_t RISCVExtensions = 0; - unsigned NF = 1; + std::vector<PrototypeDescriptor> SignatureTable; + + void insert(ArrayRef<PrototypeDescriptor> Signature); public: - RVVIntrinsic(StringRef Name, StringRef Suffix, StringRef MangledName, - StringRef MangledSuffix, StringRef IRName, bool HasSideEffects, - bool IsMask, bool HasMaskedOffOperand, bool HasVL, - bool HasNoMaskedOverloaded, bool HasAutoDef, - StringRef ManualCodegen, const RVVTypes &Types, - const std::vector<int64_t> &IntrinsicTypes, - StringRef RequiredExtension, unsigned NF); - ~RVVIntrinsic() = default; - - StringRef getName() const { return Name; } - StringRef getMangledName() const { return MangledName; } - bool hasSideEffects() const { return HasSideEffects; } - bool hasMaskedOffOperand() const { return HasMaskedOffOperand; } - bool hasVL() const { return HasVL; } - bool hasNoMaskedOverloaded() const { return HasNoMaskedOverloaded; } - bool hasManualCodegen() const { return !ManualCodegen.empty(); } - bool hasAutoDef() const { return HasAutoDef; } - bool isMask() const { return IsMask; } - StringRef getIRName() const { return IRName; } - StringRef getManualCodegen() const { return ManualCodegen; } - uint8_t getRISCVExtensions() const { return RISCVExtensions; } - unsigned getNF() const { return NF; } - - // Return the type string for a BUILTIN() macro in Builtins.def. - std::string getBuiltinTypeStr() const; - - // Emit the code block for switch body in EmitRISCVBuiltinExpr, it should - // init the RVVIntrinsic ID and IntrinsicTypes. - void emitCodeGenSwitchBody(raw_ostream &o) const; - - // Emit the macros for mapping C/C++ intrinsic function to builtin functions. - void emitIntrinsicMacro(raw_ostream &o) const; - - // Emit the mangled function definition. - void emitMangledFuncDef(raw_ostream &o) const; + static constexpr unsigned INVALID_INDEX = ~0U; + + // Create compressed signature table from SemaRecords. + void init(ArrayRef<SemaRecord> SemaRecords); + + // Query the Signature, return INVALID_INDEX if not found. + unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature); + + /// Print signature table in RVVHeader Record to \p OS + void print(raw_ostream &OS); }; class RVVEmitter { private: RecordKeeper &Records; - std::string HeaderCode; - // Concat BasicType, LMUL and Proto as key - StringMap<RVVType> LegalTypes; - StringSet<> IllegalTypes; + RVVTypeCache TypeCache; public: RVVEmitter(RecordKeeper &R) : Records(R) {} @@ -226,619 +108,113 @@ public: /// Emit all the information needed to map builtin -> LLVM IR intrinsic. void createCodeGen(raw_ostream &o); - std::string getSuffixStr(char Type, int Log2LMUL, StringRef Prototypes); + /// Emit all the information needed by SemaRISCVVectorLookup.cpp. + /// We've large number of intrinsic function for RVV, creating a customized + /// could speed up the compilation time. + void createSema(raw_ostream &o); private: - /// Create all intrinsics and add them to \p Out - void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out); - /// Compute output and input types by applying different config (basic type - /// and LMUL with type transformers). It also record result of type in legal - /// or illegal set to avoid compute the same config again. The result maybe - /// have illegal RVVType. - Optional<RVVTypes> computeTypes(BasicType BT, int Log2LMUL, unsigned NF, - ArrayRef<std::string> PrototypeSeq); - Optional<RVVTypePtr> computeType(BasicType BT, int Log2LMUL, StringRef Proto); - - /// Emit Acrh predecessor definitions and body, assume the element of Defs are - /// sorted by extension. - void emitArchMacroAndBody( - std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &o, - std::function<void(raw_ostream &, const RVVIntrinsic &)>); - - // Emit the architecture preprocessor definitions. Return true when emits - // non-empty string. - bool emitExtDefStr(uint8_t Extensions, raw_ostream &o); - // Slice Prototypes string into sub prototype string and process each sub - // prototype string individually in the Handler. - void parsePrototypes(StringRef Prototypes, - std::function<void(StringRef)> Handler); + /// Create all intrinsics and add them to \p Out and SemaRecords. + void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out, + std::vector<SemaRecord> *SemaRecords = nullptr); + /// Create all intrinsic records and SemaSignatureTable from SemaRecords. + void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, + SemaSignatureTable &SST, + ArrayRef<SemaRecord> SemaRecords); + + /// Print HeaderCode in RVVHeader Record to \p Out + void printHeaderCode(raw_ostream &OS); }; } // namespace -//===----------------------------------------------------------------------===// -// Type implementation -//===----------------------------------------------------------------------===// - -LMULType::LMULType(int NewLog2LMUL) { - // Check Log2LMUL is -3, -2, -1, 0, 1, 2, 3 - assert(NewLog2LMUL <= 3 && NewLog2LMUL >= -3 && "Bad LMUL number!"); - Log2LMUL = NewLog2LMUL; -} - -std::string LMULType::str() const { - if (Log2LMUL < 0) - return "mf" + utostr(1ULL << (-Log2LMUL)); - return "m" + utostr(1ULL << Log2LMUL); -} - -VScaleVal LMULType::getScale(unsigned ElementBitwidth) const { - int Log2ScaleResult = 0; - switch (ElementBitwidth) { - default: - break; - case 8: - Log2ScaleResult = Log2LMUL + 3; - break; - case 16: - Log2ScaleResult = Log2LMUL + 2; - break; - case 32: - Log2ScaleResult = Log2LMUL + 1; - break; - case 64: - Log2ScaleResult = Log2LMUL; - break; - } - // Illegal vscale result would be less than 1 - if (Log2ScaleResult < 0) - return None; - return 1 << Log2ScaleResult; -} - -void LMULType::MulLog2LMUL(int log2LMUL) { Log2LMUL += log2LMUL; } - -LMULType &LMULType::operator*=(uint32_t RHS) { - assert(isPowerOf2_32(RHS)); - this->Log2LMUL = this->Log2LMUL + Log2_32(RHS); - return *this; -} - -RVVType::RVVType(BasicType BT, int Log2LMUL, StringRef prototype) - : BT(BT), LMUL(LMULType(Log2LMUL)) { - applyBasicType(); - applyModifier(prototype); - Valid = verifyType(); - if (Valid) { - initBuiltinStr(); - initTypeStr(); - if (isVector()) { - initClangBuiltinStr(); - } - } -} - -// clang-format off -// boolean type are encoded the ratio of n (SEW/LMUL) -// SEW/LMUL | 1 | 2 | 4 | 8 | 16 | 32 | 64 -// c type | vbool64_t | vbool32_t | vbool16_t | vbool8_t | vbool4_t | vbool2_t | vbool1_t -// IR type | nxv1i1 | nxv2i1 | nxv4i1 | nxv8i1 | nxv16i1 | nxv32i1 | nxv64i1 - -// type\lmul | 1/8 | 1/4 | 1/2 | 1 | 2 | 4 | 8 -// -------- |------ | -------- | ------- | ------- | -------- | -------- | -------- -// i64 | N/A | N/A | N/A | nxv1i64 | nxv2i64 | nxv4i64 | nxv8i64 -// i32 | N/A | N/A | nxv1i32 | nxv2i32 | nxv4i32 | nxv8i32 | nxv16i32 -// i16 | N/A | nxv1i16 | nxv2i16 | nxv4i16 | nxv8i16 | nxv16i16 | nxv32i16 -// i8 | nxv1i8 | nxv2i8 | nxv4i8 | nxv8i8 | nxv16i8 | nxv32i8 | nxv64i8 -// double | N/A | N/A | N/A | nxv1f64 | nxv2f64 | nxv4f64 | nxv8f64 -// float | N/A | N/A | nxv1f32 | nxv2f32 | nxv4f32 | nxv8f32 | nxv16f32 -// half | N/A | nxv1f16 | nxv2f16 | nxv4f16 | nxv8f16 | nxv16f16 | nxv32f16 -// clang-format on - -bool RVVType::verifyType() const { - if (ScalarType == Invalid) - return false; - if (isScalar()) - return true; - if (!Scale.hasValue()) - return false; - if (isFloat() && ElementBitwidth == 8) - return false; - unsigned V = Scale.getValue(); - switch (ElementBitwidth) { - case 1: - case 8: - // Check Scale is 1,2,4,8,16,32,64 - return (V <= 64 && isPowerOf2_32(V)); - case 16: - // Check Scale is 1,2,4,8,16,32 - return (V <= 32 && isPowerOf2_32(V)); - case 32: - // Check Scale is 1,2,4,8,16 - return (V <= 16 && isPowerOf2_32(V)); - case 64: - // Check Scale is 1,2,4,8 - return (V <= 8 && isPowerOf2_32(V)); - } - return false; -} - -void RVVType::initBuiltinStr() { - assert(isValid() && "RVVType is invalid"); - switch (ScalarType) { - case ScalarTypeKind::Void: - BuiltinStr = "v"; - return; - case ScalarTypeKind::Size_t: - BuiltinStr = "z"; - if (IsImmediate) - BuiltinStr = "I" + BuiltinStr; - if (IsPointer) - BuiltinStr += "*"; - return; - case ScalarTypeKind::Ptrdiff_t: - BuiltinStr = "Y"; - return; - case ScalarTypeKind::UnsignedLong: - BuiltinStr = "ULi"; - return; - case ScalarTypeKind::SignedLong: - BuiltinStr = "Li"; - return; - case ScalarTypeKind::Boolean: - assert(ElementBitwidth == 1); - BuiltinStr += "b"; - break; - case ScalarTypeKind::SignedInteger: - case ScalarTypeKind::UnsignedInteger: - switch (ElementBitwidth) { - case 8: - BuiltinStr += "c"; - break; - case 16: - BuiltinStr += "s"; - break; - case 32: - BuiltinStr += "i"; - break; - case 64: - BuiltinStr += "Wi"; - break; - default: - llvm_unreachable("Unhandled ElementBitwidth!"); - } - if (isSignedInteger()) - BuiltinStr = "S" + BuiltinStr; - else - BuiltinStr = "U" + BuiltinStr; - break; - case ScalarTypeKind::Float: - switch (ElementBitwidth) { - case 16: - BuiltinStr += "x"; - break; - case 32: - BuiltinStr += "f"; - break; - case 64: - BuiltinStr += "d"; - break; - default: - llvm_unreachable("Unhandled ElementBitwidth!"); - } - break; - default: - llvm_unreachable("ScalarType is invalid!"); - } - if (IsImmediate) - BuiltinStr = "I" + BuiltinStr; - if (isScalar()) { - if (IsConstant) - BuiltinStr += "C"; - if (IsPointer) - BuiltinStr += "*"; - return; - } - BuiltinStr = "q" + utostr(Scale.getValue()) + BuiltinStr; - // Pointer to vector types. Defined for Zvlsseg load intrinsics. - // Zvlsseg load intrinsics have pointer type arguments to store the loaded - // vector values. - if (IsPointer) - BuiltinStr += "*"; -} - -void RVVType::initClangBuiltinStr() { - assert(isValid() && "RVVType is invalid"); - assert(isVector() && "Handle Vector type only"); - - ClangBuiltinStr = "__rvv_"; - switch (ScalarType) { - case ScalarTypeKind::Boolean: - ClangBuiltinStr += "bool" + utostr(64 / Scale.getValue()) + "_t"; - return; - case ScalarTypeKind::Float: - ClangBuiltinStr += "float"; - break; - case ScalarTypeKind::SignedInteger: - ClangBuiltinStr += "int"; - break; - case ScalarTypeKind::UnsignedInteger: - ClangBuiltinStr += "uint"; - break; - default: - llvm_unreachable("ScalarTypeKind is invalid"); - } - ClangBuiltinStr += utostr(ElementBitwidth) + LMUL.str() + "_t"; -} - -void RVVType::initTypeStr() { - assert(isValid() && "RVVType is invalid"); - - if (IsConstant) - Str += "const "; - - auto getTypeString = [&](StringRef TypeStr) { - if (isScalar()) - return Twine(TypeStr + Twine(ElementBitwidth) + "_t").str(); - return Twine("v" + TypeStr + Twine(ElementBitwidth) + LMUL.str() + "_t") - .str(); - }; - - switch (ScalarType) { - case ScalarTypeKind::Void: - Str = "void"; - return; - case ScalarTypeKind::Size_t: - Str = "size_t"; - if (IsPointer) - Str += " *"; - return; - case ScalarTypeKind::Ptrdiff_t: - Str = "ptrdiff_t"; - return; - case ScalarTypeKind::UnsignedLong: - Str = "unsigned long"; - return; - case ScalarTypeKind::SignedLong: - Str = "long"; - return; - case ScalarTypeKind::Boolean: - if (isScalar()) - Str += "bool"; - else - // Vector bool is special case, the formulate is - // `vbool<N>_t = MVT::nxv<64/N>i1` ex. vbool16_t = MVT::4i1 - Str += "vbool" + utostr(64 / Scale.getValue()) + "_t"; - break; - case ScalarTypeKind::Float: - if (isScalar()) { - if (ElementBitwidth == 64) - Str += "double"; - else if (ElementBitwidth == 32) - Str += "float"; - else if (ElementBitwidth == 16) - Str += "_Float16"; - else - llvm_unreachable("Unhandled floating type."); - } else - Str += getTypeString("float"); - break; - case ScalarTypeKind::SignedInteger: - Str += getTypeString("int"); - break; - case ScalarTypeKind::UnsignedInteger: - Str += getTypeString("uint"); - break; - default: - llvm_unreachable("ScalarType is invalid!"); - } - if (IsPointer) - Str += " *"; -} - -void RVVType::initShortStr() { - switch (ScalarType) { - case ScalarTypeKind::Boolean: - assert(isVector()); - ShortStr = "b" + utostr(64 / Scale.getValue()); - return; - case ScalarTypeKind::Float: - ShortStr = "f" + utostr(ElementBitwidth); - break; - case ScalarTypeKind::SignedInteger: - ShortStr = "i" + utostr(ElementBitwidth); - break; - case ScalarTypeKind::UnsignedInteger: - ShortStr = "u" + utostr(ElementBitwidth); - break; - default: - PrintFatalError("Unhandled case!"); - } - if (isVector()) - ShortStr += LMUL.str(); -} - -void RVVType::applyBasicType() { - switch (BT) { +static BasicType ParseBasicType(char c) { + switch (c) { case 'c': - ElementBitwidth = 8; - ScalarType = ScalarTypeKind::SignedInteger; + return BasicType::Int8; break; case 's': - ElementBitwidth = 16; - ScalarType = ScalarTypeKind::SignedInteger; + return BasicType::Int16; break; case 'i': - ElementBitwidth = 32; - ScalarType = ScalarTypeKind::SignedInteger; + return BasicType::Int32; break; case 'l': - ElementBitwidth = 64; - ScalarType = ScalarTypeKind::SignedInteger; + return BasicType::Int64; break; case 'x': - ElementBitwidth = 16; - ScalarType = ScalarTypeKind::Float; + return BasicType::Float16; break; case 'f': - ElementBitwidth = 32; - ScalarType = ScalarTypeKind::Float; + return BasicType::Float32; break; case 'd': - ElementBitwidth = 64; - ScalarType = ScalarTypeKind::Float; - break; - default: - PrintFatalError("Unhandled type code!"); - } - assert(ElementBitwidth != 0 && "Bad element bitwidth!"); -} - -void RVVType::applyModifier(StringRef Transformer) { - if (Transformer.empty()) - return; - // Handle primitive type transformer - auto PType = Transformer.back(); - switch (PType) { - case 'e': - Scale = 0; - break; - case 'v': - Scale = LMUL.getScale(ElementBitwidth); - break; - case 'w': - ElementBitwidth *= 2; - LMUL *= 2; - Scale = LMUL.getScale(ElementBitwidth); - break; - case 'q': - ElementBitwidth *= 4; - LMUL *= 4; - Scale = LMUL.getScale(ElementBitwidth); - break; - case 'o': - ElementBitwidth *= 8; - LMUL *= 8; - Scale = LMUL.getScale(ElementBitwidth); - break; - case 'm': - ScalarType = ScalarTypeKind::Boolean; - Scale = LMUL.getScale(ElementBitwidth); - ElementBitwidth = 1; - break; - case '0': - ScalarType = ScalarTypeKind::Void; - break; - case 'z': - ScalarType = ScalarTypeKind::Size_t; - break; - case 't': - ScalarType = ScalarTypeKind::Ptrdiff_t; + return BasicType::Float64; break; - case 'u': - ScalarType = ScalarTypeKind::UnsignedLong; - break; - case 'l': - ScalarType = ScalarTypeKind::SignedLong; + case 'y': + return BasicType::BFloat16; break; default: - PrintFatalError("Illegal primitive type transformers!"); - } - Transformer = Transformer.drop_back(); - - // Extract and compute complex type transformer. It can only appear one time. - if (Transformer.startswith("(")) { - size_t Idx = Transformer.find(')'); - assert(Idx != StringRef::npos); - StringRef ComplexType = Transformer.slice(1, Idx); - Transformer = Transformer.drop_front(Idx + 1); - assert(Transformer.find('(') == StringRef::npos && - "Only allow one complex type transformer"); - - auto UpdateAndCheckComplexProto = [&]() { - Scale = LMUL.getScale(ElementBitwidth); - const StringRef VectorPrototypes("vwqom"); - if (!VectorPrototypes.contains(PType)) - PrintFatalError("Complex type transformer only supports vector type!"); - if (Transformer.find_first_of("PCKWS") != StringRef::npos) - PrintFatalError( - "Illegal type transformer for Complex type transformer"); - }; - auto ComputeFixedLog2LMUL = - [&](StringRef Value, - std::function<bool(const int32_t &, const int32_t &)> Compare) { - int32_t Log2LMUL; - Value.getAsInteger(10, Log2LMUL); - if (!Compare(Log2LMUL, LMUL.Log2LMUL)) { - ScalarType = Invalid; - return false; - } - // Update new LMUL - LMUL = LMULType(Log2LMUL); - UpdateAndCheckComplexProto(); - return true; - }; - auto ComplexTT = ComplexType.split(":"); - if (ComplexTT.first == "Log2EEW") { - uint32_t Log2EEW; - ComplexTT.second.getAsInteger(10, Log2EEW); - // update new elmul = (eew/sew) * lmul - LMUL.MulLog2LMUL(Log2EEW - Log2_32(ElementBitwidth)); - // update new eew - ElementBitwidth = 1 << Log2EEW; - ScalarType = ScalarTypeKind::SignedInteger; - UpdateAndCheckComplexProto(); - } else if (ComplexTT.first == "FixedSEW") { - uint32_t NewSEW; - ComplexTT.second.getAsInteger(10, NewSEW); - // Set invalid type if src and dst SEW are same. - if (ElementBitwidth == NewSEW) { - ScalarType = Invalid; - return; - } - // Update new SEW - ElementBitwidth = NewSEW; - UpdateAndCheckComplexProto(); - } else if (ComplexTT.first == "LFixedLog2LMUL") { - // New LMUL should be larger than old - if (!ComputeFixedLog2LMUL(ComplexTT.second, std::greater<int32_t>())) - return; - } else if (ComplexTT.first == "SFixedLog2LMUL") { - // New LMUL should be smaller than old - if (!ComputeFixedLog2LMUL(ComplexTT.second, std::less<int32_t>())) - return; - } else { - PrintFatalError("Illegal complex type transformers!"); - } - } - - // Compute the remain type transformers - for (char I : Transformer) { - switch (I) { - case 'P': - if (IsConstant) - PrintFatalError("'P' transformer cannot be used after 'C'"); - if (IsPointer) - PrintFatalError("'P' transformer cannot be used twice"); - IsPointer = true; - break; - case 'C': - if (IsConstant) - PrintFatalError("'C' transformer cannot be used twice"); - IsConstant = true; - break; - case 'K': - IsImmediate = true; - break; - case 'U': - ScalarType = ScalarTypeKind::UnsignedInteger; - break; - case 'I': - ScalarType = ScalarTypeKind::SignedInteger; - break; - case 'F': - ScalarType = ScalarTypeKind::Float; - break; - case 'S': - LMUL = LMULType(0); - // Update ElementBitwidth need to update Scale too. - Scale = LMUL.getScale(ElementBitwidth); - break; - default: - PrintFatalError("Illegal non-primitive type transformer!"); - } + return BasicType::Unknown; } } -//===----------------------------------------------------------------------===// -// RVVIntrinsic implementation -//===----------------------------------------------------------------------===// -RVVIntrinsic::RVVIntrinsic(StringRef NewName, StringRef Suffix, - StringRef NewMangledName, StringRef MangledSuffix, - StringRef IRName, bool HasSideEffects, bool IsMask, - bool HasMaskedOffOperand, bool HasVL, - bool HasNoMaskedOverloaded, bool HasAutoDef, - StringRef ManualCodegen, const RVVTypes &OutInTypes, - const std::vector<int64_t> &NewIntrinsicTypes, - StringRef RequiredExtension, unsigned NF) - : IRName(IRName), HasSideEffects(HasSideEffects), IsMask(IsMask), - HasMaskedOffOperand(HasMaskedOffOperand), HasVL(HasVL), - HasNoMaskedOverloaded(HasNoMaskedOverloaded), HasAutoDef(HasAutoDef), - ManualCodegen(ManualCodegen.str()), NF(NF) { - - // Init Name and MangledName - Name = NewName.str(); - if (NewMangledName.empty()) - MangledName = NewName.split("_").first.str(); - else - MangledName = NewMangledName.str(); - if (!Suffix.empty()) - Name += "_" + Suffix.str(); - if (!MangledSuffix.empty()) - MangledName += "_" + MangledSuffix.str(); - if (IsMask) { - Name += "_m"; - } - // Init RISC-V extensions - for (const auto &T : OutInTypes) { - if (T->isFloatVector(16) || T->isFloat(16)) - RISCVExtensions |= RISCVExtension::Zfh; - else if (T->isFloatVector(32) || T->isFloat(32)) - RISCVExtensions |= RISCVExtension::F; - else if (T->isFloatVector(64) || T->isFloat(64)) - RISCVExtensions |= RISCVExtension::D; - } - if (RequiredExtension == "Zvamo") - RISCVExtensions |= RISCVExtension::Zvamo; - if (RequiredExtension == "Zvlsseg") - RISCVExtensions |= RISCVExtension::Zvlsseg; - - // Init OutputType and InputTypes - OutputType = OutInTypes[0]; - InputTypes.assign(OutInTypes.begin() + 1, OutInTypes.end()); - - // IntrinsicTypes is nonmasked version index. Need to update it - // if there is maskedoff operand (It is always in first operand). - IntrinsicTypes = NewIntrinsicTypes; - if (IsMask && HasMaskedOffOperand) { - for (auto &I : IntrinsicTypes) { - if (I >= 0) - I += NF; - } - } +static VectorTypeModifier getTupleVTM(unsigned NF) { + assert(2 <= NF && NF <= 8 && "2 <= NF <= 8"); + return static_cast<VectorTypeModifier>( + static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2)); } -std::string RVVIntrinsic::getBuiltinTypeStr() const { - std::string S; - S += OutputType->getBuiltinStr(); - for (const auto &T : InputTypes) { - S += T->getBuiltinStr(); - } - return S; -} +void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) { + if (!RVVI->getIRName().empty()) + OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n"; + if (RVVI->getNF() >= 2) + OS << " NF = " + utostr(RVVI->getNF()) + ";\n"; -void RVVIntrinsic::emitCodeGenSwitchBody(raw_ostream &OS) const { - if (!getIRName().empty()) - OS << " ID = Intrinsic::riscv_" + getIRName() + ";\n"; - if (NF >= 2) - OS << " NF = " + utostr(getNF()) + ";\n"; - if (hasManualCodegen()) { - OS << ManualCodegen; + OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n"; + + if (RVVI->hasManualCodegen()) { + OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\n"; + OS << RVVI->getManualCodegen(); OS << "break;\n"; return; } - if (isMask()) { - if (hasVL()) { + for (const auto &I : enumerate(RVVI->getInputTypes())) { + if (I.value()->isPointer()) { + assert(RVVI->getIntrinsicTypes().front() == -1 && + "RVVI should be vector load intrinsic."); + } + } + + if (RVVI->isMasked()) { + if (RVVI->hasVL()) { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n"; + if (RVVI->hasPolicyOperand()) + OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType()," + " PolicyAttrs));\n"; + if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy()) + OS << " Ops.insert(Ops.begin(), " + "llvm::PoisonValue::get(ResultType));\n"; + // Masked reduction cases. + if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() && + RVVI->getPolicyAttrs().isTAMAPolicy()) + OS << " Ops.insert(Ops.begin(), " + "llvm::PoisonValue::get(ResultType));\n"; } else { OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n"; } + } else { + if (RVVI->hasPolicyOperand()) + OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), " + "PolicyAttrs));\n"; + else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy()) + OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n"; } OS << " IntrinsicTypes = {"; ListSeparator LS; - for (const auto &Idx : IntrinsicTypes) { + for (const auto &Idx : RVVI->getIntrinsicTypes()) { if (Idx == -1) OS << LS << "ResultType"; else @@ -847,40 +223,89 @@ void RVVIntrinsic::emitCodeGenSwitchBody(raw_ostream &OS) const { // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is // always last operand. - if (hasVL()) + if (RVVI->hasVL()) OS << ", Ops.back()->getType()"; OS << "};\n"; OS << " break;\n"; } -void RVVIntrinsic::emitIntrinsicMacro(raw_ostream &OS) const { - OS << "#define " << getName() << "("; - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0, e = InputTypes.size(); i != e; ++i) - OS << LS << "op" << i; - } - OS << ") \\\n"; - OS << "__builtin_rvv_" << getName() << "("; - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0, e = InputTypes.size(); i != e; ++i) - OS << LS << "(" << InputTypes[i]->getTypeStr() << ")(op" << i << ")"; +//===----------------------------------------------------------------------===// +// SemaSignatureTable implementation +//===----------------------------------------------------------------------===// +void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) { + // Sort signature entries by length, let longer signature insert first, to + // make it more possible to reuse table entries, that can reduce ~10% table + // size. + struct Compare { + bool operator()(const SmallVector<PrototypeDescriptor> &A, + const SmallVector<PrototypeDescriptor> &B) const { + if (A.size() != B.size()) + return A.size() > B.size(); + + size_t Len = A.size(); + for (size_t i = 0; i < Len; ++i) { + if (A[i] != B[i]) + return A[i] < B[i]; + } + + return false; + } + }; + + std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures; + auto InsertToSignatureSet = + [&](const SmallVector<PrototypeDescriptor> &Signature) { + if (Signature.empty()) + return; + + Signatures.insert(Signature); + }; + + assert(!SemaRecords.empty()); + + for (const SemaRecord &SR : SemaRecords) { + InsertToSignatureSet(SR.Prototype); + InsertToSignatureSet(SR.Suffix); + InsertToSignatureSet(SR.OverloadedSuffix); } - OS << ")\n"; + + for (auto &Sig : Signatures) + insert(Sig); } -void RVVIntrinsic::emitMangledFuncDef(raw_ostream &OS) const { - OS << "__attribute__((clang_builtin_alias("; - OS << "__builtin_rvv_" << getName() << ")))\n"; - OS << OutputType->getTypeStr() << " " << getMangledName() << "("; - // Emit function arguments - if (!InputTypes.empty()) { - ListSeparator LS; - for (unsigned i = 0; i < InputTypes.size(); ++i) - OS << LS << InputTypes[i]->getTypeStr() << " op" << i; +void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) { + if (getIndex(Signature) != INVALID_INDEX) + return; + + // Insert Signature into SignatureTable if not found in the table. + SignatureTable.insert(SignatureTable.begin(), Signature.begin(), + Signature.end()); +} + +unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) { + // Empty signature could be point into any index since there is length + // field when we use, so just always point it to 0. + if (Signature.empty()) + return 0; + + // Checking Signature already in table or not. + if (Signature.size() <= SignatureTable.size()) { + size_t Bound = SignatureTable.size() - Signature.size() + 1; + for (size_t Index = 0; Index < Bound; ++Index) { + if (equal(Signature.begin(), Signature.end(), + SignatureTable.begin() + Index)) + return Index; + } } - OS << ");\n\n"; + + return INVALID_INDEX; +} + +void SemaSignatureTable::print(raw_ostream &OS) { + for (const auto &Sig : SignatureTable) + OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", " + << static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM) + << "),\n"; } //===----------------------------------------------------------------------===// @@ -915,13 +340,9 @@ void RVVEmitter::createHeader(raw_ostream &OS) { OS << "extern \"C\" {\n"; OS << "#endif\n\n"; - std::vector<std::unique_ptr<RVVIntrinsic>> Defs; - createRVVIntrinsics(Defs); + OS << "#pragma clang riscv intrinsic vector\n\n"; - // Print header code - if (!HeaderCode.empty()) { - OS << HeaderCode; - } + printHeaderCode(OS); auto printType = [&](auto T) { OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() @@ -931,73 +352,66 @@ void RVVEmitter::createHeader(raw_ostream &OS) { constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3}; // Print RVV boolean types. for (int Log2LMUL : Log2LMULs) { - auto T = computeType('c', Log2LMUL, "m"); - if (T.hasValue()) - printType(T.getValue()); + auto T = TypeCache.computeType(BasicType::Int8, Log2LMUL, + PrototypeDescriptor::Mask); + if (T) + printType(*T); } // Print RVV int/float types. for (char I : StringRef("csil")) { + BasicType BT = ParseBasicType(I); for (int Log2LMUL : Log2LMULs) { - auto T = computeType(I, Log2LMUL, "v"); - if (T.hasValue()) { - printType(T.getValue()); - auto UT = computeType(I, Log2LMUL, "Uv"); - printType(UT.getValue()); + auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); + if (T) { + printType(*T); + auto UT = TypeCache.computeType( + BT, Log2LMUL, + PrototypeDescriptor(BaseTypeModifier::Vector, + VectorTypeModifier::NoModifier, + TypeModifier::UnsignedInteger)); + printType(*UT); + } + for (int NF = 2; NF <= 8; ++NF) { + auto TupleT = TypeCache.computeType( + BT, Log2LMUL, + PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), + TypeModifier::SignedInteger)); + auto TupleUT = TypeCache.computeType( + BT, Log2LMUL, + PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), + TypeModifier::UnsignedInteger)); + if (TupleT) + printType(*TupleT); + if (TupleUT) + printType(*TupleUT); } } } - OS << "#if defined(__riscv_zfh)\n"; - for (int Log2LMUL : Log2LMULs) { - auto T = computeType('x', Log2LMUL, "v"); - if (T.hasValue()) - printType(T.getValue()); - } - OS << "#endif\n"; - - OS << "#if defined(__riscv_f)\n"; - for (int Log2LMUL : Log2LMULs) { - auto T = computeType('f', Log2LMUL, "v"); - if (T.hasValue()) - printType(T.getValue()); - } - OS << "#endif\n"; - OS << "#if defined(__riscv_d)\n"; - for (int Log2LMUL : Log2LMULs) { - auto T = computeType('d', Log2LMUL, "v"); - if (T.hasValue()) - printType(T.getValue()); + for (BasicType BT : {BasicType::Float16, BasicType::Float32, + BasicType::Float64, BasicType::BFloat16}) { + for (int Log2LMUL : Log2LMULs) { + auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); + if (T) + printType(*T); + for (int NF = 2; NF <= 8; ++NF) { + auto TupleT = TypeCache.computeType( + BT, Log2LMUL, + PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF), + (BT == BasicType::BFloat16 + ? TypeModifier::BFloat + : TypeModifier::Float))); + if (TupleT) + printType(*TupleT); + } + } } - OS << "#endif\n\n"; - - // The same extension include in the same arch guard marco. - std::stable_sort(Defs.begin(), Defs.end(), - [](const std::unique_ptr<RVVIntrinsic> &A, - const std::unique_ptr<RVVIntrinsic> &B) { - return A->getRISCVExtensions() < B->getRISCVExtensions(); - }); - - // Print intrinsic functions with macro - emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { - Inst.emitIntrinsicMacro(OS); - }); OS << "#define __riscv_v_intrinsic_overloading 1\n"; - // Print Overloaded APIs - OS << "#define __rvv_overloaded static inline " - "__attribute__((__always_inline__, __nodebug__, __overloadable__))\n"; - - emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { - if (!Inst.isMask() && !Inst.hasNoMaskedOverloaded()) - return; - OS << "__rvv_overloaded "; - Inst.emitMangledFuncDef(OS); - }); - OS << "\n#ifdef __cplusplus\n"; OS << "}\n"; - OS << "#endif // __riscv_vector\n"; + OS << "#endif // __cplusplus\n"; OS << "#endif // __RISCV_VECTOR_H\n"; } @@ -1005,17 +419,29 @@ void RVVEmitter::createBuiltins(raw_ostream &OS) { std::vector<std::unique_ptr<RVVIntrinsic>> Defs; createRVVIntrinsics(Defs); + // Map to keep track of which builtin names have already been emitted. + StringMap<RVVIntrinsic *> BuiltinMap; + OS << "#if defined(TARGET_BUILTIN) && !defined(RISCVV_BUILTIN)\n"; OS << "#define RISCVV_BUILTIN(ID, TYPE, ATTRS) TARGET_BUILTIN(ID, TYPE, " - "ATTRS, \"experimental-v\")\n"; + "ATTRS, \"zve32x\")\n"; OS << "#endif\n"; for (auto &Def : Defs) { - OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getName() << ",\"" - << Def->getBuiltinTypeStr() << "\", "; - if (!Def->hasSideEffects()) - OS << "\"n\")\n"; - else - OS << "\"\")\n"; + auto P = + BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); + if (!P.second) { + // Verf that this would have produced the same builtin definition. + if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias()) + PrintFatalError("Builtin with same name has different hasAutoDef"); + else if (!Def->hasBuiltinAlias() && + P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr()) + PrintFatalError("Builtin with same name has different type string"); + continue; + } + OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getBuiltinName() << ",\""; + if (!Def->hasBuiltinAlias()) + OS << Def->getBuiltinTypeStr(); + OS << "\", \"n\")\n"; } OS << "#undef RISCVV_BUILTIN\n"; } @@ -1024,233 +450,319 @@ void RVVEmitter::createCodeGen(raw_ostream &OS) { std::vector<std::unique_ptr<RVVIntrinsic>> Defs; createRVVIntrinsics(Defs); // IR name could be empty, use the stable sort preserves the relative order. - std::stable_sort(Defs.begin(), Defs.end(), - [](const std::unique_ptr<RVVIntrinsic> &A, - const std::unique_ptr<RVVIntrinsic> &B) { - return A->getIRName() < B->getIRName(); - }); - // Print switch body when the ir name or ManualCodegen changes from previous - // iteration. + llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A, + const std::unique_ptr<RVVIntrinsic> &B) { + if (A->getIRName() == B->getIRName()) + return (A->getPolicyAttrs() < B->getPolicyAttrs()); + return (A->getIRName() < B->getIRName()); + }); + + // Map to keep track of which builtin names have already been emitted. + StringMap<RVVIntrinsic *> BuiltinMap; + + // Print switch body when the ir name, ManualCodegen or policy changes from + // previous iteration. RVVIntrinsic *PrevDef = Defs.begin()->get(); for (auto &Def : Defs) { StringRef CurIRName = Def->getIRName(); if (CurIRName != PrevDef->getIRName() || - (Def->getManualCodegen() != PrevDef->getManualCodegen())) { - PrevDef->emitCodeGenSwitchBody(OS); + (Def->getManualCodegen() != PrevDef->getManualCodegen()) || + (Def->getPolicyAttrs() != PrevDef->getPolicyAttrs())) { + emitCodeGenSwitchBody(PrevDef, OS); } PrevDef = Def.get(); - OS << "case RISCV::BI__builtin_rvv_" << Def->getName() << ":\n"; - } - Defs.back()->emitCodeGenSwitchBody(OS); - OS << "\n"; -} -void RVVEmitter::parsePrototypes(StringRef Prototypes, - std::function<void(StringRef)> Handler) { - const StringRef Primaries("evwqom0ztul"); - while (!Prototypes.empty()) { - size_t Idx = 0; - // Skip over complex prototype because it could contain primitive type - // character. - if (Prototypes[0] == '(') - Idx = Prototypes.find_first_of(')'); - Idx = Prototypes.find_first_of(Primaries, Idx); - assert(Idx != StringRef::npos); - Handler(Prototypes.slice(0, Idx + 1)); - Prototypes = Prototypes.drop_front(Idx + 1); - } -} + auto P = + BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); + if (P.second) { + OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName() + << ":\n"; + continue; + } -std::string RVVEmitter::getSuffixStr(char Type, int Log2LMUL, - StringRef Prototypes) { - SmallVector<std::string> SuffixStrs; - parsePrototypes(Prototypes, [&](StringRef Proto) { - auto T = computeType(Type, Log2LMUL, Proto); - SuffixStrs.push_back(T.getValue()->getShortStr()); - }); - return join(SuffixStrs, "_"); + if (P.first->second->getIRName() != Def->getIRName()) + PrintFatalError("Builtin with same name has different IRName"); + else if (P.first->second->getManualCodegen() != Def->getManualCodegen()) + PrintFatalError("Builtin with same name has different ManualCodegen"); + else if (P.first->second->isMasked() != Def->isMasked()) + PrintFatalError("Builtin with same name has different isMasked"); + else if (P.first->second->hasVL() != Def->hasVL()) + PrintFatalError("Builtin with same name has different hasVL"); + else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme()) + PrintFatalError("Builtin with same name has different getPolicyScheme"); + else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes()) + PrintFatalError("Builtin with same name has different IntrinsicTypes"); + } + emitCodeGenSwitchBody(Defs.back().get(), OS); + OS << "\n"; } void RVVEmitter::createRVVIntrinsics( - std::vector<std::unique_ptr<RVVIntrinsic>> &Out) { + std::vector<std::unique_ptr<RVVIntrinsic>> &Out, + std::vector<SemaRecord> *SemaRecords) { std::vector<Record *> RV = Records.getAllDerivedDefinitions("RVVBuiltin"); for (auto *R : RV) { StringRef Name = R->getValueAsString("Name"); StringRef SuffixProto = R->getValueAsString("Suffix"); - StringRef MangledName = R->getValueAsString("MangledName"); - StringRef MangledSuffixProto = R->getValueAsString("MangledSuffix"); + StringRef OverloadedName = R->getValueAsString("OverloadedName"); + StringRef OverloadedSuffixProto = R->getValueAsString("OverloadedSuffix"); StringRef Prototypes = R->getValueAsString("Prototype"); StringRef TypeRange = R->getValueAsString("TypeRange"); - bool HasMask = R->getValueAsBit("HasMask"); + bool HasMasked = R->getValueAsBit("HasMasked"); bool HasMaskedOffOperand = R->getValueAsBit("HasMaskedOffOperand"); bool HasVL = R->getValueAsBit("HasVL"); - bool HasNoMaskedOverloaded = R->getValueAsBit("HasNoMaskedOverloaded"); - bool HasSideEffects = R->getValueAsBit("HasSideEffects"); + Record *MPSRecord = R->getValueAsDef("MaskedPolicyScheme"); + auto MaskedPolicyScheme = + static_cast<PolicyScheme>(MPSRecord->getValueAsInt("Value")); + Record *UMPSRecord = R->getValueAsDef("UnMaskedPolicyScheme"); + auto UnMaskedPolicyScheme = + static_cast<PolicyScheme>(UMPSRecord->getValueAsInt("Value")); std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts("Log2LMUL"); + bool HasTailPolicy = R->getValueAsBit("HasTailPolicy"); + bool HasMaskPolicy = R->getValueAsBit("HasMaskPolicy"); + bool SupportOverloading = R->getValueAsBit("SupportOverloading"); + bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias"); StringRef ManualCodegen = R->getValueAsString("ManualCodegen"); - StringRef ManualCodegenMask = R->getValueAsString("ManualCodegenMask"); std::vector<int64_t> IntrinsicTypes = R->getValueAsListOfInts("IntrinsicTypes"); - StringRef RequiredExtension = R->getValueAsString("RequiredExtension"); + std::vector<StringRef> RequiredFeatures = + R->getValueAsListOfStrings("RequiredFeatures"); StringRef IRName = R->getValueAsString("IRName"); - StringRef IRNameMask = R->getValueAsString("IRNameMask"); + StringRef MaskedIRName = R->getValueAsString("MaskedIRName"); unsigned NF = R->getValueAsInt("NF"); + bool IsTuple = R->getValueAsBit("IsTuple"); + bool HasFRMRoundModeOp = R->getValueAsBit("HasFRMRoundModeOp"); + + const Policy DefaultPolicy; + SmallVector<Policy> SupportedUnMaskedPolicies = + RVVIntrinsic::getSupportedUnMaskedPolicies(); + SmallVector<Policy> SupportedMaskedPolicies = + RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy); - StringRef HeaderCodeStr = R->getValueAsString("HeaderCode"); - bool HasAutoDef = HeaderCodeStr.empty(); - if (!HeaderCodeStr.empty()) { - HeaderCode += HeaderCodeStr.str(); - } // Parse prototype and create a list of primitive type with transformers - // (operand) in ProtoSeq. ProtoSeq[0] is output operand. - SmallVector<std::string> ProtoSeq; - parsePrototypes(Prototypes, [&ProtoSeq](StringRef Proto) { - ProtoSeq.push_back(Proto.str()); - }); + // (operand) in Prototype. Prototype[0] is output operand. + SmallVector<PrototypeDescriptor> BasicPrototype = + parsePrototypes(Prototypes); + + SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(SuffixProto); + SmallVector<PrototypeDescriptor> OverloadedSuffixDesc = + parsePrototypes(OverloadedSuffixProto); // Compute Builtin types - SmallVector<std::string> ProtoMaskSeq = ProtoSeq; - if (HasMask) { - // If HasMaskedOffOperand, insert result type as first input operand. - if (HasMaskedOffOperand) { - if (NF == 1) { - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + 1, ProtoSeq[0]); - } else { - // Convert - // (void, op0 address, op1 address, ...) - // to - // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) - for (unsigned I = 0; I < NF; ++I) - ProtoMaskSeq.insert( - ProtoMaskSeq.begin() + NF + 1, - ProtoSeq[1].substr(1)); // Use substr(1) to skip '*' - } - } - if (HasMaskedOffOperand && NF > 1) { - // Convert - // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) - // to - // (void, op0 address, op1 address, ..., mask, maskedoff0, maskedoff1, - // ...) - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + NF + 1, "m"); - } else { - // If HasMask, insert 'm' as first input operand. - ProtoMaskSeq.insert(ProtoMaskSeq.begin() + 1, "m"); - } - } - // If HasVL, append 'z' to last operand - if (HasVL) { - ProtoSeq.push_back("z"); - ProtoMaskSeq.push_back("z"); - } + auto Prototype = RVVIntrinsic::computeBuiltinTypes( + BasicPrototype, /*IsMasked=*/false, + /*HasMaskedOffOperand=*/false, HasVL, NF, UnMaskedPolicyScheme, + DefaultPolicy, IsTuple); + llvm::SmallVector<PrototypeDescriptor> MaskedPrototype; + if (HasMasked) + MaskedPrototype = RVVIntrinsic::computeBuiltinTypes( + BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF, + MaskedPolicyScheme, DefaultPolicy, IsTuple); // Create Intrinsics for each type and LMUL. for (char I : TypeRange) { for (int Log2LMUL : Log2LMULList) { - Optional<RVVTypes> Types = computeTypes(I, Log2LMUL, NF, ProtoSeq); + BasicType BT = ParseBasicType(I); + std::optional<RVVTypes> Types = + TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype); // Ignored to create new intrinsic if there are any illegal types. - if (!Types.hasValue()) + if (!Types) continue; - auto SuffixStr = getSuffixStr(I, Log2LMUL, SuffixProto); - auto MangledSuffixStr = getSuffixStr(I, Log2LMUL, MangledSuffixProto); - // Create a non-mask intrinsic + auto SuffixStr = + RVVIntrinsic::getSuffixStr(TypeCache, BT, Log2LMUL, SuffixDesc); + auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr( + TypeCache, BT, Log2LMUL, OverloadedSuffixDesc); + // Create a unmasked intrinsic Out.push_back(std::make_unique<RVVIntrinsic>( - Name, SuffixStr, MangledName, MangledSuffixStr, IRName, - HasSideEffects, /*IsMask=*/false, /*HasMaskedOffOperand=*/false, - HasVL, HasNoMaskedOverloaded, HasAutoDef, ManualCodegen, - Types.getValue(), IntrinsicTypes, RequiredExtension, NF)); - if (HasMask) { - // Create a mask intrinsic - Optional<RVVTypes> MaskTypes = - computeTypes(I, Log2LMUL, NF, ProtoMaskSeq); + Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, + /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, + UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, + ManualCodegen, *Types, IntrinsicTypes, RequiredFeatures, NF, + DefaultPolicy, HasFRMRoundModeOp)); + if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone) + for (auto P : SupportedUnMaskedPolicies) { + SmallVector<PrototypeDescriptor> PolicyPrototype = + RVVIntrinsic::computeBuiltinTypes( + BasicPrototype, /*IsMasked=*/false, + /*HasMaskedOffOperand=*/false, HasVL, NF, + UnMaskedPolicyScheme, P, IsTuple); + std::optional<RVVTypes> PolicyTypes = + TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype); + Out.push_back(std::make_unique<RVVIntrinsic>( + Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, + /*IsMask=*/false, /*HasMaskedOffOperand=*/false, HasVL, + UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, + ManualCodegen, *PolicyTypes, IntrinsicTypes, RequiredFeatures, + NF, P, HasFRMRoundModeOp)); + } + if (!HasMasked) + continue; + // Create a masked intrinsic + std::optional<RVVTypes> MaskTypes = + TypeCache.computeTypes(BT, Log2LMUL, NF, MaskedPrototype); + Out.push_back(std::make_unique<RVVIntrinsic>( + Name, SuffixStr, OverloadedName, OverloadedSuffixStr, MaskedIRName, + /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicyScheme, + SupportOverloading, HasBuiltinAlias, ManualCodegen, *MaskTypes, + IntrinsicTypes, RequiredFeatures, NF, DefaultPolicy, + HasFRMRoundModeOp)); + if (MaskedPolicyScheme == PolicyScheme::SchemeNone) + continue; + for (auto P : SupportedMaskedPolicies) { + SmallVector<PrototypeDescriptor> PolicyPrototype = + RVVIntrinsic::computeBuiltinTypes( + BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, + NF, MaskedPolicyScheme, P, IsTuple); + std::optional<RVVTypes> PolicyTypes = + TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype); Out.push_back(std::make_unique<RVVIntrinsic>( - Name, SuffixStr, MangledName, MangledSuffixStr, IRNameMask, - HasSideEffects, /*IsMask=*/true, HasMaskedOffOperand, HasVL, - HasNoMaskedOverloaded, HasAutoDef, ManualCodegenMask, - MaskTypes.getValue(), IntrinsicTypes, RequiredExtension, NF)); + Name, SuffixStr, OverloadedName, OverloadedSuffixStr, + MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, + MaskedPolicyScheme, SupportOverloading, HasBuiltinAlias, + ManualCodegen, *PolicyTypes, IntrinsicTypes, RequiredFeatures, NF, + P, HasFRMRoundModeOp)); } - } // end for Log2LMULList - } // end for TypeRange - } -} + } // End for Log2LMULList + } // End for TypeRange + + // We don't emit vsetvli and vsetvlimax for SemaRecord. + // They are written in riscv_vector.td and will emit those marco define in + // riscv_vector.h + if (Name == "vsetvli" || Name == "vsetvlimax") + continue; + + if (!SemaRecords) + continue; + + // Create SemaRecord + SemaRecord SR; + SR.Name = Name.str(); + SR.OverloadedName = OverloadedName.str(); + BasicType TypeRangeMask = BasicType::Unknown; + for (char I : TypeRange) + TypeRangeMask |= ParseBasicType(I); + + SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask); + + unsigned Log2LMULMask = 0; + for (int Log2LMUL : Log2LMULList) + Log2LMULMask |= 1 << (Log2LMUL + 3); + + SR.Log2LMULMask = Log2LMULMask; + + SR.RequiredExtensions = 0; + for (auto RequiredFeature : RequiredFeatures) { + RVVRequire RequireExt = + StringSwitch<RVVRequire>(RequiredFeature) + .Case("RV64", RVV_REQ_RV64) + .Case("Zvfhmin", RVV_REQ_Zvfhmin) + .Case("Xsfvcp", RVV_REQ_Xsfvcp) + .Case("Xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf) + .Case("Xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq) + .Case("Xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod) + .Case("Xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq) + .Case("Zvbb", RVV_REQ_Zvbb) + .Case("Zvbc", RVV_REQ_Zvbc) + .Case("Zvkb", RVV_REQ_Zvkb) + .Case("Zvkg", RVV_REQ_Zvkg) + .Case("Zvkned", RVV_REQ_Zvkned) + .Case("Zvknha", RVV_REQ_Zvknha) + .Case("Zvknhb", RVV_REQ_Zvknhb) + .Case("Zvksed", RVV_REQ_Zvksed) + .Case("Zvksh", RVV_REQ_Zvksh) + .Case("Experimental", RVV_REQ_Experimental) + .Default(RVV_REQ_None); + assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?"); + SR.RequiredExtensions |= RequireExt; + } -Optional<RVVTypes> -RVVEmitter::computeTypes(BasicType BT, int Log2LMUL, unsigned NF, - ArrayRef<std::string> PrototypeSeq) { - // LMUL x NF must be less than or equal to 8. - if ((Log2LMUL >= 1) && (1 << Log2LMUL) * NF > 8) - return llvm::None; - - RVVTypes Types; - for (const std::string &Proto : PrototypeSeq) { - auto T = computeType(BT, Log2LMUL, Proto); - if (!T.hasValue()) - return llvm::None; - // Record legal type index - Types.push_back(T.getValue()); + SR.NF = NF; + SR.HasMasked = HasMasked; + SR.HasVL = HasVL; + SR.HasMaskedOffOperand = HasMaskedOffOperand; + SR.HasTailPolicy = HasTailPolicy; + SR.HasMaskPolicy = HasMaskPolicy; + SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme); + SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme); + SR.Prototype = std::move(BasicPrototype); + SR.Suffix = parsePrototypes(SuffixProto); + SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto); + SR.IsTuple = IsTuple; + SR.HasFRMRoundModeOp = HasFRMRoundModeOp; + + SemaRecords->push_back(SR); } - return Types; } -Optional<RVVTypePtr> RVVEmitter::computeType(BasicType BT, int Log2LMUL, - StringRef Proto) { - std::string Idx = Twine(Twine(BT) + Twine(Log2LMUL) + Proto).str(); - // Search first - auto It = LegalTypes.find(Idx); - if (It != LegalTypes.end()) - return &(It->second); - if (IllegalTypes.count(Idx)) - return llvm::None; - // Compute type and record the result. - RVVType T(BT, Log2LMUL, Proto); - if (T.isValid()) { - // Record legal type index and value. - LegalTypes.insert({Idx, T}); - return &(LegalTypes[Idx]); +void RVVEmitter::printHeaderCode(raw_ostream &OS) { + std::vector<Record *> RVVHeaders = + Records.getAllDerivedDefinitions("RVVHeader"); + for (auto *R : RVVHeaders) { + StringRef HeaderCodeStr = R->getValueAsString("HeaderCode"); + OS << HeaderCodeStr.str(); } - // Record illegal type index. - IllegalTypes.insert(Idx); - return llvm::None; } -void RVVEmitter::emitArchMacroAndBody( - std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &OS, - std::function<void(raw_ostream &, const RVVIntrinsic &)> PrintBody) { - uint8_t PrevExt = (*Defs.begin())->getRISCVExtensions(); - bool NeedEndif = emitExtDefStr(PrevExt, OS); - for (auto &Def : Defs) { - uint8_t CurExt = Def->getRISCVExtensions(); - if (CurExt != PrevExt) { - if (NeedEndif) - OS << "#endif\n\n"; - NeedEndif = emitExtDefStr(CurExt, OS); - PrevExt = CurExt; - } - if (Def->hasAutoDef()) - PrintBody(OS, *Def); +void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out, + SemaSignatureTable &SST, + ArrayRef<SemaRecord> SemaRecords) { + SST.init(SemaRecords); + + for (const auto &SR : SemaRecords) { + Out.emplace_back(RVVIntrinsicRecord()); + RVVIntrinsicRecord &R = Out.back(); + R.Name = SR.Name.c_str(); + R.OverloadedName = SR.OverloadedName.c_str(); + R.PrototypeIndex = SST.getIndex(SR.Prototype); + R.SuffixIndex = SST.getIndex(SR.Suffix); + R.OverloadedSuffixIndex = SST.getIndex(SR.OverloadedSuffix); + R.PrototypeLength = SR.Prototype.size(); + R.SuffixLength = SR.Suffix.size(); + R.OverloadedSuffixSize = SR.OverloadedSuffix.size(); + R.RequiredExtensions = SR.RequiredExtensions; + R.TypeRangeMask = SR.TypeRangeMask; + R.Log2LMULMask = SR.Log2LMULMask; + R.NF = SR.NF; + R.HasMasked = SR.HasMasked; + R.HasVL = SR.HasVL; + R.HasMaskedOffOperand = SR.HasMaskedOffOperand; + R.HasTailPolicy = SR.HasTailPolicy; + R.HasMaskPolicy = SR.HasMaskPolicy; + R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme; + R.MaskedPolicyScheme = SR.MaskedPolicyScheme; + R.IsTuple = SR.IsTuple; + R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp; + + assert(R.PrototypeIndex != + static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); + assert(R.SuffixIndex != + static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); + assert(R.OverloadedSuffixIndex != + static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); } - if (NeedEndif) - OS << "#endif\n\n"; } -bool RVVEmitter::emitExtDefStr(uint8_t Extents, raw_ostream &OS) { - if (Extents == RISCVExtension::Basic) - return false; - OS << "#if "; - ListSeparator LS(" && "); - if (Extents & RISCVExtension::F) - OS << LS << "defined(__riscv_f)"; - if (Extents & RISCVExtension::D) - OS << LS << "defined(__riscv_d)"; - if (Extents & RISCVExtension::Zfh) - OS << LS << "defined(__riscv_zfh)"; - if (Extents & RISCVExtension::Zvamo) - OS << LS << "defined(__riscv_zvamo)"; - if (Extents & RISCVExtension::Zvlsseg) - OS << LS << "defined(__riscv_zvlsseg)"; - OS << "\n"; - return true; +void RVVEmitter::createSema(raw_ostream &OS) { + std::vector<std::unique_ptr<RVVIntrinsic>> Defs; + std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords; + SemaSignatureTable SST; + std::vector<SemaRecord> SemaRecords; + + createRVVIntrinsics(Defs, &SemaRecords); + + createRVVIntrinsicRecords(RVVIntrinsicRecords, SST, SemaRecords); + + // Emit signature table for SemaRISCVVectorLookup.cpp. + OS << "#ifdef DECL_SIGNATURE_TABLE\n"; + SST.print(OS); + OS << "#endif\n"; + + // Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp. + OS << "#ifdef DECL_INTRINSIC_RECORDS\n"; + for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords) + OS << Record; + OS << "#endif\n"; } namespace clang { @@ -1266,4 +778,8 @@ void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { RVVEmitter(Records).createCodeGen(OS); } +void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) { + RVVEmitter(Records).createSema(OS); +} + } // End namespace clang diff --git a/contrib/llvm-project/clang/utils/TableGen/SveEmitter.cpp b/contrib/llvm-project/clang/utils/TableGen/SveEmitter.cpp index b2f6ede56522..174304f09007 100644 --- a/contrib/llvm-project/clang/utils/TableGen/SveEmitter.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/SveEmitter.cpp @@ -23,16 +23,17 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringMap.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/TableGen/Record.h" +#include "llvm/ADT/StringMap.h" #include "llvm/TableGen/Error.h" -#include <string> -#include <sstream> -#include <set> +#include "llvm/TableGen/Record.h" +#include <array> #include <cctype> +#include <set> +#include <sstream> +#include <string> #include <tuple> using namespace llvm; @@ -43,6 +44,8 @@ enum ClassKind { ClassG, // Overloaded name without type suffix }; +enum class ACLEKind { SVE, SME }; + using TypeSpec = std::string; namespace { @@ -64,24 +67,29 @@ public: }; class SVEType { - TypeSpec TS; bool Float, Signed, Immediate, Void, Constant, Pointer, BFloat; - bool DefaultType, IsScalable, Predicate, PredicatePattern, PrefetchOp; + bool DefaultType, IsScalable, Predicate, PredicatePattern, PrefetchOp, + Svcount; unsigned Bitwidth, ElementBitwidth, NumVectors; public: - SVEType() : SVEType(TypeSpec(), 'v') {} + SVEType() : SVEType("", 'v') {} - SVEType(TypeSpec TS, char CharMod) - : TS(TS), Float(false), Signed(true), Immediate(false), Void(false), + SVEType(StringRef TS, char CharMod, unsigned NumVectors = 1) + : 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) { + PrefetchOp(false), Svcount(false), Bitwidth(128), ElementBitwidth(~0U), + NumVectors(NumVectors) { if (!TS.empty()) - applyTypespec(); + applyTypespec(TS); applyModifier(CharMod); } + SVEType(const SVEType &Base, unsigned NumV) : SVEType(Base) { + NumVectors = NumV; + } + bool isPointer() const { return Pointer; } bool isVoidPointer() const { return Pointer && Void; } bool isSigned() const { return Signed; } @@ -89,19 +97,23 @@ public: bool isScalar() const { return NumVectors == 0; } bool isVector() const { return NumVectors > 0; } bool isScalableVector() const { return isVector() && IsScalable; } + bool isFixedLengthVector() 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 isInteger() const { + return !isFloatingPoint() && !Predicate && !Svcount; + } bool isScalarPredicate() const { return !isFloatingPoint() && Predicate && NumVectors == 0; } bool isPredicateVector() const { return Predicate; } bool isPredicatePattern() const { return PredicatePattern; } bool isPrefetchOp() const { return PrefetchOp; } + bool isSvcount() const { return Svcount; } bool isConstant() const { return Constant; } unsigned getElementSizeInBits() const { return ElementBitwidth; } unsigned getNumVectors() const { return NumVectors; } @@ -124,13 +136,12 @@ public: private: /// Creates the type based on the typespec string in TS. - void applyTypespec(); + void applyTypespec(StringRef TS); /// 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 @@ -189,7 +200,9 @@ public: 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; } + unsigned getNumParams() const { + return Proto.size() - (2 * llvm::count(Proto, '.')) - 1; + } uint64_t getFlags() const { return Flags; } bool isFlagSet(uint64_t Flag) const { return Flags & Flag;} @@ -203,6 +216,9 @@ public: /// ClassS, so will add type suffixes such as _u32/_s32. std::string getMangledName() const { return mangleName(ClassS); } + /// As above, but mangles the LLVM name instead. + std::string getMangledLLVMName() const { return mangleLLVMName(); } + /// 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(..)'. @@ -220,19 +236,28 @@ public: /// 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; + unsigned I = 1, Param = 0; + for (; I < Proto.size(); ++I, ++Param) { + if (Proto[I] == 'a' || Proto[I] == 'j' || Proto[I] == 'f' || + Proto[I] == 'r' || Proto[I] == 'K' || Proto[I] == 'L' || + Proto[I] == 'R' || Proto[I] == '@') + break; + + // Multivector modifier can be skipped + if (Proto[I] == '.') + I += 2; + } + assert(I != Proto.size() && "Prototype has no splat operand"); + return Param; } /// Emits the intrinsic declaration to the ostream. - void emitIntrinsic(raw_ostream &OS) const; + void emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, ACLEKind Kind) const; private: std::string getMergeSuffix() const { return MergeSuffix; } std::string mangleName(ClassKind LocalCK) const; + std::string mangleLLVMName() const; std::string replaceTemplatedArgs(std::string Name, TypeSpec TS, std::string Proto) const; }; @@ -244,17 +269,11 @@ private: // which is inconvenient to specify in the arm_sve.td file or // generate in CGBuiltin.cpp. struct ReinterpretTypeInfo { + SVEType BaseType; 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"}}; + + static const std::array<ReinterpretTypeInfo, 12> Reinterprets; RecordKeeper &Records; llvm::StringMap<uint64_t> EltTypes; @@ -298,7 +317,8 @@ public: auto It = FlagTypes.find(MaskName); if (It != FlagTypes.end()) { uint64_t Mask = It->getValue(); - unsigned Shift = llvm::countTrailingZeros(Mask); + unsigned Shift = llvm::countr_zero(Mask); + assert(Shift < 64 && "Mask value produced an invalid shift value"); return (V << Shift) & Mask; } llvm_unreachable("Unsupported flag"); @@ -334,6 +354,10 @@ public: /// Emit arm_sve.h. void createHeader(raw_ostream &o); + // Emits core intrinsics in both arm_sme.h and arm_sve.h + void createCoreHeaderIntrinsics(raw_ostream &o, SVEEmitter &Emitter, + ACLEKind Kind); + /// Emit all the __builtin prototypes and code needed by Sema. void createBuiltins(raw_ostream &o); @@ -346,10 +370,43 @@ public: /// Create the SVETypeFlags used in CGBuiltins void createTypeFlags(raw_ostream &o); + /// Emit arm_sme.h. + void createSMEHeader(raw_ostream &o); + + /// Emit all the SME __builtin prototypes and code needed by Sema. + void createSMEBuiltins(raw_ostream &o); + + /// Emit all the information needed to map builtin -> LLVM IR intrinsic. + void createSMECodeGenMap(raw_ostream &o); + + /// Create a table for a builtin's requirement for PSTATE.SM. + void createStreamingAttrs(raw_ostream &o, ACLEKind Kind); + + /// Emit all the range checks for the immediates. + void createSMERangeChecks(raw_ostream &o); + + /// Create a table for a builtin's requirement for PSTATE.ZA. + void createBuiltinZAState(raw_ostream &OS); + /// Create intrinsic and add it to \p Out - void createIntrinsic(Record *R, SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out); + void createIntrinsic(Record *R, + SmallVectorImpl<std::unique_ptr<Intrinsic>> &Out); }; +const std::array<SVEEmitter::ReinterpretTypeInfo, 12> SVEEmitter::Reinterprets = + {{{SVEType("c", 'd'), "s8"}, + {SVEType("Uc", 'd'), "u8"}, + {SVEType("s", 'd'), "s16"}, + {SVEType("Us", 'd'), "u16"}, + {SVEType("i", 'd'), "s32"}, + {SVEType("Ui", 'd'), "u32"}, + {SVEType("l", 'd'), "s64"}, + {SVEType("Ul", 'd'), "u64"}, + {SVEType("h", 'd'), "f16"}, + {SVEType("b", 'd'), "bf16"}, + {SVEType("f", 'd'), "f32"}, + {SVEType("d", 'd'), "f64"}}}; + } // end anonymous namespace @@ -365,6 +422,9 @@ std::string SVEType::builtin_str() const { if (isScalarPredicate()) return "b"; + if (isSvcount()) + return "Qa"; + if (isVoidPointer()) S += "v"; else if (!isFloatingPoint()) @@ -413,7 +473,8 @@ std::string SVEType::builtin_str() const { return S; } - assert(isScalableVector() && "Unsupported type"); + if (isFixedLengthVector()) + return "V" + utostr(getNumElements() * NumVectors) + S; return "q" + utostr(getNumElements() * NumVectors) + S; } @@ -428,13 +489,15 @@ std::string SVEType::str() const { if (Void) S += "void"; else { - if (isScalableVector()) + if (isScalableVector() || isSvcount()) S += "sv"; if (!Signed && !isFloatingPoint()) S += "u"; if (Float) S += "float"; + else if (isSvcount()) + S += "count"; else if (isScalarPredicate() || isPredicateVector()) S += "bool"; else if (isBFloat()) @@ -442,9 +505,9 @@ std::string SVEType::str() const { else S += "int"; - if (!isScalarPredicate() && !isPredicateVector()) + if (!isScalarPredicate() && !isPredicateVector() && !isSvcount()) S += utostr(ElementBitwidth); - if (!isScalableVector() && isVector()) + if (isFixedLengthVector()) S += "x" + utostr(getNumElements()); if (NumVectors > 1) S += "x" + utostr(NumVectors); @@ -459,9 +522,13 @@ std::string SVEType::str() const { return S; } -void SVEType::applyTypespec() { + +void SVEType::applyTypespec(StringRef TS) { for (char I : TS) { switch (I) { + case 'Q': + Svcount = true; + break; case 'P': Predicate = true; break; @@ -480,6 +547,9 @@ void SVEType::applyTypespec() { case 'l': ElementBitwidth = 64; break; + case 'q': + ElementBitwidth = 128; + break; case 'h': Float = true; ElementBitwidth = 16; @@ -506,15 +576,6 @@ void SVEType::applyTypespec() { 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; @@ -523,7 +584,7 @@ void SVEType::applyModifier(char Mod) { break; case 'c': Constant = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case 'p': Pointer = true; Bitwidth = ElementBitwidth; @@ -553,9 +614,15 @@ void SVEType::applyModifier(char Mod) { Float = false; BFloat = false; Predicate = true; + Svcount = false; Bitwidth = 16; ElementBitwidth = 1; break; + case '{': + IsScalable = false; + Bitwidth = 128; + NumVectors = 1; + break; case 's': case 'a': Bitwidth = ElementBitwidth; @@ -592,18 +659,21 @@ void SVEType::applyModifier(char Mod) { break; case 'u': Predicate = false; + Svcount = false; Signed = false; Float = false; BFloat = false; break; case 'x': Predicate = false; + Svcount = false; Signed = true; Float = false; BFloat = false; break; case 'i': Predicate = false; + Svcount = false; Float = false; BFloat = false; ElementBitwidth = Bitwidth = 64; @@ -613,6 +683,7 @@ void SVEType::applyModifier(char Mod) { break; case 'I': Predicate = false; + Svcount = false; Float = false; BFloat = false; ElementBitwidth = Bitwidth = 32; @@ -623,6 +694,7 @@ void SVEType::applyModifier(char Mod) { break; case 'J': Predicate = false; + Svcount = false; Float = false; BFloat = false; ElementBitwidth = Bitwidth = 32; @@ -633,6 +705,7 @@ void SVEType::applyModifier(char Mod) { break; case 'k': Predicate = false; + Svcount = false; Signed = true; Float = false; BFloat = false; @@ -641,6 +714,7 @@ void SVEType::applyModifier(char Mod) { break; case 'l': Predicate = false; + Svcount = false; Signed = true; Float = false; BFloat = false; @@ -649,6 +723,7 @@ void SVEType::applyModifier(char Mod) { break; case 'm': Predicate = false; + Svcount = false; Signed = false; Float = false; BFloat = false; @@ -657,6 +732,7 @@ void SVEType::applyModifier(char Mod) { break; case 'n': Predicate = false; + Svcount = false; Signed = false; Float = false; BFloat = false; @@ -681,6 +757,12 @@ void SVEType::applyModifier(char Mod) { BFloat = false; ElementBitwidth = 64; break; + case '[': + Signed = false; + Float = false; + BFloat = false; + ElementBitwidth = 8; + break; case 't': Signed = true; Float = false; @@ -695,17 +777,20 @@ void SVEType::applyModifier(char Mod) { break; case 'O': Predicate = false; + Svcount = false; Float = true; ElementBitwidth = 16; break; case 'M': Predicate = false; + Svcount = false; Float = true; BFloat = false; ElementBitwidth = 32; break; case 'N': Predicate = false; + Svcount = false; Float = true; ElementBitwidth = 64; break; @@ -757,6 +842,11 @@ void SVEType::applyModifier(char Mod) { NumVectors = 0; Signed = true; break; + case '%': + Pointer = true; + Void = true; + NumVectors = 0; + break; case 'A': Pointer = true; ElementBitwidth = Bitwidth = 8; @@ -799,11 +889,51 @@ void SVEType::applyModifier(char Mod) { NumVectors = 0; Signed = false; break; + case '$': + Predicate = false; + Svcount = false; + Float = false; + BFloat = true; + ElementBitwidth = 16; + break; + case '}': + Predicate = false; + Signed = true; + Svcount = true; + NumVectors = 0; + Float = false; + BFloat = false; + break; + case '.': + llvm_unreachable(". is never a type in itself"); + break; default: llvm_unreachable("Unhandled character!"); } } +/// Returns the modifier and number of vectors for the given operand \p Op. +std::pair<char, unsigned> getProtoModifier(StringRef Proto, unsigned Op) { + for (unsigned P = 0; !Proto.empty(); ++P) { + unsigned NumVectors = 1; + unsigned CharsToSkip = 1; + char Mod = Proto[0]; + if (Mod == '2' || Mod == '3' || Mod == '4') { + NumVectors = Mod - '0'; + Mod = 'd'; + if (Proto.size() > 1 && Proto[1] == '.') { + Mod = Proto[2]; + CharsToSkip = 3; + } + } + + if (P == Op) + return {Mod, NumVectors}; + + Proto = Proto.drop_front(CharsToSkip); + } + llvm_unreachable("Unexpected Op"); +} //===----------------------------------------------------------------------===// // Intrinsic implementation @@ -819,8 +949,11 @@ Intrinsic::Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy, 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]); + for (unsigned I = 0; I < (getNumParams() + 1); ++I) { + char Mod; + unsigned NumVectors; + std::tie(Mod, NumVectors) = getProtoModifier(Proto, I); + SVEType T(BaseTypeSpec, Mod, NumVectors); Types.push_back(T); // Add range checks for immediates @@ -879,6 +1012,8 @@ std::string Intrinsic::replaceTemplatedArgs(std::string Name, TypeSpec TS, std::string TypeCode; if (T.isInteger()) TypeCode = T.isSigned() ? 's' : 'u'; + else if (T.isSvcount()) + TypeCode = 'c'; else if (T.isPredicateVector()) TypeCode = 'b'; else if (T.isBFloat()) @@ -891,6 +1026,13 @@ std::string Intrinsic::replaceTemplatedArgs(std::string Name, TypeSpec TS, return Ret; } +std::string Intrinsic::mangleLLVMName() const { + std::string S = getLLVMName(); + + // Replace all {d} like expressions with e.g. 'u32' + return replaceTemplatedArgs(S, getBaseTypeSpec(), getProto()); +} + std::string Intrinsic::mangleName(ClassKind LocalCK) const { std::string S = getName(); @@ -918,15 +1060,25 @@ std::string Intrinsic::mangleName(ClassKind LocalCK) const { getMergeSuffix(); } -void Intrinsic::emitIntrinsic(raw_ostream &OS) const { +void Intrinsic::emitIntrinsic(raw_ostream &OS, SVEEmitter &Emitter, + ACLEKind Kind) const { bool IsOverloaded = getClassKind() == ClassG && getProto().size() > 1; std::string FullName = mangleName(ClassS); std::string ProtoName = mangleName(getClassKind()); - OS << (IsOverloaded ? "__aio " : "__ai ") - << "__attribute__((__clang_arm_builtin_alias(" - << "__builtin_sve_" << FullName << ")))\n"; + << "__attribute__((__clang_arm_builtin_alias("; + + switch (Kind) { + case ACLEKind::SME: + OS << "__builtin_sme_" << FullName << ")"; + break; + case ACLEKind::SVE: + OS << "__builtin_sve_" << FullName << ")"; + break; + } + + OS << "))\n"; OS << getTypes()[0].str() << " " << ProtoName << "("; for (unsigned I = 0; I < getTypes().size() - 1; ++I) { @@ -959,7 +1111,7 @@ uint64_t SVEEmitter::encodeTypeFlags(const SVEType &T) { return encodeEltType("EltTyBFloat16"); } - if (T.isPredicateVector()) { + if (T.isPredicateVector() || T.isSvcount()) { switch (T.getElementSizeInBits()) { case 8: return encodeEltType("EltTyBool8"); @@ -983,6 +1135,8 @@ uint64_t SVEEmitter::encodeTypeFlags(const SVEType &T) { return encodeEltType("EltTyInt32"); case 64: return encodeEltType("EltTyInt64"); + case 128: + return encodeEltType("EltTyInt128"); default: llvm_unreachable("Unhandled integer element bitwidth!"); } @@ -993,7 +1147,7 @@ void SVEEmitter::createIntrinsic( StringRef Name = R->getValueAsString("Name"); StringRef Proto = R->getValueAsString("Prototype"); StringRef Types = R->getValueAsString("Types"); - StringRef Guard = R->getValueAsString("ArchGuard"); + StringRef Guard = R->getValueAsString("TargetGuard"); StringRef LLVMName = R->getValueAsString("LLVMIntrinsic"); uint64_t Merge = R->getValueAsInt("Merge"); StringRef MergeSuffix = R->getValueAsString("MergeSuffix"); @@ -1039,10 +1193,11 @@ void SVEEmitter::createIntrinsic( assert(Arg >= 0 && Kind >= 0 && "Arg and Kind must be nonnegative"); unsigned ElementSizeInBits = 0; + char Mod; + unsigned NumVectors; + std::tie(Mod, NumVectors) = getProtoModifier(Proto, EltSizeArg + 1); if (EltSizeArg >= 0) - ElementSizeInBits = - SVEType(TS, Proto[EltSizeArg + /* offset by return arg */ 1]) - .getElementSizeInBits(); + ElementSizeInBits = SVEType(TS, Mod, NumVectors).getElementSizeInBits(); ImmChecks.push_back(ImmCheck(Arg, Kind, ElementSizeInBits)); } @@ -1058,6 +1213,34 @@ void SVEEmitter::createIntrinsic( } } +void SVEEmitter::createCoreHeaderIntrinsics(raw_ostream &OS, + SVEEmitter &Emitter, + ACLEKind Kind) { + 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); + }); + + // Actually emit the intrinsic declarations. + for (auto &I : Defs) + I->emitIntrinsic(OS, Emitter, Kind); +} + void SVEEmitter::createHeader(raw_ostream &OS) { OS << "/*===---- arm_sve.h - ARM SVE intrinsics " "-----------------------------------===\n" @@ -1075,10 +1258,6 @@ void SVEEmitter::createHeader(raw_ostream &OS) { 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"; @@ -1104,20 +1283,10 @@ void SVEEmitter::createHeader(raw_ostream &OS) { 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 << "typedef __SVBfloat16_t svbfloat16_t;\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 << "#include <arm_vector_types.h>\n"; OS << "typedef __SVFloat32_t svfloat32_t;\n"; OS << "typedef __SVFloat64_t svfloat64_t;\n"; @@ -1154,13 +1323,15 @@ void SVEEmitter::createHeader(raw_ostream &OS) { 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 << "typedef __SVBool_t svbool_t;\n"; + OS << "typedef __clang_svboolx2_t svboolx2_t;\n"; + OS << "typedef __clang_svboolx4_t svboolx4_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 __SVCount_t svcount_t;\n\n"; OS << "enum svpattern\n"; OS << "{\n"; @@ -1206,69 +1377,34 @@ void SVEEmitter::createHeader(raw_ostream &OS) { "__nodebug__, __overloadable__))\n\n"; // Add reinterpret functions. - for (auto ShortForm : { false, true } ) - for (const ReinterpretTypeInfo &From : Reinterprets) + for (auto [N, Suffix] : + std::initializer_list<std::pair<unsigned, const char *>>{ + {1, ""}, {2, "_x2"}, {3, "_x3"}, {4, "_x4"}}) { + for (auto ShortForm : {false, true}) 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"; + SVEType ToV(To.BaseType, N); + for (const ReinterpretTypeInfo &From : Reinterprets) { + SVEType FromV(From.BaseType, N); + if (ShortForm) { + OS << "__aio __attribute__((target(\"sve\"))) " << ToV.str() + << " svreinterpret_" << To.Suffix; + OS << "(" << FromV.str() << " op) __arm_streaming_compatible {\n"; + OS << " return __builtin_sve_reinterpret_" << To.Suffix << "_" + << From.Suffix << Suffix << "(op);\n"; + OS << "}\n\n"; + } else + OS << "#define svreinterpret_" << To.Suffix << "_" << From.Suffix + << Suffix << "(...) __builtin_sve_reinterpret_" << To.Suffix + << "_" << From.Suffix << Suffix << "(__VA_ARGS__)\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"; + createCoreHeaderIntrinsics(OS, *this, ACLEKind::SVE); - 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"; @@ -1277,12 +1413,11 @@ void SVEEmitter::createHeader(raw_ostream &OS) { 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 << "#undef __ai\n\n"; + OS << "#undef __aio\n\n"; OS << "#endif /* __ARM_SVE_H */\n"; } @@ -1303,19 +1438,28 @@ void SVEEmitter::createBuiltins(raw_ostream &OS) { // 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"; + OS << "TARGET_BUILTIN(__builtin_sve_" << Def->getMangledName() << ", \"" + << Def->getBuiltinTypeStr() << "\", \"n\", \"" << Def->getGuard() + << "\")\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"; + // Add reinterpret functions. + for (auto [N, Suffix] : + std::initializer_list<std::pair<unsigned, const char *>>{ + {1, ""}, {2, "_x2"}, {3, "_x3"}, {4, "_x4"}}) { + for (const ReinterpretTypeInfo &To : Reinterprets) { + SVEType ToV(To.BaseType, N); + for (const ReinterpretTypeInfo &From : Reinterprets) { + SVEType FromV(From.BaseType, N); + OS << "TARGET_BUILTIN(__builtin_sve_reinterpret_" << To.Suffix << "_" + << From.Suffix << Suffix << +", \"" << ToV.builtin_str() + << FromV.builtin_str() << "\", \"n\", \"sve\")\n"; + } + } + } OS << "#endif\n\n"; - } +} void SVEEmitter::createCodeGenMap(raw_ostream &OS) { std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); @@ -1339,7 +1483,7 @@ void SVEEmitter::createCodeGenMap(raw_ostream &OS) { uint64_t Flags = Def->getFlags(); auto FlagString = std::to_string(Flags); - std::string LLVMName = Def->getLLVMName(); + std::string LLVMName = Def->getMangledLLVMName(); std::string Builtin = Def->getMangledName(); if (!LLVMName.empty()) OS << "SVEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString @@ -1413,6 +1557,251 @@ void SVEEmitter::createTypeFlags(raw_ostream &OS) { OS << "#endif\n\n"; } +void SVEEmitter::createSMEHeader(raw_ostream &OS) { + OS << "/*===---- arm_sme.h - ARM SME 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_SME_H\n"; + OS << "#define __ARM_SME_H\n\n"; + + OS << "#if !defined(__LITTLE_ENDIAN__)\n"; + OS << "#error \"Big endian is currently not supported for arm_sme.h\"\n"; + OS << "#endif\n"; + + OS << "#include <arm_sve.h>\n\n"; + + OS << "/* Function attributes */\n"; + OS << "#define __ai static __inline__ __attribute__((__always_inline__, " + "__nodebug__))\n\n"; + OS << "#define __aio static __inline__ __attribute__((__always_inline__, " + "__nodebug__, __overloadable__))\n\n"; + + OS << "#ifdef __cplusplus\n"; + OS << "extern \"C\" {\n"; + OS << "#endif\n\n"; + + OS << "void __arm_za_disable(void) __arm_streaming_compatible;\n\n"; + + OS << "__ai bool __arm_has_sme(void) __arm_streaming_compatible {\n"; + OS << " uint64_t x0, x1;\n"; + OS << " __builtin_arm_get_sme_state(&x0, &x1);\n"; + OS << " return x0 & (1ULL << 63);\n"; + OS << "}\n\n"; + + OS << "__ai bool __arm_in_streaming_mode(void) __arm_streaming_compatible " + "{\n"; + OS << " uint64_t x0, x1;\n"; + OS << " __builtin_arm_get_sme_state(&x0, &x1);\n"; + OS << " return x0 & 1;\n"; + OS << "}\n\n"; + + OS << "__ai __attribute__((target(\"sme\"))) void svundef_za(void) " + "__arm_streaming_compatible __arm_out(\"za\") " + "{ }\n\n"; + + createCoreHeaderIntrinsics(OS, *this, ACLEKind::SME); + + OS << "#ifdef __cplusplus\n"; + OS << "} // extern \"C\"\n"; + OS << "#endif\n\n"; + OS << "#undef __ai\n\n"; + OS << "#endif /* __ARM_SME_H */\n"; +} + +void SVEEmitter::createSMEBuiltins(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_SME_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 << "TARGET_BUILTIN(__builtin_sme_" << Def->getMangledName() << ", \"" + << Def->getBuiltinTypeStr() << "\", \"n\", \"" << Def->getGuard() + << "\")\n"; + } + + OS << "#endif\n\n"; +} + +void SVEEmitter::createSMECodeGenMap(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_SME_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 << "SMEMAP1(" << Builtin << ", " << LLVMName << ", " << FlagString + << "),\n"; + else + OS << "SMEMAP2(" << Builtin << ", " << FlagString << "),\n"; + } + OS << "#endif\n\n"; +} + +void SVEEmitter::createSMERangeChecks(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_SME_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 SME::BI__builtin_sme_" << 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"; +} + +void SVEEmitter::createBuiltinZAState(raw_ostream &OS) { + std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); + SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; + for (auto *R : RV) + createIntrinsic(R, Defs); + + std::map<std::string, std::set<std::string>> IntrinsicsPerState; + for (auto &Def : Defs) { + std::string Key; + auto AddToKey = [&Key](const std::string &S) -> void { + Key = Key.empty() ? S : (Key + " | " + S); + }; + + if (Def->isFlagSet(getEnumValueForFlag("IsInZA"))) + AddToKey("ArmInZA"); + else if (Def->isFlagSet(getEnumValueForFlag("IsOutZA"))) + AddToKey("ArmOutZA"); + else if (Def->isFlagSet(getEnumValueForFlag("IsInOutZA"))) + AddToKey("ArmInOutZA"); + + if (Def->isFlagSet(getEnumValueForFlag("IsInZT0"))) + AddToKey("ArmInZT0"); + else if (Def->isFlagSet(getEnumValueForFlag("IsOutZT0"))) + AddToKey("ArmOutZT0"); + else if (Def->isFlagSet(getEnumValueForFlag("IsInOutZT0"))) + AddToKey("ArmInOutZT0"); + + if (!Key.empty()) + IntrinsicsPerState[Key].insert(Def->getMangledName()); + } + + OS << "#ifdef GET_SME_BUILTIN_GET_STATE\n"; + for (auto &KV : IntrinsicsPerState) { + for (StringRef Name : KV.second) + OS << "case SME::BI__builtin_sme_" << Name << ":\n"; + OS << " return " << KV.first << ";\n"; + } + OS << "#endif\n\n"; +} + +void SVEEmitter::createStreamingAttrs(raw_ostream &OS, ACLEKind Kind) { + std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); + SmallVector<std::unique_ptr<Intrinsic>, 128> Defs; + for (auto *R : RV) + createIntrinsic(R, Defs); + + StringRef ExtensionKind; + switch (Kind) { + case ACLEKind::SME: + ExtensionKind = "SME"; + break; + case ACLEKind::SVE: + ExtensionKind = "SVE"; + break; + } + + OS << "#ifdef GET_" << ExtensionKind << "_STREAMING_ATTRS\n"; + + llvm::StringMap<std::set<std::string>> StreamingMap; + + uint64_t IsStreamingFlag = getEnumValueForFlag("IsStreaming"); + uint64_t IsStreamingOrSVE2p1Flag = getEnumValueForFlag("IsStreamingOrSVE2p1"); + uint64_t IsStreamingCompatibleFlag = + getEnumValueForFlag("IsStreamingCompatible"); + for (auto &Def : Defs) { + if (Def->isFlagSet(IsStreamingFlag)) + StreamingMap["ArmStreaming"].insert(Def->getMangledName()); + else if (Def->isFlagSet(IsStreamingOrSVE2p1Flag)) + StreamingMap["ArmStreamingOrSVE2p1"].insert(Def->getMangledName()); + else if (Def->isFlagSet(IsStreamingCompatibleFlag)) + StreamingMap["ArmStreamingCompatible"].insert(Def->getMangledName()); + else + StreamingMap["ArmNonStreaming"].insert(Def->getMangledName()); + } + + for (auto BuiltinType : StreamingMap.keys()) { + for (auto Name : StreamingMap[BuiltinType]) { + OS << "case " << ExtensionKind << "::BI__builtin_" + << ExtensionKind.lower() << "_"; + OS << Name << ":\n"; + } + OS << " BuiltinType = " << BuiltinType << ";\n"; + OS << " break;\n"; + } + + OS << "#endif\n\n"; +} + namespace clang { void EmitSveHeader(RecordKeeper &Records, raw_ostream &OS) { SVEEmitter(Records).createHeader(OS); @@ -1434,4 +1823,31 @@ void EmitSveTypeFlags(RecordKeeper &Records, raw_ostream &OS) { SVEEmitter(Records).createTypeFlags(OS); } +void EmitSveStreamingAttrs(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createStreamingAttrs(OS, ACLEKind::SVE); +} + +void EmitSmeHeader(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createSMEHeader(OS); +} + +void EmitSmeBuiltins(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createSMEBuiltins(OS); +} + +void EmitSmeBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createSMECodeGenMap(OS); +} + +void EmitSmeRangeChecks(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createSMERangeChecks(OS); +} + +void EmitSmeStreamingAttrs(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createStreamingAttrs(OS, ACLEKind::SME); +} + +void EmitSmeBuiltinZAState(RecordKeeper &Records, raw_ostream &OS) { + SVEEmitter(Records).createBuiltinZAState(OS); +} } // End namespace clang diff --git a/contrib/llvm-project/clang/utils/TableGen/TableGen.cpp b/contrib/llvm-project/clang/utils/TableGen/TableGen.cpp index 7fb5d0acc6f3..158d10e2b3d6 100644 --- a/contrib/llvm-project/clang/utils/TableGen/TableGen.cpp +++ b/contrib/llvm-project/clang/utils/TableGen/TableGen.cpp @@ -13,6 +13,7 @@ #include "TableGenBackends.h" // Declares all backends. #include "ASTTableGen.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/TableGen/Error.h" @@ -30,9 +31,13 @@ enum ActionType { GenClangAttrSubjectMatchRulesParserStringSwitches, GenClangAttrImpl, GenClangAttrList, + GenClangAttrCanPrintLeftList, + GenClangAttrMustPrintLeftList, + GenClangAttrDocTable, GenClangAttrSubjectMatchRuleList, GenClangAttrPCHRead, GenClangAttrPCHWrite, + GenClangRegularKeywordAttributeInfo, GenClangAttrHasAttributeImpl, GenClangAttrSpellingListIndex, GenClangAttrASTVisitor, @@ -63,10 +68,12 @@ enum ActionType { GenClangCommentCommandInfo, GenClangCommentCommandList, GenClangOpenCLBuiltins, + GenClangOpenCLBuiltinHeader, GenClangOpenCLBuiltinTests, GenArmNeon, GenArmFP16, GenArmBF16, + GenArmVectorType, GenArmNeonSema, GenArmNeonTest, GenArmMveHeader, @@ -79,6 +86,13 @@ enum ActionType { GenArmSveBuiltinCG, GenArmSveTypeFlags, GenArmSveRangeChecks, + GenArmSveStreamingAttrs, + GenArmSmeHeader, + GenArmSmeBuiltins, + GenArmSmeBuiltinCG, + GenArmSmeRangeChecks, + GenArmSmeStreamingAttrs, + GenArmSmeBuiltinZAState, GenArmCdeHeader, GenArmCdeBuiltinDef, GenArmCdeBuiltinSema, @@ -87,6 +101,10 @@ enum ActionType { GenRISCVVectorHeader, GenRISCVVectorBuiltins, GenRISCVVectorBuiltinCG, + GenRISCVVectorBuiltinSema, + GenRISCVSiFiveVectorBuiltins, + GenRISCVSiFiveVectorBuiltinCG, + GenRISCVSiFiveVectorBuiltinSema, GenAttrDocs, GenDiagDocs, GenOptDocs, @@ -115,6 +133,16 @@ cl::opt<ActionType> Action( "Generate clang attribute implementations"), clEnumValN(GenClangAttrList, "gen-clang-attr-list", "Generate a clang attribute list"), + clEnumValN(GenClangAttrCanPrintLeftList, + "gen-clang-attr-can-print-left-list", + "Generate list of attributes that can be printed on left " + "side of a decl"), + clEnumValN(GenClangAttrMustPrintLeftList, + "gen-clang-attr-must-print-left-list", + "Generate list of attributes that must be printed on left " + "side of a decl"), + clEnumValN(GenClangAttrDocTable, "gen-clang-attr-doc-table", + "Generate a table of attribute documentation"), clEnumValN(GenClangAttrSubjectMatchRuleList, "gen-clang-attr-subject-match-rule-list", "Generate a clang attribute subject match rule list"), @@ -122,6 +150,10 @@ cl::opt<ActionType> Action( "Generate clang PCH attribute reader"), clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write", "Generate clang PCH attribute writer"), + clEnumValN(GenClangRegularKeywordAttributeInfo, + "gen-clang-regular-keyword-attr-info", + "Generate a list of regular keyword attributes with info " + "about their arguments"), clEnumValN(GenClangAttrHasAttributeImpl, "gen-clang-attr-has-attribute-impl", "Generate a clang attribute spelling list"), @@ -195,11 +227,16 @@ cl::opt<ActionType> Action( "documentation comments"), clEnumValN(GenClangOpenCLBuiltins, "gen-clang-opencl-builtins", "Generate OpenCL builtin declaration handlers"), + clEnumValN(GenClangOpenCLBuiltinHeader, + "gen-clang-opencl-builtin-header", + "Generate OpenCL builtin header"), clEnumValN(GenClangOpenCLBuiltinTests, "gen-clang-opencl-builtin-tests", "Generate OpenCL builtin declaration tests"), 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(GenArmVectorType, "gen-arm-vector-type", + "Generate arm_vector_types.h for clang"), clEnumValN(GenArmNeonSema, "gen-arm-neon-sema", "Generate ARM NEON sema support for clang"), clEnumValN(GenArmNeonTest, "gen-arm-neon-test", @@ -214,6 +251,20 @@ cl::opt<ActionType> Action( "Generate arm_sve_typeflags.inc for clang"), clEnumValN(GenArmSveRangeChecks, "gen-arm-sve-sema-rangechecks", "Generate arm_sve_sema_rangechecks.inc for clang"), + clEnumValN(GenArmSveStreamingAttrs, "gen-arm-sve-streaming-attrs", + "Generate arm_sve_streaming_attrs.inc for clang"), + clEnumValN(GenArmSmeHeader, "gen-arm-sme-header", + "Generate arm_sme.h for clang"), + clEnumValN(GenArmSmeBuiltins, "gen-arm-sme-builtins", + "Generate arm_sme_builtins.inc for clang"), + clEnumValN(GenArmSmeBuiltinCG, "gen-arm-sme-builtin-codegen", + "Generate arm_sme_builtin_cg_map.inc for clang"), + clEnumValN(GenArmSmeRangeChecks, "gen-arm-sme-sema-rangechecks", + "Generate arm_sme_sema_rangechecks.inc for clang"), + clEnumValN(GenArmSmeStreamingAttrs, "gen-arm-sme-streaming-attrs", + "Generate arm_sme_streaming_attrs.inc for clang"), + clEnumValN(GenArmSmeBuiltinZAState, "gen-arm-sme-builtin-za-state", + "Generate arm_sme_builtins_za_state.inc for clang"), clEnumValN(GenArmMveHeader, "gen-arm-mve-header", "Generate arm_mve.h for clang"), clEnumValN(GenArmMveBuiltinDef, "gen-arm-mve-builtin-def", @@ -240,6 +291,17 @@ cl::opt<ActionType> Action( "Generate riscv_vector_builtins.inc for clang"), clEnumValN(GenRISCVVectorBuiltinCG, "gen-riscv-vector-builtin-codegen", "Generate riscv_vector_builtin_cg.inc for clang"), + clEnumValN(GenRISCVVectorBuiltinSema, "gen-riscv-vector-builtin-sema", + "Generate riscv_vector_builtin_sema.inc for clang"), + clEnumValN(GenRISCVSiFiveVectorBuiltins, + "gen-riscv-sifive-vector-builtins", + "Generate riscv_sifive_vector_builtins.inc for clang"), + clEnumValN(GenRISCVSiFiveVectorBuiltinCG, + "gen-riscv-sifive-vector-builtin-codegen", + "Generate riscv_sifive_vector_builtin_cg.inc for clang"), + clEnumValN(GenRISCVSiFiveVectorBuiltinSema, + "gen-riscv-sifive-vector-builtin-sema", + "Generate riscv_sifive_vector_builtin_sema.inc for clang"), clEnumValN(GenAttrDocs, "gen-attr-docs", "Generate attribute documentation"), clEnumValN(GenDiagDocs, "gen-diag-docs", @@ -280,6 +342,15 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangAttrList: EmitClangAttrList(Records, OS); break; + case GenClangAttrCanPrintLeftList: + EmitClangAttrPrintList("CanPrintOnLeft", Records, OS); + break; + case GenClangAttrMustPrintLeftList: + EmitClangAttrPrintList("PrintOnLeft", Records, OS); + break; + case GenClangAttrDocTable: + EmitClangAttrDocTable(Records, OS); + break; case GenClangAttrSubjectMatchRuleList: EmitClangAttrSubjectMatchRuleList(Records, OS); break; @@ -289,6 +360,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangAttrPCHWrite: EmitClangAttrPCHWrite(Records, OS); break; + case GenClangRegularKeywordAttributeInfo: + EmitClangRegularKeywordAttributeInfo(Records, OS); + break; case GenClangAttrHasAttributeImpl: EmitClangAttrHasAttrImpl(Records, OS); break; @@ -329,7 +403,8 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { EmitClangASTNodes(Records, OS, CommentNodeClassName, ""); break; case GenClangDeclNodes: - EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl"); + EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl", + DeclContextNodeClassName); EmitClangDeclContext(Records, OS); break; case GenClangStmtNodes: @@ -374,6 +449,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangOpenCLBuiltins: EmitClangOpenCLBuiltins(Records, OS); break; + case GenClangOpenCLBuiltinHeader: + EmitClangOpenCLBuiltinHeader(Records, OS); + break; case GenClangOpenCLBuiltinTests: EmitClangOpenCLBuiltinTests(Records, OS); break; @@ -389,6 +467,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenArmFP16: EmitFP16(Records, OS); break; + case GenArmVectorType: + EmitVectorTypes(Records, OS); + break; case GenArmBF16: EmitBF16(Records, OS); break; @@ -428,6 +509,27 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenArmSveRangeChecks: EmitSveRangeChecks(Records, OS); break; + case GenArmSveStreamingAttrs: + EmitSveStreamingAttrs(Records, OS); + break; + case GenArmSmeHeader: + EmitSmeHeader(Records, OS); + break; + case GenArmSmeBuiltins: + EmitSmeBuiltins(Records, OS); + break; + case GenArmSmeBuiltinCG: + EmitSmeBuiltinCG(Records, OS); + break; + case GenArmSmeRangeChecks: + EmitSmeRangeChecks(Records, OS); + break; + case GenArmSmeStreamingAttrs: + EmitSmeStreamingAttrs(Records, OS); + break; + case GenArmSmeBuiltinZAState: + EmitSmeBuiltinZAState(Records, OS); + break; case GenArmCdeHeader: EmitCdeHeader(Records, OS); break; @@ -452,6 +554,18 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenRISCVVectorBuiltinCG: EmitRVVBuiltinCG(Records, OS); break; + case GenRISCVVectorBuiltinSema: + EmitRVVBuiltinSema(Records, OS); + break; + case GenRISCVSiFiveVectorBuiltins: + EmitRVVBuiltins(Records, OS); + break; + case GenRISCVSiFiveVectorBuiltinCG: + EmitRVVBuiltinCG(Records, OS); + break; + case GenRISCVSiFiveVectorBuiltinSema: + EmitRVVBuiltinSema(Records, OS); + break; case GenAttrDocs: EmitClangAttrDocs(Records, OS); break; diff --git a/contrib/llvm-project/clang/utils/TableGen/TableGenBackends.h b/contrib/llvm-project/clang/utils/TableGen/TableGenBackends.h index bf40c7b1d18f..58a4af3c23a6 100644 --- a/contrib/llvm-project/clang/utils/TableGen/TableGenBackends.h +++ b/contrib/llvm-project/clang/utils/TableGen/TableGenBackends.h @@ -25,8 +25,16 @@ class RecordKeeper; namespace clang { void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS); +/** + @param PriorizeIfSubclassOf These classes should be prioritized in the output. + This is useful to force enum generation/jump tables/lookup tables to be more + compact in both size and surrounding code in hot functions. An example use is + in Decl for classes that inherit from DeclContext, for functions like + castFromDeclContext. + */ void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS, - const std::string &N, const std::string &S); + const std::string &N, const std::string &S, + std::string_view PriorizeIfSubclassOf = ""); void EmitClangBasicReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangBasicWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); @@ -39,10 +47,14 @@ void EmitClangAttrSubjectMatchRulesParserStringSwitches( void EmitClangAttrClass(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrImpl(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrList(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangAttrPrintList(const std::string &FieldName, + llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrSubjectMatchRuleList(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrPCHRead(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrPCHWrite(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangRegularKeywordAttributeInfo(llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); void EmitClangAttrHasAttrImpl(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrSpellingListIndex(llvm::RecordKeeper &Records, @@ -61,6 +73,7 @@ void EmitClangAttrTextNodeDump(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrNodeTraverse(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangAttrDocTable(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangDiagsDefs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS, const std::string &Component); @@ -92,6 +105,7 @@ 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 EmitVectorTypes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitNeonTest(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitSveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); @@ -99,6 +113,14 @@ 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 EmitSveStreamingAttrs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); + +void EmitSmeHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSmeBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSmeBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSmeRangeChecks(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSmeStreamingAttrs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitSmeBuiltinZAState(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); @@ -109,6 +131,7 @@ void EmitMveBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitRVVBuiltinSema(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); @@ -122,6 +145,8 @@ void EmitClangOptDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangOpenCLBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangOpenCLBuiltinHeader(llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); void EmitClangOpenCLBuiltinTests(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); |