diff options
Diffstat (limited to 'contrib/llvm/tools/clang/utils')
10 files changed, 7438 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangASTNodesEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangASTNodesEmitter.cpp new file mode 100644 index 0000000..b17a4a3 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangASTNodesEmitter.cpp @@ -0,0 +1,229 @@ +//=== ClangASTNodesEmitter.cpp - Generate Clang AST node tables -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These tablegen backends emit Clang AST node tables +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <cctype> +#include <map> +#include <set> +#include <string> +using namespace llvm; + +/// ClangASTNodesEmitter - The top-level class emits .inc files containing +/// declarations of Clang statements. +/// +namespace { +class ClangASTNodesEmitter { + // A map from a node to each of its derived nodes. + typedef std::multimap<Record*, Record*> ChildMap; + typedef ChildMap::const_iterator ChildIterator; + + RecordKeeper &Records; + Record Root; + const std::string &BaseSuffix; + + // Create a macro-ized version of a name + static std::string macroName(std::string S) { + for (unsigned i = 0; i < S.size(); ++i) + S[i] = std::toupper(S[i]); + + return S; + } + + // Return the name to be printed in the base field. Normally this is + // the record's name plus the base suffix, but if it is the root node and + // the suffix is non-empty, it's just the suffix. + std::string baseName(Record &R) { + if (&R == &Root && !BaseSuffix.empty()) + return BaseSuffix; + + return R.getName() + BaseSuffix; + } + + std::pair<Record *, Record *> EmitNode (const ChildMap &Tree, raw_ostream& OS, + Record *Base); +public: + explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N, + const std::string &S) + : Records(R), Root(N, SMLoc(), R), BaseSuffix(S) + {} + + // run - Output the .inc file contents + void run(raw_ostream &OS); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Statement Node Tables (.inc file) generation. +//===----------------------------------------------------------------------===// + +// Returns the first and last non-abstract subrecords +// Called recursively to ensure that nodes remain contiguous +std::pair<Record *, Record *> ClangASTNodesEmitter::EmitNode( + const ChildMap &Tree, + raw_ostream &OS, + Record *Base) { + std::string BaseName = macroName(Base->getName()); + + ChildIterator i = Tree.lower_bound(Base), e = Tree.upper_bound(Base); + + Record *First = nullptr, *Last = nullptr; + // This might be the pseudo-node for Stmt; don't assume it has an Abstract + // bit + if (Base->getValue("Abstract") && !Base->getValueAsBit("Abstract")) + First = Last = Base; + + for (; i != e; ++i) { + Record *R = i->second; + bool Abstract = R->getValueAsBit("Abstract"); + std::string NodeName = macroName(R->getName()); + + OS << "#ifndef " << NodeName << "\n"; + OS << "# define " << NodeName << "(Type, Base) " + << BaseName << "(Type, Base)\n"; + OS << "#endif\n"; + + if (Abstract) + OS << "ABSTRACT_" << macroName(Root.getName()) << "(" << NodeName << "(" + << R->getName() << ", " << baseName(*Base) << "))\n"; + else + OS << NodeName << "(" << R->getName() << ", " + << baseName(*Base) << ")\n"; + + if (Tree.find(R) != Tree.end()) { + const std::pair<Record *, Record *> &Result + = EmitNode(Tree, OS, R); + if (!First && Result.first) + First = Result.first; + if (Result.second) + Last = Result.second; + } else { + if (!Abstract) { + Last = R; + + if (!First) + First = R; + } + } + + OS << "#undef " << NodeName << "\n\n"; + } + + if (First) { + assert (Last && "Got a first node but not a last node for a range!"); + if (Base == &Root) + OS << "LAST_" << macroName(Root.getName()) << "_RANGE("; + else + OS << macroName(Root.getName()) << "_RANGE("; + OS << Base->getName() << ", " << First->getName() << ", " + << Last->getName() << ")\n\n"; + } + + return std::make_pair(First, Last); +} + +void ClangASTNodesEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("List of AST nodes of a particular kind", OS); + + // Write the preamble + OS << "#ifndef ABSTRACT_" << macroName(Root.getName()) << "\n"; + OS << "# define ABSTRACT_" << macroName(Root.getName()) << "(Type) Type\n"; + OS << "#endif\n"; + + OS << "#ifndef " << macroName(Root.getName()) << "_RANGE\n"; + OS << "# define " + << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef LAST_" << macroName(Root.getName()) << "_RANGE\n"; + OS << "# define LAST_" + << macroName(Root.getName()) << "_RANGE(Base, First, Last) " + << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n"; + OS << "#endif\n\n"; + + // Emit statements + const std::vector<Record*> Stmts + = Records.getAllDerivedDefinitions(Root.getName()); + + ChildMap Tree; + + for (unsigned i = 0, e = Stmts.size(); i != e; ++i) { + Record *R = Stmts[i]; + + if (R->getValue("Base")) + Tree.insert(std::make_pair(R->getValueAsDef("Base"), R)); + else + Tree.insert(std::make_pair(&Root, R)); + } + + EmitNode(Tree, OS, &Root); + + OS << "#undef " << macroName(Root.getName()) << "\n"; + OS << "#undef " << macroName(Root.getName()) << "_RANGE\n"; + OS << "#undef LAST_" << macroName(Root.getName()) << "_RANGE\n"; + OS << "#undef ABSTRACT_" << macroName(Root.getName()) << "\n"; +} + +namespace clang { +void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, + const std::string &N, const std::string &S) { + ClangASTNodesEmitter(RK, N, S).run(OS); +} + +// Emits and addendum to a .inc file to enumerate the clang declaration +// contexts. +void 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); + + 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("DeclContext"); + RecordVector Decls = Records.getAllDerivedDefinitions("Decl"); + RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end()); + + for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) { + Record *R = *i; + + if (R->getValue("Base")) { + Record *B = R->getValueAsDef("Base"); + if (DeclContexts.find(B) != DeclContexts.end()) { + OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n"; + DeclContexts.erase(B); + } + } + } + + // 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"; + + OS << "#undef DECL_CONTEXT\n"; + OS << "#undef DECL_CONTEXT_BASE\n"; +} +} // end namespace clang diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangAttrEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangAttrEmitter.cpp new file mode 100644 index 0000000..5dc33a0 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -0,0 +1,2976 @@ +//===- ClangAttrEmitter.cpp - Generate Clang attribute handling =-*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These tablegen backends emit Clang attribute processing code +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <algorithm> +#include <cctype> +#include <memory> +#include <set> +#include <sstream> + +using namespace llvm; + +namespace { +class FlattenedSpelling { + std::string V, N, NS; + bool K; + +public: + FlattenedSpelling(const std::string &Variety, const std::string &Name, + const std::string &Namespace, bool KnownToGCC) : + V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {} + explicit FlattenedSpelling(const Record &Spelling) : + V(Spelling.getValueAsString("Variety")), + N(Spelling.getValueAsString("Name")) { + + assert(V != "GCC" && "Given a GCC spelling, which means this hasn't been" + "flattened!"); + if (V == "CXX11" || V == "Pragma") + NS = Spelling.getValueAsString("Namespace"); + bool Unset; + K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset); + } + + const std::string &variety() const { return V; } + const std::string &name() const { return N; } + const std::string &nameSpace() const { return NS; } + bool knownToGCC() const { return K; } +}; +} // namespace + +static std::vector<FlattenedSpelling> +GetFlattenedSpellings(const Record &Attr) { + std::vector<Record *> Spellings = Attr.getValueAsListOfDefs("Spellings"); + std::vector<FlattenedSpelling> Ret; + + for (const auto &Spelling : Spellings) { + if (Spelling->getValueAsString("Variety") == "GCC") { + // Gin up two new spelling objects to add into the list. + Ret.emplace_back("GNU", Spelling->getValueAsString("Name"), "", true); + Ret.emplace_back("CXX11", Spelling->getValueAsString("Name"), "gnu", + true); + } else + Ret.push_back(FlattenedSpelling(*Spelling)); + } + + return Ret; +} + +static std::string ReadPCHRecord(StringRef type) { + return StringSwitch<std::string>(type) + .EndsWith("Decl *", "GetLocalDeclAs<" + + std::string(type, 0, type.size()-1) + ">(F, Record[Idx++])") + .Case("TypeSourceInfo *", "GetTypeSourceInfo(F, Record, Idx)") + .Case("Expr *", "ReadExpr(F)") + .Case("IdentifierInfo *", "GetIdentifierInfo(F, Record, Idx)") + .Case("std::string", "ReadString(Record, Idx)") + .Default("Record[Idx++]"); +} + +// Assumes that the way to get the value is SA->getname() +static std::string WritePCHRecord(StringRef type, StringRef name) { + return StringSwitch<std::string>(type) + .EndsWith("Decl *", "AddDeclRef(" + std::string(name) + + ", Record);\n") + .Case("TypeSourceInfo *", + "AddTypeSourceInfo(" + std::string(name) + ", Record);\n") + .Case("Expr *", "AddStmt(" + std::string(name) + ");\n") + .Case("IdentifierInfo *", + "AddIdentifierRef(" + std::string(name) + ", Record);\n") + .Case("std::string", "AddString(" + std::string(name) + ", Record);\n") + .Default("Record.push_back(" + std::string(name) + ");\n"); +} + +// Normalize attribute name by removing leading and trailing +// underscores. For example, __foo, foo__, __foo__ would +// become foo. +static StringRef NormalizeAttrName(StringRef AttrName) { + if (AttrName.startswith("__")) + AttrName = AttrName.substr(2, AttrName.size()); + + if (AttrName.endswith("__")) + AttrName = AttrName.substr(0, AttrName.size() - 2); + + return AttrName; +} + +// Normalize the name by removing any and all leading and trailing underscores. +// This is different from NormalizeAttrName in that it also handles names like +// _pascal and __pascal. +static StringRef NormalizeNameForSpellingComparison(StringRef Name) { + return Name.trim("_"); +} + +// Normalize attribute spelling only if the spelling has both leading +// and trailing underscores. For example, __ms_struct__ will be +// normalized to "ms_struct"; __cdecl will remain intact. +static StringRef NormalizeAttrSpelling(StringRef AttrSpelling) { + if (AttrSpelling.startswith("__") && AttrSpelling.endswith("__")) { + AttrSpelling = AttrSpelling.substr(2, AttrSpelling.size() - 4); + } + + return AttrSpelling; +} + +typedef std::vector<std::pair<std::string, const Record *>> ParsedAttrMap; + +static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records, + ParsedAttrMap *Dupes = nullptr) { + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + std::set<std::string> Seen; + ParsedAttrMap R; + for (const auto *Attr : Attrs) { + if (Attr->getValueAsBit("SemaHandler")) { + std::string AN; + if (Attr->isSubClassOf("TargetSpecificAttr") && + !Attr->isValueUnset("ParseKind")) { + AN = Attr->getValueAsString("ParseKind"); + + // If this attribute has already been handled, it does not need to be + // handled again. + if (Seen.find(AN) != Seen.end()) { + if (Dupes) + Dupes->push_back(std::make_pair(AN, Attr)); + continue; + } + Seen.insert(AN); + } else + AN = NormalizeAttrName(Attr->getName()).str(); + + R.push_back(std::make_pair(AN, Attr)); + } + } + return R; +} + +namespace { + class Argument { + std::string lowerName, upperName; + StringRef attrName; + bool isOpt; + + public: + Argument(const Record &Arg, StringRef Attr) + : lowerName(Arg.getValueAsString("Name")), upperName(lowerName), + attrName(Attr), isOpt(false) { + if (!lowerName.empty()) { + lowerName[0] = std::tolower(lowerName[0]); + upperName[0] = std::toupper(upperName[0]); + } + } + virtual ~Argument() {} + + StringRef getLowerName() const { return lowerName; } + StringRef getUpperName() const { return upperName; } + StringRef getAttrName() const { return attrName; } + + bool isOptional() const { return isOpt; } + void setOptional(bool set) { isOpt = set; } + + // These functions print the argument contents formatted in different ways. + virtual void writeAccessors(raw_ostream &OS) const = 0; + virtual void writeAccessorDefinitions(raw_ostream &OS) const {} + virtual void writeASTVisitorTraversal(raw_ostream &OS) const {} + virtual void writeCloneArgs(raw_ostream &OS) const = 0; + virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0; + virtual void writeTemplateInstantiation(raw_ostream &OS) const {} + virtual void writeCtorBody(raw_ostream &OS) const {} + virtual void writeCtorInitializers(raw_ostream &OS) const = 0; + virtual void writeCtorDefaultInitializers(raw_ostream &OS) const = 0; + virtual void writeCtorParameters(raw_ostream &OS) const = 0; + virtual void writeDeclarations(raw_ostream &OS) const = 0; + virtual void writePCHReadArgs(raw_ostream &OS) const = 0; + virtual void writePCHReadDecls(raw_ostream &OS) const = 0; + virtual void writePCHWrite(raw_ostream &OS) const = 0; + virtual void writeValue(raw_ostream &OS) const = 0; + virtual void writeDump(raw_ostream &OS) const = 0; + virtual void writeDumpChildren(raw_ostream &OS) const {} + virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; } + + virtual bool isEnumArg() const { return false; } + virtual bool isVariadicEnumArg() const { return false; } + virtual bool isVariadic() const { return false; } + + virtual void writeImplicitCtorArgs(raw_ostream &OS) const { + OS << getUpperName(); + } + }; + + class SimpleArgument : public Argument { + std::string type; + + public: + SimpleArgument(const Record &Arg, StringRef Attr, std::string T) + : Argument(Arg, Attr), type(T) + {} + + std::string getType() const { return type; } + + void writeAccessors(raw_ostream &OS) const override { + OS << " " << type << " get" << getUpperName() << "() const {\n"; + OS << " return " << getLowerName() << ";\n"; + OS << " }"; + } + void writeCloneArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "A->get" << getUpperName() << "()"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "(" << getUpperName() << ")"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "()"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << type << " " << getUpperName(); + } + void writeDeclarations(raw_ostream &OS) const override { + OS << type << " " << getLowerName() << ";"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + std::string read = ReadPCHRecord(type); + OS << " " << type << " " << getLowerName() << " = " << read << ";\n"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " " << WritePCHRecord(type, "SA->get" + + std::string(getUpperName()) + "()"); + } + void writeValue(raw_ostream &OS) const override { + if (type == "FunctionDecl *") { + OS << "\" << get" << getUpperName() + << "()->getNameInfo().getAsString() << \""; + } else if (type == "IdentifierInfo *") { + OS << "\" << get" << getUpperName() << "()->getName() << \""; + } else if (type == "TypeSourceInfo *") { + OS << "\" << get" << getUpperName() << "().getAsString() << \""; + } else { + OS << "\" << get" << getUpperName() << "() << \""; + } + } + void writeDump(raw_ostream &OS) const override { + if (type == "FunctionDecl *") { + OS << " OS << \" \";\n"; + OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n"; + } else if (type == "IdentifierInfo *") { + OS << " OS << \" \" << SA->get" << getUpperName() + << "()->getName();\n"; + } else if (type == "TypeSourceInfo *") { + OS << " OS << \" \" << SA->get" << getUpperName() + << "().getAsString();\n"; + } else if (type == "bool") { + OS << " if (SA->get" << getUpperName() << "()) OS << \" " + << getUpperName() << "\";\n"; + } else if (type == "int" || type == "unsigned") { + OS << " OS << \" \" << SA->get" << getUpperName() << "();\n"; + } else { + llvm_unreachable("Unknown SimpleArgument type!"); + } + } + }; + + class DefaultSimpleArgument : public SimpleArgument { + int64_t Default; + + public: + DefaultSimpleArgument(const Record &Arg, StringRef Attr, + std::string T, int64_t Default) + : SimpleArgument(Arg, Attr, T), Default(Default) {} + + void writeAccessors(raw_ostream &OS) const override { + SimpleArgument::writeAccessors(OS); + + OS << "\n\n static const " << getType() << " Default" << getUpperName() + << " = " << Default << ";"; + } + }; + + class StringArgument : public Argument { + public: + StringArgument(const Record &Arg, StringRef Attr) + : Argument(Arg, Attr) + {} + + void writeAccessors(raw_ostream &OS) const override { + OS << " llvm::StringRef get" << getUpperName() << "() const {\n"; + OS << " return llvm::StringRef(" << getLowerName() << ", " + << getLowerName() << "Length);\n"; + OS << " }\n"; + OS << " unsigned get" << getUpperName() << "Length() const {\n"; + OS << " return " << getLowerName() << "Length;\n"; + OS << " }\n"; + OS << " void set" << getUpperName() + << "(ASTContext &C, llvm::StringRef S) {\n"; + OS << " " << getLowerName() << "Length = S.size();\n"; + OS << " this->" << getLowerName() << " = new (C, 1) char [" + << getLowerName() << "Length];\n"; + OS << " std::memcpy(this->" << getLowerName() << ", S.data(), " + << getLowerName() << "Length);\n"; + OS << " }"; + } + void writeCloneArgs(raw_ostream &OS) const override { + OS << "get" << getUpperName() << "()"; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "A->get" << getUpperName() << "()"; + } + void writeCtorBody(raw_ostream &OS) const override { + OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() + << ".data(), " << getLowerName() << "Length);"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "Length(" << getUpperName() << ".size())," + << getLowerName() << "(new (Ctx, 1) char[" << getLowerName() + << "Length])"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "Length(0)," << getLowerName() << "(0)"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << "llvm::StringRef " << getUpperName(); + } + void writeDeclarations(raw_ostream &OS) const override { + OS << "unsigned " << getLowerName() << "Length;\n"; + OS << "char *" << getLowerName() << ";"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " std::string " << getLowerName() + << "= ReadString(Record, Idx);\n"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddString(SA->get" << getUpperName() << "(), Record);\n"; + } + void writeValue(raw_ostream &OS) const override { + OS << "\\\"\" << get" << getUpperName() << "() << \"\\\""; + } + void writeDump(raw_ostream &OS) const override { + OS << " OS << \" \\\"\" << SA->get" << getUpperName() + << "() << \"\\\"\";\n"; + } + }; + + class AlignedArgument : public Argument { + public: + AlignedArgument(const Record &Arg, StringRef Attr) + : Argument(Arg, Attr) + {} + + void writeAccessors(raw_ostream &OS) const override { + OS << " bool is" << getUpperName() << "Dependent() const;\n"; + + OS << " unsigned get" << getUpperName() << "(ASTContext &Ctx) const;\n"; + + OS << " bool is" << getUpperName() << "Expr() const {\n"; + OS << " return is" << getLowerName() << "Expr;\n"; + OS << " }\n"; + + OS << " Expr *get" << getUpperName() << "Expr() const {\n"; + OS << " assert(is" << getLowerName() << "Expr);\n"; + OS << " return " << getLowerName() << "Expr;\n"; + OS << " }\n"; + + OS << " TypeSourceInfo *get" << getUpperName() << "Type() const {\n"; + OS << " assert(!is" << getLowerName() << "Expr);\n"; + OS << " return " << getLowerName() << "Type;\n"; + OS << " }"; + } + void writeAccessorDefinitions(raw_ostream &OS) const override { + OS << "bool " << getAttrName() << "Attr::is" << getUpperName() + << "Dependent() const {\n"; + OS << " if (is" << getLowerName() << "Expr)\n"; + OS << " return " << getLowerName() << "Expr && (" << getLowerName() + << "Expr->isValueDependent() || " << getLowerName() + << "Expr->isTypeDependent());\n"; + OS << " else\n"; + OS << " return " << getLowerName() + << "Type->getType()->isDependentType();\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 writeCloneArgs(raw_ostream &OS) const override { + OS << "is" << getLowerName() << "Expr, is" << getLowerName() + << "Expr ? static_cast<void*>(" << getLowerName() + << "Expr) : " << getLowerName() + << "Type"; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + // FIXME: move the definition in Sema::InstantiateAttrs to here. + // In the meantime, aligned attributes are cloned. + } + void writeCtorBody(raw_ostream &OS) const override { + OS << " if (is" << getLowerName() << "Expr)\n"; + OS << " " << getLowerName() << "Expr = reinterpret_cast<Expr *>(" + << getUpperName() << ");\n"; + OS << " else\n"; + OS << " " << getLowerName() + << "Type = reinterpret_cast<TypeSourceInfo *>(" << getUpperName() + << ");"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << "is" << getLowerName() << "Expr(Is" << getUpperName() << "Expr)"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << "is" << getLowerName() << "Expr(false)"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << "bool Is" << getUpperName() << "Expr, void *" << getUpperName(); + } + void writeImplicitCtorArgs(raw_ostream &OS) const override { + OS << "Is" << getUpperName() << "Expr, " << getUpperName(); + } + void writeDeclarations(raw_ostream &OS) const override { + OS << "bool is" << getLowerName() << "Expr;\n"; + OS << "union {\n"; + OS << "Expr *" << getLowerName() << "Expr;\n"; + OS << "TypeSourceInfo *" << getLowerName() << "Type;\n"; + OS << "};"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << "is" << getLowerName() << "Expr, " << getLowerName() << "Ptr"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " bool is" << getLowerName() << "Expr = Record[Idx++];\n"; + OS << " void *" << getLowerName() << "Ptr;\n"; + OS << " if (is" << getLowerName() << "Expr)\n"; + OS << " " << getLowerName() << "Ptr = ReadExpr(F);\n"; + OS << " else\n"; + OS << " " << getLowerName() + << "Ptr = GetTypeSourceInfo(F, Record, Idx);\n"; + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " Record.push_back(SA->is" << getUpperName() << "Expr());\n"; + OS << " if (SA->is" << getUpperName() << "Expr())\n"; + OS << " AddStmt(SA->get" << getUpperName() << "Expr());\n"; + OS << " else\n"; + OS << " AddTypeSourceInfo(SA->get" << getUpperName() + << "Type(), Record);\n"; + } + void writeValue(raw_ostream &OS) const override { + OS << "\";\n"; + // The aligned attribute argument expression is optional. + OS << " if (is" << getLowerName() << "Expr && " + << getLowerName() << "Expr)\n"; + OS << " " << getLowerName() << "Expr->printPretty(OS, 0, Policy);\n"; + OS << " OS << \""; + } + void writeDump(raw_ostream &OS) const override { + } + void writeDumpChildren(raw_ostream &OS) const override { + OS << " if (SA->is" << getUpperName() << "Expr())\n"; + OS << " dumpStmt(SA->get" << getUpperName() << "Expr());\n"; + OS << " else\n"; + OS << " dumpType(SA->get" << getUpperName() + << "Type()->getType());\n"; + } + void writeHasChildren(raw_ostream &OS) const override { + OS << "SA->is" << getUpperName() << "Expr()"; + } + }; + + class VariadicArgument : public Argument { + std::string Type, ArgName, ArgSizeName, RangeName; + + protected: + // Assumed to receive a parameter: raw_ostream OS. + virtual void writeValueImpl(raw_ostream &OS) const { + OS << " OS << Val;\n"; + } + + public: + VariadicArgument(const Record &Arg, StringRef Attr, std::string T) + : Argument(Arg, Attr), Type(T), ArgName(getLowerName().str() + "_"), + ArgSizeName(ArgName + "Size"), RangeName(getLowerName()) {} + + std::string getType() const { return Type; } + bool isVariadic() const override { return true; } + + void writeAccessors(raw_ostream &OS) const override { + std::string IteratorType = getLowerName().str() + "_iterator"; + std::string BeginFn = getLowerName().str() + "_begin()"; + std::string EndFn = getLowerName().str() + "_end()"; + + OS << " typedef " << Type << "* " << IteratorType << ";\n"; + OS << " " << IteratorType << " " << BeginFn << " const {" + << " return " << ArgName << "; }\n"; + OS << " " << IteratorType << " " << EndFn << " const {" + << " return " << ArgName << " + " << ArgSizeName << "; }\n"; + OS << " unsigned " << getLowerName() << "_size() const {" + << " return " << ArgSizeName << "; }\n"; + OS << " llvm::iterator_range<" << IteratorType << "> " << RangeName + << "() const { return llvm::make_range(" << BeginFn << ", " << EndFn + << "); }\n"; + } + void writeCloneArgs(raw_ostream &OS) const override { + OS << ArgName << ", " << ArgSizeName; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + // This isn't elegant, but we have to go through public methods... + OS << "A->" << getLowerName() << "_begin(), " + << "A->" << getLowerName() << "_size()"; + } + void writeCtorBody(raw_ostream &OS) const override { + OS << " std::copy(" << getUpperName() << ", " << getUpperName() + << " + " << ArgSizeName << ", " << ArgName << ");"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << ArgSizeName << "(" << getUpperName() << "Size), " + << ArgName << "(new (Ctx, 16) " << getType() << "[" + << ArgSizeName << "])"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << ArgSizeName << "(0), " << ArgName << "(nullptr)"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << getType() << " *" << getUpperName() << ", unsigned " + << getUpperName() << "Size"; + } + void writeImplicitCtorArgs(raw_ostream &OS) const override { + OS << getUpperName() << ", " << getUpperName() << "Size"; + } + void writeDeclarations(raw_ostream &OS) const override { + OS << " unsigned " << ArgSizeName << ";\n"; + OS << " " << getType() << " *" << ArgName << ";"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n"; + OS << " SmallVector<" << Type << ", 4> " << getLowerName() + << ";\n"; + OS << " " << getLowerName() << ".reserve(" << getLowerName() + << "Size);\n"; + OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n"; + + std::string read = ReadPCHRecord(Type); + OS << " " << getLowerName() << ".push_back(" << read << ");\n"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << getLowerName() << ".data(), " << getLowerName() << "Size"; + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " Record.push_back(SA->" << getLowerName() << "_size());\n"; + OS << " for (auto &Val : SA->" << RangeName << "())\n"; + OS << " " << WritePCHRecord(Type, "Val"); + } + void writeValue(raw_ostream &OS) const override { + OS << "\";\n"; + OS << " bool isFirst = true;\n" + << " for (const auto &Val : " << RangeName << "()) {\n" + << " if (isFirst) isFirst = false;\n" + << " else OS << \", \";\n"; + writeValueImpl(OS); + OS << " }\n"; + OS << " OS << \""; + } + void writeDump(raw_ostream &OS) const override { + OS << " for (const auto &Val : SA->" << RangeName << "())\n"; + OS << " OS << \" \" << Val;\n"; + } + }; + + // Unique the enums, but maintain the original declaration ordering. + std::vector<std::string> + uniqueEnumsInOrder(const std::vector<std::string> &enums) { + std::vector<std::string> uniques; + std::set<std::string> unique_set(enums.begin(), enums.end()); + for (const auto &i : enums) { + std::set<std::string>::iterator set_i = unique_set.find(i); + if (set_i != unique_set.end()) { + uniques.push_back(i); + unique_set.erase(set_i); + } + } + return uniques; + } + + class EnumArgument : public Argument { + std::string type; + std::vector<std::string> values, enums, uniques; + public: + EnumArgument(const Record &Arg, StringRef Attr) + : Argument(Arg, Attr), type(Arg.getValueAsString("Type")), + values(Arg.getValueAsListOfStrings("Values")), + enums(Arg.getValueAsListOfStrings("Enums")), + uniques(uniqueEnumsInOrder(enums)) + { + // FIXME: Emit a proper error + assert(!uniques.empty()); + } + + bool isEnumArg() const override { return true; } + + void writeAccessors(raw_ostream &OS) const override { + OS << " " << type << " get" << getUpperName() << "() const {\n"; + OS << " return " << getLowerName() << ";\n"; + OS << " }"; + } + void writeCloneArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "A->get" << getUpperName() << "()"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "(" << getUpperName() << ")"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "(" << type << "(0))"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << type << " " << getUpperName(); + } + void writeDeclarations(raw_ostream &OS) const override { + std::vector<std::string>::const_iterator i = uniques.begin(), + e = uniques.end(); + // 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"; + OS << "private:\n"; + OS << " " << type << " " << getLowerName() << ";"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName() + << "(static_cast<" << getAttrName() << "Attr::" << type + << ">(Record[Idx++]));\n"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writePCHWrite(raw_ostream &OS) const override { + OS << "Record.push_back(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() << "()) << \"\\\""; + } + 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 << " OS << \" " << I << "\";\n"; + OS << " break;\n"; + } + OS << " }\n"; + } + + void writeConversion(raw_ostream &OS) const { + OS << " static bool ConvertStrTo" << type << "(StringRef Val, "; + OS << type << " &Out) {\n"; + OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; + OS << type << ">>(Val)\n"; + for (size_t I = 0; I < enums.size(); ++I) { + OS << " .Case(\"" << values[I] << "\", "; + OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + } + OS << " .Default(Optional<" << type << ">());\n"; + OS << " if (R) {\n"; + OS << " Out = *R;\n return true;\n }\n"; + OS << " return false;\n"; + OS << " }\n\n"; + + // Mapping from enumeration values back to enumeration strings isn't + // trivial because some enumeration values have multiple named + // enumerators, such as type_visibility(internal) and + // type_visibility(hidden) both mapping to TypeVisibilityAttr::Hidden. + OS << " static const char *Convert" << type << "ToStr(" + << type << " Val) {\n" + << " switch(Val) {\n"; + std::set<std::string> 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 << " }\n" + << " llvm_unreachable(\"No enumerator with that value\");\n" + << " }\n"; + } + }; + + class VariadicEnumArgument: public VariadicArgument { + std::string type, QualifiedTypeName; + std::vector<std::string> values, enums, uniques; + + 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"; + } + + public: + VariadicEnumArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")), + type(Arg.getValueAsString("Type")), + values(Arg.getValueAsListOfStrings("Values")), + enums(Arg.getValueAsListOfStrings("Enums")), + uniques(uniqueEnumsInOrder(enums)) + { + QualifiedTypeName = getAttrName().str() + "Attr::" + type; + + // FIXME: Emit a proper error + assert(!uniques.empty()); + } + + bool isVariadicEnumArg() const override { return true; } + + void writeDeclarations(raw_ostream &OS) const override { + std::vector<std::string>::const_iterator i = uniques.begin(), + e = uniques.end(); + // 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"; + OS << "private:\n"; + + VariadicArgument::writeDeclarations(OS); + } + 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 << " switch(*I) {\n"; + for (const auto &UI : uniques) { + OS << " case " << getAttrName() << "Attr::" << UI << ":\n"; + OS << " OS << \" " << UI << "\";\n"; + OS << " break;\n"; + } + OS << " }\n"; + OS << " }\n"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n"; + OS << " SmallVector<" << QualifiedTypeName << ", 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[Idx++]));\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 << " " << WritePCHRecord(QualifiedTypeName, "(*i)"); + } + void writeConversion(raw_ostream &OS) const { + OS << " static bool ConvertStrTo" << type << "(StringRef Val, "; + OS << type << " &Out) {\n"; + OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<"; + OS << type << ">>(Val)\n"; + for (size_t I = 0; I < enums.size(); ++I) { + OS << " .Case(\"" << values[I] << "\", "; + OS << getAttrName() << "Attr::" << enums[I] << ")\n"; + } + OS << " .Default(Optional<" << type << ">());\n"; + OS << " if (R) {\n"; + OS << " Out = *R;\n return true;\n }\n"; + OS << " return false;\n"; + OS << " }\n\n"; + + OS << " static const char *Convert" << type << "ToStr(" + << type << " Val) {\n" + << " switch(Val) {\n"; + std::set<std::string> 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 << " }\n" + << " llvm_unreachable(\"No enumerator with that value\");\n" + << " }\n"; + } + }; + + class VersionArgument : public Argument { + public: + VersionArgument(const Record &Arg, StringRef Attr) + : Argument(Arg, Attr) + {} + + void writeAccessors(raw_ostream &OS) const override { + OS << " VersionTuple get" << getUpperName() << "() const {\n"; + OS << " return " << getLowerName() << ";\n"; + OS << " }\n"; + OS << " void set" << getUpperName() + << "(ASTContext &C, VersionTuple V) {\n"; + OS << " " << getLowerName() << " = V;\n"; + OS << " }"; + } + void writeCloneArgs(raw_ostream &OS) const override { + OS << "get" << getUpperName() << "()"; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "A->get" << getUpperName() << "()"; + } + void writeCtorInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "(" << getUpperName() << ")"; + } + void writeCtorDefaultInitializers(raw_ostream &OS) const override { + OS << getLowerName() << "()"; + } + void writeCtorParameters(raw_ostream &OS) const override { + OS << "VersionTuple " << getUpperName(); + } + void writeDeclarations(raw_ostream &OS) const override { + OS << "VersionTuple " << getLowerName() << ";\n"; + } + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " VersionTuple " << getLowerName() + << "= ReadVersionTuple(Record, Idx);\n"; + } + void writePCHReadArgs(raw_ostream &OS) const override { + OS << getLowerName(); + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddVersionTuple(SA->get" << getUpperName() << "(), Record);\n"; + } + void writeValue(raw_ostream &OS) const override { + OS << getLowerName() << "=\" << get" << getUpperName() << "() << \""; + } + void writeDump(raw_ostream &OS) const override { + OS << " OS << \" \" << SA->get" << getUpperName() << "();\n"; + } + }; + + class ExprArgument : public SimpleArgument { + public: + ExprArgument(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Expr *") + {} + + void writeASTVisitorTraversal(raw_ostream &OS) const override { + OS << " if (!" + << "getDerived().TraverseStmt(A->get" << getUpperName() << "()))\n"; + OS << " return false;\n"; + } + + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "tempInst" << getUpperName(); + } + + void writeTemplateInstantiation(raw_ostream &OS) const override { + OS << " " << getType() << " tempInst" << getUpperName() << ";\n"; + OS << " {\n"; + OS << " EnterExpressionEvaluationContext " + << "Unevaluated(S, Sema::Unevaluated);\n"; + OS << " ExprResult " << "Result = S.SubstExpr(" + << "A->get" << getUpperName() << "(), TemplateArgs);\n"; + OS << " tempInst" << getUpperName() << " = " + << "Result.getAs<Expr>();\n"; + OS << " }\n"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " dumpStmt(SA->get" << getUpperName() << "());\n"; + } + void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + }; + + class VariadicExprArgument : public VariadicArgument { + public: + VariadicExprArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "Expr *") + {} + + void writeASTVisitorTraversal(raw_ostream &OS) const override { + OS << " {\n"; + OS << " " << getType() << " *I = A->" << getLowerName() + << "_begin();\n"; + OS << " " << getType() << " *E = A->" << getLowerName() + << "_end();\n"; + OS << " for (; I != E; ++I) {\n"; + OS << " if (!getDerived().TraverseStmt(*I))\n"; + OS << " return false;\n"; + OS << " }\n"; + OS << " }\n"; + } + + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "tempInst" << getUpperName() << ", " + << "A->" << getLowerName() << "_size()"; + } + + void writeTemplateInstantiation(raw_ostream &OS) const override { + OS << " " << getType() << " *tempInst" << getUpperName() + << " = new (C, 16) " << getType() + << "[A->" << getLowerName() << "_size()];\n"; + OS << " {\n"; + OS << " EnterExpressionEvaluationContext " + << "Unevaluated(S, Sema::Unevaluated);\n"; + OS << " " << getType() << " *TI = tempInst" << getUpperName() + << ";\n"; + OS << " " << getType() << " *I = A->" << getLowerName() + << "_begin();\n"; + OS << " " << getType() << " *E = A->" << getLowerName() + << "_end();\n"; + OS << " for (; I != E; ++I, ++TI) {\n"; + OS << " ExprResult Result = S.SubstExpr(*I, TemplateArgs);\n"; + OS << " *TI = Result.getAs<Expr>();\n"; + OS << " }\n"; + OS << " }\n"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " for (" << getAttrName() << "Attr::" << getLowerName() + << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->" + << getLowerName() << "_end(); I != E; ++I)\n"; + OS << " dumpStmt(*I);\n"; + } + + void writeHasChildren(raw_ostream &OS) const override { + OS << "SA->" << getLowerName() << "_begin() != " + << "SA->" << getLowerName() << "_end()"; + } + }; + + class VariadicStringArgument : public VariadicArgument { + public: + VariadicStringArgument(const Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "std::string") + {} + void writeValueImpl(raw_ostream &OS) const override { + OS << " OS << \"\\\"\" << Val << \"\\\"\";\n"; + } + }; + + class TypeArgument : public SimpleArgument { + public: + TypeArgument(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "TypeSourceInfo *") + {} + + void writeAccessors(raw_ostream &OS) const override { + OS << " QualType get" << getUpperName() << "() const {\n"; + OS << " return " << getLowerName() << "->getType();\n"; + OS << " }"; + OS << " " << getType() << " get" << getUpperName() << "Loc() const {\n"; + OS << " return " << getLowerName() << ";\n"; + OS << " }"; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { + OS << "A->get" << getUpperName() << "Loc()"; + } + void writePCHWrite(raw_ostream &OS) const override { + OS << " " << WritePCHRecord( + getType(), "SA->get" + std::string(getUpperName()) + "Loc()"); + } + }; +} + +static std::unique_ptr<Argument> +createArgument(const Record &Arg, StringRef Attr, + const Record *Search = nullptr) { + if (!Search) + Search = &Arg; + + std::unique_ptr<Argument> Ptr; + llvm::StringRef ArgName = Search->getName(); + + if (ArgName == "AlignedArgument") + Ptr = llvm::make_unique<AlignedArgument>(Arg, Attr); + else if (ArgName == "EnumArgument") + Ptr = llvm::make_unique<EnumArgument>(Arg, Attr); + else if (ArgName == "ExprArgument") + Ptr = llvm::make_unique<ExprArgument>(Arg, Attr); + else if (ArgName == "FunctionArgument") + Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "FunctionDecl *"); + else if (ArgName == "IdentifierArgument") + Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "IdentifierInfo *"); + else if (ArgName == "DefaultBoolArgument") + Ptr = llvm::make_unique<DefaultSimpleArgument>( + Arg, Attr, "bool", Arg.getValueAsBit("Default")); + else if (ArgName == "BoolArgument") + Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "bool"); + else if (ArgName == "DefaultIntArgument") + Ptr = llvm::make_unique<DefaultSimpleArgument>( + Arg, Attr, "int", Arg.getValueAsInt("Default")); + else if (ArgName == "IntArgument") + Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "int"); + else if (ArgName == "StringArgument") + Ptr = llvm::make_unique<StringArgument>(Arg, Attr); + else if (ArgName == "TypeArgument") + Ptr = llvm::make_unique<TypeArgument>(Arg, Attr); + else if (ArgName == "UnsignedArgument") + Ptr = llvm::make_unique<SimpleArgument>(Arg, Attr, "unsigned"); + else if (ArgName == "VariadicUnsignedArgument") + Ptr = llvm::make_unique<VariadicArgument>(Arg, Attr, "unsigned"); + else if (ArgName == "VariadicStringArgument") + Ptr = llvm::make_unique<VariadicStringArgument>(Arg, Attr); + else if (ArgName == "VariadicEnumArgument") + Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr); + else if (ArgName == "VariadicExprArgument") + Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr); + else if (ArgName == "VersionArgument") + Ptr = llvm::make_unique<VersionArgument>(Arg, Attr); + + if (!Ptr) { + // Search in reverse order so that the most-derived type is handled first. + ArrayRef<Record*> Bases = Search->getSuperClasses(); + for (const auto *Base : llvm::make_range(Bases.rbegin(), Bases.rend())) { + if ((Ptr = createArgument(Arg, Attr, Base))) + break; + } + } + + if (Ptr && Arg.getValueAsBit("Optional")) + Ptr->setOptional(true); + + return Ptr; +} + +static void writeAvailabilityValue(raw_ostream &OS) { + OS << "\" << getPlatform()->getName();\n" + << " if (!getIntroduced().empty()) OS << \", introduced=\" << getIntroduced();\n" + << " if (!getDeprecated().empty()) OS << \", deprecated=\" << getDeprecated();\n" + << " if (!getObsoleted().empty()) OS << \", obsoleted=\" << getObsoleted();\n" + << " if (getUnavailable()) OS << \", unavailable\";\n" + << " OS << \""; +} + +static void writeGetSpellingFunction(Record &R, raw_ostream &OS) { + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); + + OS << "const char *" << R.getName() << "Attr::getSpelling() const {\n"; + if (Spellings.empty()) { + OS << " return \"(No spelling)\";\n}\n\n"; + return; + } + + OS << " switch (SpellingListIndex) {\n" + " default:\n" + " llvm_unreachable(\"Unknown attribute spelling!\");\n" + " return \"(No spelling)\";\n"; + + for (unsigned I = 0; I < Spellings.size(); ++I) + OS << " case " << I << ":\n" + " return \"" << Spellings[I].name() << "\";\n"; + // End of the switch statement. + OS << " }\n"; + // End of the getSpelling function. + OS << "}\n\n"; +} + +static void +writePrettyPrintFunction(Record &R, + const std::vector<std::unique_ptr<Argument>> &Args, + raw_ostream &OS) { + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); + + OS << "void " << R.getName() << "Attr::printPretty(" + << "raw_ostream &OS, const PrintingPolicy &Policy) const {\n"; + + if (Spellings.empty()) { + OS << "}\n\n"; + return; + } + + OS << + " switch (SpellingListIndex) {\n" + " default:\n" + " llvm_unreachable(\"Unknown attribute spelling!\");\n" + " break;\n"; + + for (unsigned I = 0; I < Spellings.size(); ++ I) { + llvm::SmallString<16> Prefix; + llvm::SmallString<8> Suffix; + // The actual spelling of the name and namespace (if applicable) + // of an attribute without considering prefix and suffix. + llvm::SmallString<64> Spelling; + std::string Name = Spellings[I].name(); + std::string Variety = Spellings[I].variety(); + + if (Variety == "GNU") { + Prefix = " __attribute__(("; + Suffix = "))"; + } else if (Variety == "CXX11") { + Prefix = " [["; + Suffix = "]]"; + std::string Namespace = Spellings[I].nameSpace(); + if (!Namespace.empty()) { + Spelling += Namespace; + Spelling += "::"; + } + } else if (Variety == "Declspec") { + Prefix = " __declspec("; + Suffix = ")"; + } else if (Variety == "Keyword") { + Prefix = " "; + Suffix = ""; + } else if (Variety == "Pragma") { + Prefix = "#pragma "; + Suffix = "\n"; + std::string Namespace = Spellings[I].nameSpace(); + if (!Namespace.empty()) { + Spelling += Namespace; + Spelling += " "; + } + } else { + llvm_unreachable("Unknown attribute syntax variety!"); + } + + Spelling += Name; + + OS << + " case " << I << " : {\n" + " OS << \"" << Prefix << Spelling; + + if (Variety == "Pragma") { + OS << " \";\n"; + OS << " printPrettyPragma(OS, Policy);\n"; + OS << " break;\n"; + OS << " }\n"; + continue; + } + + // FIXME: always printing the parenthesis isn't the correct behavior for + // attributes which have optional arguments that were not provided. For + // instance: __attribute__((aligned)) will be pretty printed as + // __attribute__((aligned())). The logic should check whether there is only + // a single argument, and if it is optional, whether it has been provided. + if (!Args.empty()) + OS << "("; + if (Spelling == "availability") { + writeAvailabilityValue(OS); + } else { + for (auto I = Args.begin(), E = Args.end(); I != E; ++ I) { + if (I != Args.begin()) OS << ", "; + (*I)->writeValue(OS); + } + } + + if (!Args.empty()) + OS << ")"; + OS << Suffix + "\";\n"; + + OS << + " break;\n" + " }\n"; + } + + // End of the switch statement. + OS << "}\n"; + // End of the print function. + OS << "}\n\n"; +} + +/// \brief Return the index of a spelling in a spelling list. +static unsigned +getSpellingListIndex(const std::vector<FlattenedSpelling> &SpellingList, + const FlattenedSpelling &Spelling) { + assert(!SpellingList.empty() && "Spelling list is empty!"); + + for (unsigned Index = 0; Index < SpellingList.size(); ++Index) { + const FlattenedSpelling &S = SpellingList[Index]; + if (S.variety() != Spelling.variety()) + continue; + if (S.nameSpace() != Spelling.nameSpace()) + continue; + if (S.name() != Spelling.name()) + continue; + + return Index; + } + + llvm_unreachable("Unknown spelling!"); +} + +static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) { + std::vector<Record*> Accessors = R.getValueAsListOfDefs("Accessors"); + for (const auto *Accessor : Accessors) { + std::string Name = Accessor->getValueAsString("Name"); + std::vector<FlattenedSpelling> Spellings = + GetFlattenedSpellings(*Accessor); + std::vector<FlattenedSpelling> SpellingList = GetFlattenedSpellings(R); + assert(!SpellingList.empty() && + "Attribute with empty spelling list can't have accessors!"); + + OS << " bool " << Name << "() const { return SpellingListIndex == "; + for (unsigned Index = 0; Index < Spellings.size(); ++Index) { + OS << getSpellingListIndex(SpellingList, Spellings[Index]); + if (Index != Spellings.size() -1) + OS << " ||\n SpellingListIndex == "; + else + OS << "; }\n"; + } + } +} + +static bool +SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) { + assert(!Spellings.empty() && "An empty list of spellings was provided"); + std::string FirstName = NormalizeNameForSpellingComparison( + Spellings.front().name()); + for (const auto &Spelling : + llvm::make_range(std::next(Spellings.begin()), Spellings.end())) { + std::string Name = NormalizeNameForSpellingComparison(Spelling.name()); + if (Name != FirstName) + return false; + } + return true; +} + +typedef std::map<unsigned, std::string> SemanticSpellingMap; +static std::string +CreateSemanticSpellings(const std::vector<FlattenedSpelling> &Spellings, + SemanticSpellingMap &Map) { + // The enumerants are automatically generated based on the variety, + // namespace (if present) and name for each attribute spelling. However, + // care is taken to avoid trampling on the reserved namespace due to + // underscores. + std::string Ret(" enum Spelling {\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; + std::string Variety = S.variety(); + std::string Spelling = S.name(); + std::string Namespace = S.nameSpace(); + std::string EnumName = ""; + + EnumName += (Variety + "_"); + if (!Namespace.empty()) + EnumName += (NormalizeNameForSpellingComparison(Namespace).str() + + "_"); + EnumName += NormalizeNameForSpellingComparison(Spelling); + + // Even if the name is not unique, this spelling index corresponds to a + // particular enumerant name that we've calculated. + Map[Idx] = EnumName; + + // Since we have been stripping underscores to avoid trampling on the + // reserved namespace, we may have inadvertently created duplicate + // enumerant names. These duplicates are not considered part of the + // semantic spelling, and can be elided. + if (Uniques.find(EnumName) != Uniques.end()) + continue; + + Uniques.insert(EnumName); + if (I != Spellings.begin()) + Ret += ",\n"; + // Duplicate spellings are not considered part of the semantic spelling + // enumeration, but the spelling index and semantic spelling values are + // meant to be equivalent, so we must specify a concrete value for each + // enumerator. + Ret += " " + EnumName + " = " + llvm::utostr(Idx); + } + Ret += "\n };\n\n"; + return Ret; +} + +void WriteSemanticSpellingSwitch(const std::string &VarName, + const SemanticSpellingMap &Map, + raw_ostream &OS) { + OS << " switch (" << VarName << ") {\n default: " + << "llvm_unreachable(\"Unknown spelling list index\");\n"; + for (const auto &I : Map) + OS << " case " << I.first << ": return " << I.second << ";\n"; + OS << " }\n"; +} + +// Emits the LateParsed property for attributes. +static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + bool LateParsed = Attr->getValueAsBit("LateParsed"); + + if (LateParsed) { + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); + + // FIXME: Handle non-GNU attributes + for (const auto &I : Spellings) { + if (I.variety() != "GNU") + continue; + OS << ".Case(\"" << I.name() << "\", " << LateParsed << ")\n"; + } + } + } + OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; +} + +/// \brief 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"; + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + // Determine whether the first argument is a type. + std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args"); + if (Args.empty()) + continue; + + if (Args[0]->getSuperClasses().back()->getName() != "TypeArgument") + continue; + + // All these spellings take a single type argument. + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); + std::set<std::string> Emitted; + for (const auto &S : Spellings) { + if (Emitted.insert(S.name()).second) + OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + } + } + OS << "#endif // CLANG_ATTR_TYPE_ARG_LIST\n\n"; +} + +/// \brief Emits the parse-arguments-in-unevaluated-context property for +/// attributes. +static void emitClangAttrArgContextList(RecordKeeper &Records, raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_ARG_CONTEXT_LIST)\n"; + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &Attr = *I.second; + + if (!Attr.getValueAsBit("ParseArgumentsAsUnevaluated")) + continue; + + // All these spellings take are parsed unevaluated. + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); + std::set<std::string> Emitted; + for (const auto &S : Spellings) { + if (Emitted.insert(S.name()).second) + OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + } + } + OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n"; +} + +static bool isIdentifierArgument(Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch<bool>(Arg->getSuperClasses().back()->getName()) + .Case("IdentifierArgument", true) + .Case("EnumArgument", true) + .Case("VariadicEnumArgument", true) + .Default(false); +} + +// 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"; + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + // Determine whether the first argument is an identifier. + std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args"); + if (Args.empty() || !isIdentifierArgument(Args[0])) + continue; + + // All these spellings take an identifier argument. + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); + std::set<std::string> Emitted; + for (const auto &S : Spellings) { + if (Emitted.insert(S.name()).second) + OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + } + } + OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n"; +} + +namespace clang { + +// Emits the class definitions for attributes. +void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute classes' definitions", OS); + + OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n"; + OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n"; + + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + + // FIXME: Currently, documentation is generated as-needed due to the fact + // that there is no way to allow a generated project "reach into" the docs + // directory (for instance, it may be an out-of-tree build). However, we want + // to ensure that every attribute has a Documentation field, and produce an + // error if it has been neglected. Otherwise, the on-demand generation which + // happens server-side will fail. This code is ensuring that functionality, + // even though this Emitter doesn't technically need the documentation. + // When attribute documentation can be generated as part of the build + // itself, this code can be removed. + (void)R.getValueAsListOfDefs("Documentation"); + + if (!R.getValueAsBit("ASTNode")) + continue; + + ArrayRef<Record *> Supers = R.getSuperClasses(); + assert(!Supers.empty() && "Forgot to specify a superclass for the attr"); + std::string SuperName; + for (const auto *Super : llvm::make_range(Supers.rbegin(), Supers.rend())) { + const Record &R = *Super; + if (R.getName() != "TargetSpecificAttr" && SuperName.empty()) + SuperName = R.getName(); + } + + OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n"; + + std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); + std::vector<std::unique_ptr<Argument>> Args; + Args.reserve(ArgRecords.size()); + + for (const auto *ArgRecord : ArgRecords) { + Args.emplace_back(createArgument(*ArgRecord, R.getName())); + Args.back()->writeDeclarations(OS); + OS << "\n\n"; + } + + OS << "\npublic:\n"; + + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); + + // If there are zero or one spellings, all spelling-related functionality + // can be elided. If all of the spellings share the same name, the spelling + // functionality can also be elided. + bool ElideSpelling = (Spellings.size() <= 1) || + SpellingNamesAreCommon(Spellings); + + // This maps spelling index values to semantic Spelling enumerants. + SemanticSpellingMap SemanticToSyntacticMap; + + if (!ElideSpelling) + OS << CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); + + OS << " static " << R.getName() << "Attr *CreateImplicit("; + OS << "ASTContext &Ctx"; + if (!ElideSpelling) + OS << ", Spelling S"; + for (auto const &ai : Args) { + OS << ", "; + ai->writeCtorParameters(OS); + } + OS << ", SourceRange Loc = SourceRange()"; + OS << ") {\n"; + OS << " " << R.getName() << "Attr *A = new (Ctx) " << R.getName(); + OS << "Attr(Loc, Ctx, "; + for (auto const &ai : Args) { + ai->writeImplicitCtorArgs(OS); + OS << ", "; + } + OS << (ElideSpelling ? "0" : "S") << ");\n"; + OS << " A->setImplicit(true);\n"; + OS << " return A;\n }\n\n"; + + OS << " " << R.getName() << "Attr(SourceRange R, ASTContext &Ctx\n"; + + bool HasOpt = false; + for (auto const &ai : Args) { + OS << " , "; + ai->writeCtorParameters(OS); + OS << "\n"; + if (ai->isOptional()) + HasOpt = true; + } + + OS << " , "; + OS << "unsigned SI\n"; + + OS << " )\n"; + OS << " : " << SuperName << "(attr::" << R.getName() << ", R, SI, " + << R.getValueAsBit("LateParsed") << ", " + << R.getValueAsBit("DuplicatesAllowedWhileMerging") << ")\n"; + + for (auto const &ai : Args) { + OS << " , "; + ai->writeCtorInitializers(OS); + OS << "\n"; + } + + OS << " {\n"; + + for (auto const &ai : Args) { + ai->writeCtorBody(OS); + OS << "\n"; + } + OS << " }\n\n"; + + // If there are optional arguments, write out a constructor that elides the + // optional arguments as well. + if (HasOpt) { + OS << " " << R.getName() << "Attr(SourceRange R, ASTContext &Ctx\n"; + for (auto const &ai : Args) { + if (!ai->isOptional()) { + OS << " , "; + ai->writeCtorParameters(OS); + OS << "\n"; + } + } + + OS << " , "; + OS << "unsigned SI\n"; + + OS << " )\n"; + OS << " : " << SuperName << "(attr::" << R.getName() << ", R, SI, " + << R.getValueAsBit("LateParsed") << ", " + << R.getValueAsBit("DuplicatesAllowedWhileMerging") << ")\n"; + + for (auto const &ai : Args) { + OS << " , "; + ai->writeCtorDefaultInitializers(OS); + OS << "\n"; + } + + OS << " {\n"; + + for (auto const &ai : Args) { + if (!ai->isOptional()) { + ai->writeCtorBody(OS); + OS << "\n"; + } + } + OS << " }\n\n"; + } + + OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n"; + OS << " void printPretty(raw_ostream &OS,\n" + << " const PrintingPolicy &Policy) const;\n"; + OS << " const char *getSpelling() const;\n"; + + if (!ElideSpelling) { + assert(!SemanticToSyntacticMap.empty() && "Empty semantic mapping list"); + OS << " Spelling getSemanticSpelling() const {\n"; + WriteSemanticSpellingSwitch("SpellingListIndex", SemanticToSyntacticMap, + OS); + OS << " }\n"; + } + + writeAttrAccessorDefinition(R, OS); + + for (auto const &ai : Args) { + ai->writeAccessors(OS); + OS << "\n\n"; + + if (ai->isEnumArg()) + static_cast<const EnumArgument *>(ai.get())->writeConversion(OS); + else if (ai->isVariadicEnumArg()) + static_cast<const VariadicEnumArgument *>(ai.get()) + ->writeConversion(OS); + } + + OS << R.getValueAsString("AdditionalMembers"); + OS << "\n\n"; + + OS << " static bool classof(const Attr *A) { return A->getKind() == " + << "attr::" << R.getName() << "; }\n"; + + OS << "};\n\n"; + } + + OS << "#endif\n"; +} + +// Emits the class method definitions for attributes. +void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute classes' member function definitions", OS); + + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (auto *Attr : Attrs) { + Record &R = *Attr; + + if (!R.getValueAsBit("ASTNode")) + continue; + + std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); + std::vector<std::unique_ptr<Argument>> Args; + for (const auto *Arg : ArgRecords) + Args.emplace_back(createArgument(*Arg, R.getName())); + + for (auto const &ai : Args) + ai->writeAccessorDefinitions(OS); + + OS << R.getName() << "Attr *" << R.getName() + << "Attr::clone(ASTContext &C) const {\n"; + OS << " auto *A = new (C) " << R.getName() << "Attr(getLocation(), C"; + for (auto const &ai : Args) { + OS << ", "; + ai->writeCloneArgs(OS); + } + OS << ", getSpellingListIndex());\n"; + OS << " A->Inherited = Inherited;\n"; + OS << " A->IsPackExpansion = IsPackExpansion;\n"; + OS << " A->Implicit = Implicit;\n"; + OS << " return A;\n}\n\n"; + + writePrettyPrintFunction(R, Args, OS); + writeGetSpellingFunction(R, OS); + } + + // Instead of relying on virtual dispatch we just create a huge dispatch + // switch. This is both smaller and faster than virtual functions. + auto EmitFunc = [&](const char *Method) { + OS << " switch (getKind()) {\n"; + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + + OS << " case attr::" << R.getName() << ":\n"; + OS << " return cast<" << R.getName() << "Attr>(this)->" << Method + << ";\n"; + } + OS << " case attr::NUM_ATTRS:\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; + OS << "}\n\n"; + }; + + OS << "const char *Attr::getSpelling() const {\n"; + EmitFunc("getSpelling()"); + + OS << "Attr *Attr::clone(ASTContext &C) const {\n"; + EmitFunc("clone(C)"); + + OS << "void Attr::printPretty(raw_ostream &OS, " + "const PrintingPolicy &Policy) const {\n"; + EmitFunc("printPretty(OS, Policy)"); +} + +} // end namespace clang + +static void EmitAttrList(raw_ostream &OS, StringRef Class, + const std::vector<Record*> &AttrList) { + std::vector<Record*>::const_iterator i = AttrList.begin(), e = AttrList.end(); + + if (i != e) { + // Move the end iterator back to emit the last attribute. + for(--e; i != e; ++i) { + if (!(*i)->getValueAsBit("ASTNode")) + continue; + + OS << Class << "(" << (*i)->getName() << ")\n"; + } + + OS << "LAST_" << Class << "(" << (*i)->getName() << ")\n\n"; + } +} + +// Determines if an attribute has a Pragma spelling. +static bool AttrHasPragmaSpelling(const Record *R) { + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R); + return std::find_if(Spellings.begin(), Spellings.end(), + [](const FlattenedSpelling &S) { + return S.variety() == "Pragma"; + }) != Spellings.end(); +} + +namespace clang { +// Emits the enumeration list for attributes. +void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + + OS << "#ifndef LAST_ATTR\n"; + OS << "#define LAST_ATTR(NAME) ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef INHERITABLE_ATTR\n"; + OS << "#define INHERITABLE_ATTR(NAME) ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef LAST_INHERITABLE_ATTR\n"; + OS << "#define LAST_INHERITABLE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef INHERITABLE_PARAM_ATTR\n"; + OS << "#define INHERITABLE_PARAM_ATTR(NAME) ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef LAST_INHERITABLE_PARAM_ATTR\n"; + OS << "#define LAST_INHERITABLE_PARAM_ATTR(NAME)" + " INHERITABLE_PARAM_ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef PRAGMA_SPELLING_ATTR\n"; + OS << "#define PRAGMA_SPELLING_ATTR(NAME)\n"; + OS << "#endif\n\n"; + + OS << "#ifndef LAST_PRAGMA_SPELLING_ATTR\n"; + OS << "#define LAST_PRAGMA_SPELLING_ATTR(NAME) PRAGMA_SPELLING_ATTR(NAME)\n"; + OS << "#endif\n\n"; + + Record *InhClass = Records.getClass("InheritableAttr"); + Record *InhParamClass = Records.getClass("InheritableParamAttr"); + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"), + NonInhAttrs, InhAttrs, InhParamAttrs, PragmaAttrs; + for (auto *Attr : Attrs) { + if (!Attr->getValueAsBit("ASTNode")) + continue; + + if (AttrHasPragmaSpelling(Attr)) + PragmaAttrs.push_back(Attr); + + if (Attr->isSubClassOf(InhParamClass)) + InhParamAttrs.push_back(Attr); + else if (Attr->isSubClassOf(InhClass)) + InhAttrs.push_back(Attr); + else + NonInhAttrs.push_back(Attr); + } + + EmitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); + EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); + EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); + EmitAttrList(OS, "ATTR", NonInhAttrs); + + OS << "#undef LAST_ATTR\n"; + OS << "#undef INHERITABLE_ATTR\n"; + OS << "#undef LAST_INHERITABLE_ATTR\n"; + OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; + OS << "#undef LAST_PRAGMA_ATTR\n"; + OS << "#undef PRAGMA_SPELLING_ATTR\n"; + OS << "#undef ATTR\n"; +} + +// Emits the code to read an attribute from a precompiled header. +void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute deserialization code", OS); + + Record *InhClass = Records.getClass("InheritableAttr"); + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), + ArgRecords; + std::vector<std::unique_ptr<Argument>> Args; + + OS << " switch (Kind) {\n"; + OS << " default:\n"; + OS << " llvm_unreachable(\"Unknown attribute!\");\n"; + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + + OS << " case attr::" << R.getName() << ": {\n"; + if (R.isSubClassOf(InhClass)) + OS << " bool isInherited = Record[Idx++];\n"; + OS << " bool isImplicit = Record[Idx++];\n"; + OS << " unsigned Spelling = Record[Idx++];\n"; + ArgRecords = R.getValueAsListOfDefs("Args"); + Args.clear(); + for (const auto *Arg : ArgRecords) { + Args.emplace_back(createArgument(*Arg, R.getName())); + Args.back()->writePCHReadDecls(OS); + } + OS << " New = new (Context) " << R.getName() << "Attr(Range, Context"; + for (auto const &ri : Args) { + OS << ", "; + ri->writePCHReadArgs(OS); + } + OS << ", Spelling);\n"; + if (R.isSubClassOf(InhClass)) + OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n"; + OS << " New->setImplicit(isImplicit);\n"; + OS << " break;\n"; + OS << " }\n"; + } + OS << " }\n"; +} + +// Emits the code to write an attribute to a precompiled header. +void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute serialization code", OS); + + Record *InhClass = Records.getClass("InheritableAttr"); + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; + + OS << " switch (A->getKind()) {\n"; + OS << " default:\n"; + OS << " llvm_unreachable(\"Unknown attribute kind!\");\n"; + OS << " break;\n"; + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + OS << " case attr::" << R.getName() << ": {\n"; + Args = R.getValueAsListOfDefs("Args"); + if (R.isSubClassOf(InhClass) || !Args.empty()) + OS << " const " << R.getName() << "Attr *SA = cast<" << R.getName() + << "Attr>(A);\n"; + if (R.isSubClassOf(InhClass)) + OS << " Record.push_back(SA->isInherited());\n"; + OS << " Record.push_back(A->isImplicit());\n"; + OS << " Record.push_back(A->getSpellingListIndex());\n"; + + for (const auto *Arg : Args) + createArgument(*Arg, R.getName())->writePCHWrite(OS); + OS << " break;\n"; + OS << " }\n"; + } + OS << " }\n"; +} + +static void GenerateHasAttrSpellingStringSwitch( + const std::vector<Record *> &Attrs, raw_ostream &OS, + const std::string &Variety = "", const std::string &Scope = "") { + for (const auto *Attr : 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 + // attribute version information should be taken from the SD-6 standing + // document, which can be found at: + // https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations + int Version = 1; + + if (Variety == "CXX11") { + std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings"); + for (const auto &Spelling : Spellings) { + if (Spelling->getValueAsString("Variety") == "CXX11") { + Version = static_cast<int>(Spelling->getValueAsInt("Version")); + if (Scope.empty() && Version == 1) + PrintError(Spelling->getLoc(), "C++ standard attributes must " + "have valid version information."); + break; + } + } + } + + // It is assumed that there will be an llvm::Triple object named T within + // scope that can be used to determine whether the attribute exists in + // a given target. + std::string Test; + if (Attr->isSubClassOf("TargetSpecificAttr")) { + const Record *R = Attr->getValueAsDef("Target"); + std::vector<std::string> Arches = R->getValueAsListOfStrings("Arches"); + + Test += "("; + for (auto AI = Arches.begin(), AE = Arches.end(); AI != AE; ++AI) { + std::string Part = *AI; + Test += "T.getArch() == llvm::Triple::" + Part; + if (AI + 1 != AE) + Test += " || "; + } + Test += ")"; + + std::vector<std::string> OSes; + if (!R->isValueUnset("OSes")) { + Test += " && ("; + std::vector<std::string> OSes = R->getValueAsListOfStrings("OSes"); + for (auto AI = OSes.begin(), AE = OSes.end(); AI != AE; ++AI) { + std::string Part = *AI; + + Test += "T.getOS() == llvm::Triple::" + Part; + if (AI + 1 != AE) + Test += " || "; + } + Test += ")"; + } + + // If this is the C++11 variety, also add in the LangOpts test. + 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"; + + 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"; + } + OS << " .Default(0);\n"; +} + +// Emits the list of spellings for attributes. +void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Code to implement the __has_attribute logic", OS); + + // 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, GNU, Pragma; + std::map<std::string, std::vector<Record *>> CXX; + + // Walk over the list of all attributes, and split them out based on the + // spelling variety. + for (auto *R : Attrs) { + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R); + for (const auto &SI : Spellings) { + std::string Variety = SI.variety(); + if (Variety == "GNU") + GNU.push_back(R); + else if (Variety == "Declspec") + Declspec.push_back(R); + else if (Variety == "CXX11") + CXX[SI.nameSpace()].push_back(R); + else if (Variety == "Pragma") + Pragma.push_back(R); + } + } + + OS << "switch (Syntax) {\n"; + OS << "case AttrSyntax::GNU:\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU"); + OS << "case AttrSyntax::Declspec:\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec"); + OS << "case AttrSyntax::Pragma:\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma"); + OS << "case AttrSyntax::CXX: {\n"; + // C++11-style attributes are further split out based on the Scope. + for (std::map<std::string, std::vector<Record *>>::iterator I = CXX.begin(), + E = CXX.end(); + I != E; ++I) { + if (I != CXX.begin()) + OS << " else "; + if (I->first.empty()) + OS << "if (!Scope || Scope->getName() == \"\") {\n"; + else + OS << "if (Scope->getName() == \"" << I->first << "\") {\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first); + OS << "}"; + } + OS << "\n}\n"; + OS << "}\n"; +} + +void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Code to translate different attribute spellings " + "into internal identifiers", OS); + + OS << + " switch (AttrKind) {\n" + " default:\n" + " llvm_unreachable(\"Unknown attribute kind!\");\n" + " break;\n"; + + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &R = *I.second; + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); + OS << " case AT_" << I.first << ": {\n"; + for (unsigned I = 0; I < Spellings.size(); ++ I) { + OS << " if (Name == \"" << Spellings[I].name() << "\" && " + << "SyntaxUsed == " + << StringSwitch<unsigned>(Spellings[I].variety()) + .Case("GNU", 0) + .Case("CXX11", 1) + .Case("Declspec", 2) + .Case("Keyword", 3) + .Case("Pragma", 4) + .Default(0) + << " && Scope == \"" << Spellings[I].nameSpace() << "\")\n" + << " return " << I << ";\n"; + } + + OS << " break;\n"; + OS << " }\n"; + } + + OS << " }\n"; + OS << " return 0;\n"; +} + +// Emits code used by RecursiveASTVisitor to visit attributes +void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS); + + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + // Write method declarations for Traverse* methods. + // We emit this here because we only generate methods for attributes that + // are declared as ASTNodes. + OS << "#ifdef ATTR_VISITOR_DECLS_ONLY\n\n"; + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + OS << " bool Traverse" + << R.getName() << "Attr(" << R.getName() << "Attr *A);\n"; + OS << " bool Visit" + << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n" + << " return true; \n" + << " };\n"; + } + OS << "\n#else // ATTR_VISITOR_DECLS_ONLY\n\n"; + + // Write individual Traverse* methods for each attribute class. + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + + OS << "template <typename Derived>\n" + << "bool VISITORCLASS<Derived>::Traverse" + << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n" + << " if (!getDerived().VisitAttr(A))\n" + << " return false;\n" + << " if (!getDerived().Visit" << R.getName() << "Attr(A))\n" + << " return false;\n"; + + std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); + for (const auto *Arg : ArgRecords) + createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS); + + OS << " return true;\n"; + OS << "}\n\n"; + } + + // Write generic Traverse routine + OS << "template <typename Derived>\n" + << "bool VISITORCLASS<Derived>::TraverseAttr(Attr *A) {\n" + << " if (!A)\n" + << " return true;\n" + << "\n" + << " switch (A->getKind()) {\n" + << " default:\n" + << " return true;\n"; + + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + + OS << " case attr::" << R.getName() << ":\n" + << " return getDerived().Traverse" << R.getName() << "Attr(" + << "cast<" << R.getName() << "Attr>(A));\n"; + } + OS << " }\n"; // end case + OS << "}\n"; // end function + OS << "#endif // ATTR_VISITOR_DECLS_ONLY\n"; +} + +// Emits code to instantiate dependent attributes on templates. +void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Template instantiation code for attributes", OS); + + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); + + OS << "namespace clang {\n" + << "namespace sema {\n\n" + << "Attr *instantiateTemplateAttribute(const Attr *At, ASTContext &C, " + << "Sema &S,\n" + << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n" + << " switch (At->getKind()) {\n" + << " default:\n" + << " break;\n"; + + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + + OS << " case attr::" << R.getName() << ": {\n"; + bool ShouldClone = R.getValueAsBit("Clone"); + + if (!ShouldClone) { + OS << " return NULL;\n"; + OS << " }\n"; + continue; + } + + OS << " const " << R.getName() << "Attr *A = cast<" + << R.getName() << "Attr>(At);\n"; + bool TDependent = R.getValueAsBit("TemplateDependent"); + + if (!TDependent) { + OS << " return A->clone(C);\n"; + OS << " }\n"; + continue; + } + + std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args"); + std::vector<std::unique_ptr<Argument>> Args; + Args.reserve(ArgRecords.size()); + + for (const auto *ArgRecord : ArgRecords) + Args.emplace_back(createArgument(*ArgRecord, R.getName())); + + for (auto const &ai : Args) + ai->writeTemplateInstantiation(OS); + + OS << " return new (C) " << R.getName() << "Attr(A->getLocation(), C"; + for (auto const &ai : Args) { + OS << ", "; + ai->writeTemplateInstantiationArgs(OS); + } + OS << ", A->getSpellingListIndex());\n }\n"; + } + OS << " } // end switch\n" + << " llvm_unreachable(\"Unknown attribute!\");\n" + << " return 0;\n" + << "}\n\n" + << "} // end namespace sema\n" + << "} // end namespace clang\n"; +} + +// Emits the list of parsed attributes. +void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + + OS << "#ifndef PARSED_ATTR\n"; + OS << "#define PARSED_ATTR(NAME) NAME\n"; + OS << "#endif\n\n"; + + ParsedAttrMap Names = getParsedAttrList(Records); + for (const auto &I : Names) { + OS << "PARSED_ATTR(" << I.first << ")\n"; + } +} + +static bool isArgVariadic(const Record &R, StringRef AttrName) { + return createArgument(R, AttrName)->isVariadic(); +} + +static void emitArgInfo(const Record &R, std::stringstream &OS) { + // This function will count the number of arguments specified for the + // 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; + bool HasVariadic = false; + for (const auto *Arg : Args) { + Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount; + 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 << ArgCount << ", " << (HasVariadic ? 15 : OptCount); +} + +static void GenerateDefaultAppertainsTo(raw_ostream &OS) { + OS << "static bool defaultAppertainsTo(Sema &, const AttributeList &,"; + OS << "const Decl *) {\n"; + OS << " return true;\n"; + OS << "}\n\n"; +} + +static std::string CalculateDiagnostic(const Record &S) { + // If the SubjectList object has a custom diagnostic associated with it, + // return that directly. + std::string CustomDiag = S.getValueAsString("CustomDiag"); + if (!CustomDiag.empty()) + return CustomDiag; + + // Given the list of subjects, determine what diagnostic best fits. + enum { + Func = 1U << 0, + Var = 1U << 1, + ObjCMethod = 1U << 2, + Param = 1U << 3, + Class = 1U << 4, + GenericRecord = 1U << 5, + Type = 1U << 6, + ObjCIVar = 1U << 7, + ObjCProp = 1U << 8, + ObjCInterface = 1U << 9, + Block = 1U << 10, + Namespace = 1U << 11, + Field = 1U << 12, + CXXMethod = 1U << 13, + ObjCProtocol = 1U << 14, + Enum = 1U << 15 + }; + uint32_t SubMask = 0; + + std::vector<Record *> Subjects = S.getValueAsListOfDefs("Subjects"); + for (const auto *Subject : Subjects) { + const Record &R = *Subject; + std::string Name; + + if (R.isSubClassOf("SubsetSubject")) { + PrintError(R.getLoc(), "SubsetSubjects should use a custom diagnostic"); + // As a fallback, look through the SubsetSubject to see what its base + // type is, and use that. This needs to be updated if SubsetSubjects + // are allowed within other SubsetSubjects. + Name = R.getValueAsDef("Base")->getName(); + } else + Name = R.getName(); + + uint32_t V = StringSwitch<uint32_t>(Name) + .Case("Function", Func) + .Case("Var", Var) + .Case("ObjCMethod", ObjCMethod) + .Case("ParmVar", Param) + .Case("TypedefName", Type) + .Case("ObjCIvar", ObjCIVar) + .Case("ObjCProperty", ObjCProp) + .Case("Record", GenericRecord) + .Case("ObjCInterface", ObjCInterface) + .Case("ObjCProtocol", ObjCProtocol) + .Case("Block", Block) + .Case("CXXRecord", Class) + .Case("Namespace", Namespace) + .Case("Field", Field) + .Case("CXXMethod", CXXMethod) + .Case("Enum", Enum) + .Default(0); + if (!V) { + // Something wasn't in our mapping, so be helpful and let the developer + // know about it. + PrintFatalError(R.getLoc(), "Unknown subject type: " + R.getName()); + return ""; + } + + SubMask |= V; + } + + switch (SubMask) { + // For the simple cases where there's only a single entry in the mask, we + // don't have to resort to bit fiddling. + case Func: return "ExpectedFunction"; + case Var: return "ExpectedVariable"; + case Param: return "ExpectedParameter"; + case Class: return "ExpectedClass"; + case Enum: return "ExpectedEnum"; + case CXXMethod: + // FIXME: Currently, this maps to ExpectedMethod based on existing code, + // but should map to something a bit more accurate at some point. + case ObjCMethod: return "ExpectedMethod"; + case Type: return "ExpectedType"; + case ObjCInterface: return "ExpectedObjectiveCInterface"; + case ObjCProtocol: return "ExpectedObjectiveCProtocol"; + + // "GenericRecord" means struct, union or class; check the language options + // and if not compiling for C++, strip off the class part. Note that this + // relies on the fact that the context for this declares "Sema &S". + case GenericRecord: + return "(S.getLangOpts().CPlusPlus ? ExpectedStructOrUnionOrClass : " + "ExpectedStructOrUnion)"; + case Func | ObjCMethod | Block: return "ExpectedFunctionMethodOrBlock"; + case Func | ObjCMethod | Class: return "ExpectedFunctionMethodOrClass"; + case Func | Param: + case Func | ObjCMethod | Param: return "ExpectedFunctionMethodOrParameter"; + case Func | ObjCMethod: return "ExpectedFunctionOrMethod"; + case Func | Var: return "ExpectedVariableOrFunction"; + + // If not compiling for C++, the class portion does not apply. + case Func | Var | Class: + return "(S.getLangOpts().CPlusPlus ? ExpectedFunctionVariableOrClass : " + "ExpectedVariableOrFunction)"; + + case ObjCMethod | ObjCProp: return "ExpectedMethodOrProperty"; + case ObjCProtocol | ObjCInterface: + return "ExpectedObjectiveCInterfaceOrProtocol"; + case Field | Var: return "ExpectedFieldOrGlobalVar"; + } + + PrintFatalError(S.getLoc(), + "Could not deduce diagnostic argument for Attr subjects"); + + return ""; +} + +static std::string GetSubjectWithSuffix(const Record *R) { + std::string B = R->getName(); + if (B == "DeclBase") + return "Decl"; + return B + "Decl"; +} +static std::string GenerateCustomAppertainsTo(const Record &Subject, + raw_ostream &OS) { + std::string FnName = "is" + Subject.getName(); + + // If this code has already been generated, simply return the previous + // instance of it. + static std::set<std::string> CustomSubjectSet; + std::set<std::string>::iterator I = CustomSubjectSet.find(FnName); + if (I != CustomSubjectSet.end()) + return *I; + + Record *Base = Subject.getValueAsDef("Base"); + + // Not currently support custom subjects within custom subjects. + if (Base->isSubClassOf("SubsetSubject")) { + PrintFatalError(Subject.getLoc(), + "SubsetSubjects within SubsetSubjects is not supported"); + return ""; + } + + OS << "static bool " << FnName << "(const Decl *D) {\n"; + OS << " if (const " << GetSubjectWithSuffix(Base) << " *S = dyn_cast<"; + OS << GetSubjectWithSuffix(Base); + OS << ">(D))\n"; + OS << " return " << Subject.getValueAsString("CheckCode") << ";\n"; + OS << " return false;\n"; + OS << "}\n\n"; + + CustomSubjectSet.insert(FnName); + return FnName; +} + +static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { + // If the attribute does not contain a Subjects definition, then use the + // default appertainsTo logic. + if (Attr.isValueUnset("Subjects")) + return "defaultAppertainsTo"; + + const Record *SubjectObj = Attr.getValueAsDef("Subjects"); + std::vector<Record*> Subjects = SubjectObj->getValueAsListOfDefs("Subjects"); + + // If the list of subjects is empty, it is assumed that the attribute + // appertains to everything. + if (Subjects.empty()) + return "defaultAppertainsTo"; + + bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn"); + + // Otherwise, generate an appertainsTo check specific to this attribute which + // checks all of the given subjects against the Decl passed in. Return the + // name of that check to the caller. + std::string FnName = "check" + Attr.getName() + "AppertainsTo"; + std::stringstream SS; + SS << "static bool " << FnName << "(Sema &S, const AttributeList &Attr, "; + SS << "const Decl *D) {\n"; + SS << " if ("; + for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) { + // If the subject has custom code associated with it, generate a function + // for it. The function cannot be inlined into this check (yet) because it + // requires the subject to be of a specific type, and were that information + // inlined here, it would not support an attribute with multiple custom + // subjects. + if ((*I)->isSubClassOf("SubsetSubject")) { + SS << "!" << GenerateCustomAppertainsTo(**I, OS) << "(D)"; + } else { + SS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)"; + } + + if (I + 1 != E) + SS << " && "; + } + SS << ") {\n"; + SS << " S.Diag(Attr.getLoc(), diag::"; + SS << (Warn ? "warn_attribute_wrong_decl_type" : + "err_attribute_wrong_decl_type"); + SS << ")\n"; + SS << " << Attr.getName() << "; + SS << CalculateDiagnostic(*SubjectObj) << ";\n"; + SS << " return false;\n"; + SS << " }\n"; + SS << " return true;\n"; + SS << "}\n\n"; + + OS << SS.str(); + return FnName; +} + +static void GenerateDefaultLangOptRequirements(raw_ostream &OS) { + OS << "static bool defaultDiagnoseLangOpts(Sema &, "; + OS << "const AttributeList &) {\n"; + OS << " return true;\n"; + OS << "}\n\n"; +} + +static std::string GenerateLangOptRequirements(const Record &R, + raw_ostream &OS) { + // If the attribute has an empty or unset list of language requirements, + // return the default handler. + std::vector<Record *> LangOpts = R.getValueAsListOfDefs("LangOpts"); + if (LangOpts.empty()) + return "defaultDiagnoseLangOpts"; + + // Generate the test condition, as well as a unique function name for the + // diagnostic test. The list of options should usually be short (one or two + // options), and the uniqueness isn't strictly necessary (it is just for + // codegen efficiency). + std::string FnName = "check", Test; + for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { + std::string Part = (*I)->getValueAsString("Name"); + if ((*I)->getValueAsBit("Negated")) + Test += "!"; + Test += "S.LangOpts." + Part; + if (I + 1 != E) + Test += " || "; + FnName += Part; + } + FnName += "LangOpts"; + + // If this code has already been generated, simply return the previous + // instance of it. + static std::set<std::string> CustomLangOptsSet; + std::set<std::string>::iterator I = CustomLangOptsSet.find(FnName); + if (I != CustomLangOptsSet.end()) + return *I; + + OS << "static bool " << FnName << "(Sema &S, const AttributeList &Attr) {\n"; + OS << " if (" << Test << ")\n"; + OS << " return true;\n\n"; + OS << " S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) "; + OS << "<< Attr.getName();\n"; + OS << " return false;\n"; + OS << "}\n\n"; + + CustomLangOptsSet.insert(FnName); + return FnName; +} + +static void GenerateDefaultTargetRequirements(raw_ostream &OS) { + OS << "static bool defaultTargetRequirements(const llvm::Triple &) {\n"; + OS << " return true;\n"; + OS << "}\n\n"; +} + +static std::string GenerateTargetRequirements(const Record &Attr, + const ParsedAttrMap &Dupes, + raw_ostream &OS) { + // If the attribute is not a target specific attribute, return the default + // target handler. + if (!Attr.isSubClassOf("TargetSpecificAttr")) + return "defaultTargetRequirements"; + + // Get the list of architectures to be tested for. + const Record *R = Attr.getValueAsDef("Target"); + std::vector<std::string> Arches = R->getValueAsListOfStrings("Arches"); + if (Arches.empty()) { + PrintError(Attr.getLoc(), "Empty list of target architectures for a " + "target-specific attr"); + return "defaultTargetRequirements"; + } + + // If there are other attributes which share the same parsed attribute kind, + // such as target-specific attributes with a shared spelling, collapse the + // duplicate architectures. This is required because a shared target-specific + // attribute has only one AttributeList::Kind enumeration value, but it + // applies to multiple target architectures. In order for the attribute to be + // considered valid, all of its architectures need to be included. + if (!Attr.isValueUnset("ParseKind")) { + std::string APK = Attr.getValueAsString("ParseKind"); + for (const auto &I : Dupes) { + if (I.first == APK) { + std::vector<std::string> DA = I.second->getValueAsDef("Target") + ->getValueAsListOfStrings("Arches"); + std::copy(DA.begin(), DA.end(), std::back_inserter(Arches)); + } + } + } + + std::string FnName = "isTarget", Test = "("; + for (auto I = Arches.begin(), E = Arches.end(); I != E; ++I) { + std::string Part = *I; + Test += "Arch == llvm::Triple::" + Part; + if (I + 1 != E) + Test += " || "; + FnName += Part; + } + Test += ")"; + + // If the target also requires OS testing, generate those tests as well. + bool UsesOS = false; + if (!R->isValueUnset("OSes")) { + UsesOS = true; + + // We know that there was at least one arch test, so we need to and in the + // OS tests. + Test += " && ("; + std::vector<std::string> OSes = R->getValueAsListOfStrings("OSes"); + for (auto I = OSes.begin(), E = OSes.end(); I != E; ++I) { + std::string Part = *I; + + Test += "OS == llvm::Triple::" + Part; + if (I + 1 != E) + Test += " || "; + FnName += Part; + } + Test += ")"; + } + + // If this code has already been generated, simply return the previous + // instance of it. + static std::set<std::string> CustomTargetSet; + std::set<std::string>::iterator I = CustomTargetSet.find(FnName); + if (I != CustomTargetSet.end()) + return *I; + + OS << "static bool " << FnName << "(const llvm::Triple &T) {\n"; + OS << " llvm::Triple::ArchType Arch = T.getArch();\n"; + if (UsesOS) + OS << " llvm::Triple::OSType OS = T.getOS();\n"; + OS << " return " << Test << ";\n"; + OS << "}\n\n"; + + CustomTargetSet.insert(FnName); + return FnName; +} + +static void GenerateDefaultSpellingIndexToSemanticSpelling(raw_ostream &OS) { + OS << "static unsigned defaultSpellingIndexToSemanticSpelling(" + << "const AttributeList &Attr) {\n"; + OS << " return UINT_MAX;\n"; + OS << "}\n\n"; +} + +static std::string GenerateSpellingIndexToSemanticSpelling(const Record &Attr, + raw_ostream &OS) { + // If the attribute does not have a semantic form, we can bail out early. + if (!Attr.getValueAsBit("ASTNode")) + return "defaultSpellingIndexToSemanticSpelling"; + + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); + + // If there are zero or one spellings, or all of the spellings share the same + // name, we can also bail out early. + if (Spellings.size() <= 1 || SpellingNamesAreCommon(Spellings)) + return "defaultSpellingIndexToSemanticSpelling"; + + // Generate the enumeration we will use for the mapping. + SemanticSpellingMap SemanticToSyntacticMap; + std::string Enum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap); + std::string Name = Attr.getName() + "AttrSpellingMap"; + + OS << "static unsigned " << Name << "(const AttributeList &Attr) {\n"; + OS << Enum; + OS << " unsigned Idx = Attr.getAttributeSpellingListIndex();\n"; + WriteSemanticSpellingSwitch("Idx", SemanticToSyntacticMap, OS); + OS << "}\n\n"; + + return Name; +} + +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. + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); + for (const auto &I : Spellings) { + if (I.knownToGCC()) + return true; + } + return false; +} + +/// Emits the parsed attribute helpers +void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Parsed attribute helpers", OS); + + // Get the list of parsed attributes, and accept the optional list of + // duplicates due to the ParseKind. + ParsedAttrMap Dupes; + ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes); + + // Generate the default appertainsTo, target and language option diagnostic, + // and spelling list index mapping methods. + GenerateDefaultAppertainsTo(OS); + GenerateDefaultLangOptRequirements(OS); + GenerateDefaultTargetRequirements(OS); + GenerateDefaultSpellingIndexToSemanticSpelling(OS); + + // Generate the appertainsTo diagnostic methods and write their names into + // another mapping. At the same time, generate the AttrInfoMap object + // contents. Due to the reliance on generated code, use separate streams so + // that code will not be interleaved. + std::stringstream SS; + for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { + // TODO: If the attribute's kind appears in the list of duplicates, that is + // because it is a target-specific attribute that appears multiple times. + // It would be beneficial to test whether the duplicates are "similar + // enough" to each other to not cause problems. For instance, check that + // the spellings are identical, and custom parsing rules match, etc. + + // We need to generate struct instances based off ParsedAttrInfo from + // AttributeList.cpp. + SS << " { "; + emitArgInfo(*I->second, SS); + SS << ", " << I->second->getValueAsBit("HasCustomParsing"); + SS << ", " << I->second->isSubClassOf("TargetSpecificAttr"); + SS << ", " << I->second->isSubClassOf("TypeAttr"); + SS << ", " << IsKnownToGCC(*I->second); + SS << ", " << GenerateAppertainsTo(*I->second, OS); + SS << ", " << GenerateLangOptRequirements(*I->second, OS); + SS << ", " << GenerateTargetRequirements(*I->second, Dupes, OS); + SS << ", " << GenerateSpellingIndexToSemanticSpelling(*I->second, OS); + SS << " }"; + + if (I + 1 != E) + SS << ","; + + SS << " // AT_" << I->first << "\n"; + } + + OS << "static const ParsedAttrInfo AttrInfoMap[AttributeList::UnknownAttribute + 1] = {\n"; + OS << SS.str(); + OS << "};\n\n"; +} + +// Emits the kind list of parsed attributes +void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute name matcher", OS); + + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); + std::vector<StringMatcher::StringPair> GNU, Declspec, CXX11, Keywords, Pragma; + std::set<std::string> Seen; + for (const auto *A : Attrs) { + const Record &Attr = *A; + + bool SemaHandler = Attr.getValueAsBit("SemaHandler"); + bool Ignored = Attr.getValueAsBit("Ignored"); + if (SemaHandler || Ignored) { + // Attribute spellings can be shared between target-specific attributes, + // and can be shared between syntaxes for the same attribute. For + // instance, an attribute can be spelled GNU<"interrupt"> for an ARM- + // specific attribute, or MSP430-specific attribute. Additionally, an + // attribute can be spelled GNU<"dllexport"> and Declspec<"dllexport"> + // for the same semantic attribute. Ultimately, we need to map each of + // these to a single AttributeList::Kind value, but the StringMatcher + // class cannot handle duplicate match strings. So we generate a list of + // string to match based on the syntax, and emit multiple string matchers + // depending on the syntax used. + std::string AttrName; + if (Attr.isSubClassOf("TargetSpecificAttr") && + !Attr.isValueUnset("ParseKind")) { + AttrName = Attr.getValueAsString("ParseKind"); + if (Seen.find(AttrName) != Seen.end()) + continue; + Seen.insert(AttrName); + } else + AttrName = NormalizeAttrName(StringRef(Attr.getName())).str(); + + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr); + for (const auto &S : Spellings) { + std::string RawSpelling = S.name(); + std::vector<StringMatcher::StringPair> *Matches = nullptr; + std::string Spelling, Variety = S.variety(); + if (Variety == "CXX11") { + Matches = &CXX11; + Spelling += S.nameSpace(); + Spelling += "::"; + } else if (Variety == "GNU") + Matches = &GNU; + else if (Variety == "Declspec") + Matches = &Declspec; + else if (Variety == "Keyword") + Matches = &Keywords; + else if (Variety == "Pragma") + Matches = &Pragma; + + assert(Matches && "Unsupported spelling variety found"); + + Spelling += NormalizeAttrSpelling(RawSpelling); + if (SemaHandler) + Matches->push_back(StringMatcher::StringPair(Spelling, + "return AttributeList::AT_" + AttrName + ";")); + else + Matches->push_back(StringMatcher::StringPair(Spelling, + "return AttributeList::IgnoredAttribute;")); + } + } + } + + OS << "static AttributeList::Kind getAttrKind(StringRef Name, "; + OS << "AttributeList::Syntax Syntax) {\n"; + OS << " if (AttributeList::AS_GNU == Syntax) {\n"; + StringMatcher("Name", GNU, OS).Emit(); + OS << " } else if (AttributeList::AS_Declspec == Syntax) {\n"; + StringMatcher("Name", Declspec, OS).Emit(); + OS << " } else if (AttributeList::AS_CXX11 == Syntax) {\n"; + StringMatcher("Name", CXX11, OS).Emit(); + OS << " } else if (AttributeList::AS_Keyword == Syntax || "; + OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n"; + StringMatcher("Name", Keywords, OS).Emit(); + OS << " } else if (AttributeList::AS_Pragma == Syntax) {\n"; + StringMatcher("Name", Pragma, OS).Emit(); + OS << " }\n"; + OS << " return AttributeList::UnknownAttribute;\n" + << "}\n"; +} + +// Emits the code to dump an attribute. +void EmitClangAttrDump(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Attribute dumper", OS); + + OS << + " switch (A->getKind()) {\n" + " default:\n" + " llvm_unreachable(\"Unknown attribute kind!\");\n" + " break;\n"; + std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args; + for (const auto *Attr : Attrs) { + const Record &R = *Attr; + if (!R.getValueAsBit("ASTNode")) + continue; + OS << " case attr::" << R.getName() << ": {\n"; + + // If the attribute has a semantically-meaningful name (which is determined + // by whether there is a Spelling enumeration for it), then write out the + // spelling used for the attribute. + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R); + if (Spellings.size() > 1 && !SpellingNamesAreCommon(Spellings)) + OS << " OS << \" \" << A->getSpelling();\n"; + + Args = R.getValueAsListOfDefs("Args"); + if (!Args.empty()) { + OS << " const " << R.getName() << "Attr *SA = cast<" << R.getName() + << "Attr>(A);\n"; + for (const auto *Arg : Args) + createArgument(*Arg, R.getName())->writeDump(OS); + + for (auto AI = Args.begin(), AE = Args.end(); AI != AE; ++AI) + createArgument(**AI, R.getName())->writeDumpChildren(OS); + } + OS << + " break;\n" + " }\n"; + } + OS << " }\n"; +} + +void EmitClangAttrParserStringSwitches(RecordKeeper &Records, + raw_ostream &OS) { + emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS); + emitClangAttrArgContextList(Records, OS); + emitClangAttrIdentifierArgList(Records, OS); + emitClangAttrTypeArgList(Records, OS); + emitClangAttrLateParsedList(Records, OS); +} + +class DocumentationData { +public: + const Record *Documentation; + const Record *Attribute; + + DocumentationData(const Record &Documentation, const Record &Attribute) + : Documentation(&Documentation), Attribute(&Attribute) {} +}; + +static void WriteCategoryHeader(const Record *DocCategory, + raw_ostream &OS) { + const std::string &Name = DocCategory->getValueAsString("Name"); + OS << Name << "\n" << std::string(Name.length(), '=') << "\n"; + + // If there is content, print that as well. + std::string ContentStr = DocCategory->getValueAsString("Content"); + // Trim leading and trailing newlines and spaces. + OS << StringRef(ContentStr).trim(); + + OS << "\n\n"; +} + +enum SpellingKind { + GNU = 1 << 0, + CXX11 = 1 << 1, + Declspec = 1 << 2, + Keyword = 1 << 3, + Pragma = 1 << 4 +}; + +static void WriteDocumentation(const DocumentationData &Doc, + raw_ostream &OS) { + // 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. + + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Doc.Attribute); + + // Determine the heading to be used for this attribute. + std::string Heading = Doc.Documentation->getValueAsString("Heading"); + bool CustomHeading = !Heading.empty(); + if (Heading.empty()) { + // If there's only one spelling, we can simply use that. + if (Spellings.size() == 1) + Heading = Spellings.begin()->name(); + else { + std::set<std::string> Uniques; + for (auto I = Spellings.begin(), E = Spellings.end(); + I != E && Uniques.size() <= 1; ++I) { + std::string Spelling = NormalizeNameForSpellingComparison(I->name()); + Uniques.insert(Spelling); + } + // If the semantic map has only one spelling, that is sufficient for our + // needs. + if (Uniques.size() == 1) + Heading = *Uniques.begin(); + } + } + + // If the heading is still empty, it is an error. + if (Heading.empty()) + PrintFatalError(Doc.Attribute->getLoc(), + "This attribute requires a heading to be specified"); + + // Gather a list of unique spellings; this is not the same as the semantic + // spelling for the attribute. Variations in underscores and other non- + // semantic characters are still acceptable. + std::vector<std::string> Names; + + unsigned SupportedSpellings = 0; + for (const auto &I : Spellings) { + SpellingKind Kind = StringSwitch<SpellingKind>(I.variety()) + .Case("GNU", GNU) + .Case("CXX11", CXX11) + .Case("Declspec", Declspec) + .Case("Keyword", Keyword) + .Case("Pragma", Pragma); + + // Mask in the supported spelling. + SupportedSpellings |= Kind; + + std::string Name; + if (Kind == CXX11 && !I.nameSpace().empty()) + Name = I.nameSpace() + "::"; + Name += I.name(); + + // If this name is the same as the heading, do not add it. + if (Name != Heading) + Names.push_back(Name); + } + + // Print out the heading for the attribute. If there are alternate spellings, + // then display those after the heading. + if (!CustomHeading && !Names.empty()) { + Heading += " ("; + for (auto I = Names.begin(), E = Names.end(); I != E; ++I) { + if (I != Names.begin()) + Heading += ", "; + Heading += *I; + } + Heading += ")"; + } + OS << Heading << "\n" << std::string(Heading.length(), '-') << "\n"; + + if (!SupportedSpellings) + PrintFatalError(Doc.Attribute->getLoc(), + "Attribute has no supported spellings; cannot be " + "documented"); + + // List what spelling syntaxes the attribute supports. + OS << ".. csv-table:: Supported Syntaxes\n"; + OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\","; + OS << " \"Pragma\"\n\n"; + OS << " \""; + if (SupportedSpellings & GNU) OS << "X"; + OS << "\",\""; + if (SupportedSpellings & CXX11) OS << "X"; + OS << "\",\""; + if (SupportedSpellings & Declspec) OS << "X"; + OS << "\",\""; + if (SupportedSpellings & Keyword) OS << "X"; + OS << "\", \""; + if (SupportedSpellings & Pragma) OS << "X"; + OS << "\"\n\n"; + + // If the attribute is deprecated, print a message about it, and possibly + // provide a replacement attribute. + if (!Doc.Documentation->isValueUnset("Deprecated")) { + OS << "This attribute has been deprecated, and may be removed in a future " + << "version of Clang."; + const Record &Deprecated = *Doc.Documentation->getValueAsDef("Deprecated"); + std::string Replacement = Deprecated.getValueAsString("Replacement"); + if (!Replacement.empty()) + OS << " This attribute has been superseded by ``" + << Replacement << "``."; + OS << "\n\n"; + } + + std::string ContentStr = Doc.Documentation->getValueAsString("Content"); + // Trim leading and trailing newlines and spaces. + OS << StringRef(ContentStr).trim(); + + OS << "\n\n\n"; +} + +void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) { + // Get the documentation introduction paragraph. + const Record *Documentation = Records.getDef("GlobalDocumentation"); + if (!Documentation) { + PrintFatalError("The Documentation top-level definition is missing, " + "no documentation will be generated."); + return; + } + + OS << Documentation->getValueAsString("Intro") << "\n"; + + // 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; + 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). + std::string Cat = Category->getValueAsString("Name"); + bool Undocumented = Cat == "Undocumented"; + if (Undocumented && Docs.size() > 1) + PrintFatalError(Doc.getLoc(), + "Attribute is \"Undocumented\", but has multiple " + "documentation categories"); + + if (!Undocumented) + SplitDocs[Category].push_back(DocumentationData(Doc, Attr)); + } + } + + // Having split the attributes out based on what documentation goes where, + // we can begin to generate sections of documentation. + for (const auto &I : SplitDocs) { + WriteCategoryHeader(I.first, OS); + + // Walk over each of the attributes in the category and write out their + // documentation. + for (const auto &Doc : I.second) + WriteDocumentation(Doc, OS); + } +} + +} // end namespace clang diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp new file mode 100644 index 0000000..3349030 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -0,0 +1,126 @@ +//===--- ClangCommentCommandInfoEmitter.cpp - Generate command lists -----====// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits command lists and efficient matchers for command +// names that are used in documentation comments. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <vector> + +using namespace llvm; + +namespace clang { +void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("A list of commands useable in documentation " + "comments", OS); + + OS << "namespace {\n" + "const CommandInfo Commands[] = {\n"; + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Command"); + for (size_t i = 0, e = Tags.size(); i != e; ++i) { + Record &Tag = *Tags[i]; + OS << " { " + << "\"" << Tag.getValueAsString("Name") << "\", " + << "\"" << Tag.getValueAsString("EndCommandName") << "\", " + << i << ", " + << Tag.getValueAsInt("NumArgs") << ", " + << Tag.getValueAsBit("IsInlineCommand") << ", " + << Tag.getValueAsBit("IsBlockCommand") << ", " + << Tag.getValueAsBit("IsBriefCommand") << ", " + << Tag.getValueAsBit("IsReturnsCommand") << ", " + << Tag.getValueAsBit("IsParamCommand") << ", " + << Tag.getValueAsBit("IsTParamCommand") << ", " + << Tag.getValueAsBit("IsThrowsCommand") << ", " + << Tag.getValueAsBit("IsDeprecatedCommand") << ", " + << Tag.getValueAsBit("IsHeaderfileCommand") << ", " + << Tag.getValueAsBit("IsEmptyParagraphAllowed") << ", " + << Tag.getValueAsBit("IsVerbatimBlockCommand") << ", " + << Tag.getValueAsBit("IsVerbatimBlockEndCommand") << ", " + << Tag.getValueAsBit("IsVerbatimLineCommand") << ", " + << Tag.getValueAsBit("IsDeclarationCommand") << ", " + << Tag.getValueAsBit("IsFunctionDeclarationCommand") << ", " + << Tag.getValueAsBit("IsRecordLikeDetailCommand") << ", " + << Tag.getValueAsBit("IsRecordLikeDeclarationCommand") << ", " + << /* IsUnknownCommand = */ "0" + << " }"; + if (i + 1 != e) + OS << ","; + OS << "\n"; + } + OS << "};\n" + "} // unnamed namespace\n\n"; + + std::vector<StringMatcher::StringPair> Matches; + for (size_t i = 0, e = Tags.size(); i != e; ++i) { + Record &Tag = *Tags[i]; + std::string Name = Tag.getValueAsString("Name"); + std::string Return; + raw_string_ostream(Return) << "return &Commands[" << i << "];"; + Matches.emplace_back(std::move(Name), std::move(Return)); + } + + OS << "const CommandInfo *CommandTraits::getBuiltinCommandInfo(\n" + << " StringRef Name) {\n"; + StringMatcher("Name", Matches, OS).Emit(); + OS << " return NULL;\n" + << "}\n\n"; +} + +static std::string MangleName(StringRef Str) { + std::string Mangled; + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + switch (Str[i]) { + default: + Mangled += Str[i]; + break; + case '[': + Mangled += "lsquare"; + break; + case ']': + Mangled += "rsquare"; + break; + case '{': + Mangled += "lbrace"; + break; + case '}': + Mangled += "rbrace"; + break; + case '$': + Mangled += "dollar"; + break; + case '/': + Mangled += "slash"; + break; + } + } + return Mangled; +} + +void EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("A list of commands useable in documentation " + "comments", OS); + + OS << "#ifndef COMMENT_COMMAND\n" + << "# define COMMENT_COMMAND(NAME)\n" + << "#endif\n"; + + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Command"); + for (size_t i = 0, e = Tags.size(); i != e; ++i) { + Record &Tag = *Tags[i]; + std::string MangledName = MangleName(Tag.getValueAsString("Name")); + + OS << "COMMENT_COMMAND(" << MangledName << ")\n"; + } +} +} // end namespace clang + diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp new file mode 100644 index 0000000..bfdb268 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLNamedCharacterReferenceEmitter.cpp @@ -0,0 +1,85 @@ +//===--- ClangCommentHTMLNamedCharacterReferenceEmitter.cpp -----------------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits an fficient function to translate HTML named +// character references to UTF-8 sequences. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <vector> + +using namespace llvm; + +/// \brief Convert a code point to the corresponding UTF-8 sequence represented +/// as a C string literal. +/// +/// \returns true on success. +static bool translateCodePointToUTF8(unsigned CodePoint, + SmallVectorImpl<char> &CLiteral) { + char Translated[UNI_MAX_UTF8_BYTES_PER_CODE_POINT]; + char *TranslatedPtr = Translated; + if (!ConvertCodePointToUTF8(CodePoint, TranslatedPtr)) + return false; + + StringRef UTF8(Translated, TranslatedPtr - Translated); + + raw_svector_ostream OS(CLiteral); + OS << "\""; + for (size_t i = 0, e = UTF8.size(); i != e; ++i) { + OS << "\\x"; + OS.write_hex(static_cast<unsigned char>(UTF8[i])); + } + OS << "\""; + + return true; +} + +namespace clang { +void EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, + raw_ostream &OS) { + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("NCR"); + std::vector<StringMatcher::StringPair> NameToUTF8; + SmallString<32> CLiteral; + for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end(); + I != E; ++I) { + Record &Tag = **I; + std::string Spelling = Tag.getValueAsString("Spelling"); + uint64_t CodePoint = Tag.getValueAsInt("CodePoint"); + CLiteral.clear(); + CLiteral.append("return "); + if (!translateCodePointToUTF8(CodePoint, CLiteral)) { + SrcMgr.PrintMessage(Tag.getLoc().front(), + SourceMgr::DK_Error, + Twine("invalid code point")); + continue; + } + CLiteral.append(";"); + + StringMatcher::StringPair Match(Spelling, CLiteral.str()); + NameToUTF8.push_back(Match); + } + + emitSourceFileHeader("HTML named character reference to UTF-8 " + "translation", OS); + + OS << "StringRef translateHTMLNamedCharacterReferenceToUTF8(\n" + " StringRef Name) {\n"; + StringMatcher("Name", NameToUTF8, OS).Emit(); + OS << " return StringRef();\n" + << "}\n\n"; +} + +} // end namespace clang + diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp new file mode 100644 index 0000000..477bbc8 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp @@ -0,0 +1,64 @@ +//===--- ClangCommentHTMLTagsEmitter.cpp - Generate HTML tag list for Clang -=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits efficient matchers for HTML tags that are used +// in documentation comments. +// +//===----------------------------------------------------------------------===// + +#include "TableGenBackends.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <vector> + +using namespace llvm; + +void clang::EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) { + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag"); + std::vector<StringMatcher::StringPair> Matches; + for (Record *Tag : Tags) { + Matches.emplace_back(Tag->getValueAsString("Spelling"), "return true;"); + } + + emitSourceFileHeader("HTML tag name matcher", OS); + + OS << "bool isHTMLTagName(StringRef Name) {\n"; + StringMatcher("Name", Matches, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; +} + +void clang::EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, + raw_ostream &OS) { + std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag"); + std::vector<StringMatcher::StringPair> MatchesEndTagOptional; + std::vector<StringMatcher::StringPair> MatchesEndTagForbidden; + for (Record *Tag : Tags) { + std::string Spelling = Tag->getValueAsString("Spelling"); + StringMatcher::StringPair Match(Spelling, "return true;"); + if (Tag->getValueAsBit("EndTagOptional")) + MatchesEndTagOptional.push_back(Match); + if (Tag->getValueAsBit("EndTagForbidden")) + MatchesEndTagForbidden.push_back(Match); + } + + emitSourceFileHeader("HTML tag properties", OS); + + OS << "bool isHTMLEndTagOptional(StringRef Name) {\n"; + StringMatcher("Name", MatchesEndTagOptional, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; + + OS << "bool isHTMLEndTagForbidden(StringRef Name) {\n"; + StringMatcher("Name", MatchesEndTagForbidden, OS).Emit(); + OS << " return false;\n" + << "}\n\n"; +} + diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp new file mode 100644 index 0000000..efce521 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -0,0 +1,900 @@ +//=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These tablegen backends emit Clang diagnostics tables. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringToOffsetTable.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <algorithm> +#include <cctype> +#include <functional> +#include <map> +#include <set> +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Diagnostic category computation code. +//===----------------------------------------------------------------------===// + +namespace { +class DiagGroupParentMap { + RecordKeeper &Records; + std::map<const Record*, std::vector<Record*> > Mapping; +public: + DiagGroupParentMap(RecordKeeper &records) : Records(records) { + std::vector<Record*> DiagGroups + = Records.getAllDerivedDefinitions("DiagGroup"); + for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { + std::vector<Record*> SubGroups = + DiagGroups[i]->getValueAsListOfDefs("SubGroups"); + for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) + Mapping[SubGroups[j]].push_back(DiagGroups[i]); + } + } + + const std::vector<Record*> &getParents(const Record *Group) { + return Mapping[Group]; + } +}; +} // end anonymous namespace. + +static std::string +getCategoryFromDiagGroup(const Record *Group, + DiagGroupParentMap &DiagGroupParents) { + // If the DiagGroup has a category, return it. + std::string CatName = Group->getValueAsString("CategoryName"); + if (!CatName.empty()) return CatName; + + // The diag group may the subgroup of one or more other diagnostic groups, + // check these for a category as well. + const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group); + for (unsigned i = 0, e = Parents.size(); i != e; ++i) { + CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents); + if (!CatName.empty()) return CatName; + } + return ""; +} + +/// getDiagnosticCategory - Return the category that the specified diagnostic +/// lives in. +static std::string getDiagnosticCategory(const Record *R, + DiagGroupParentMap &DiagGroupParents) { + // If the diagnostic is in a group, and that group has a category, use it. + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { + // Check the diagnostic's diag group for a category. + std::string CatName = getCategoryFromDiagGroup(Group->getDef(), + DiagGroupParents); + if (!CatName.empty()) return CatName; + } + + // If the diagnostic itself has a category, get it. + return R->getValueAsString("CategoryName"); +} + +namespace { + class DiagCategoryIDMap { + RecordKeeper &Records; + StringMap<unsigned> CategoryIDs; + std::vector<std::string> CategoryStrings; + public: + DiagCategoryIDMap(RecordKeeper &records) : Records(records) { + DiagGroupParentMap ParentInfo(Records); + + // The zero'th category is "". + CategoryStrings.push_back(""); + CategoryIDs[""] = 0; + + std::vector<Record*> Diags = + Records.getAllDerivedDefinitions("Diagnostic"); + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + std::string Category = getDiagnosticCategory(Diags[i], ParentInfo); + if (Category.empty()) continue; // Skip diags with no category. + + unsigned &ID = CategoryIDs[Category]; + if (ID != 0) continue; // Already seen. + + ID = CategoryStrings.size(); + CategoryStrings.push_back(Category); + } + } + + unsigned getID(StringRef CategoryString) { + return CategoryIDs[CategoryString]; + } + + typedef std::vector<std::string>::const_iterator const_iterator; + const_iterator begin() const { return CategoryStrings.begin(); } + const_iterator end() const { return CategoryStrings.end(); } + }; + + struct GroupInfo { + std::vector<const Record*> DiagsInGroup; + std::vector<std::string> SubGroups; + unsigned IDNo; + + const Record *ExplicitDef; + + GroupInfo() : ExplicitDef(nullptr) {} + }; +} // end anonymous namespace. + +static bool beforeThanCompare(const Record *LHS, const Record *RHS) { + assert(!LHS->getLoc().empty() && !RHS->getLoc().empty()); + return + LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer(); +} + +static bool beforeThanCompareGroups(const GroupInfo *LHS, const GroupInfo *RHS){ + assert(!LHS->DiagsInGroup.empty() && !RHS->DiagsInGroup.empty()); + return beforeThanCompare(LHS->DiagsInGroup.front(), + RHS->DiagsInGroup.front()); +} + +static SMRange findSuperClassRange(const Record *R, StringRef SuperName) { + ArrayRef<Record *> Supers = R->getSuperClasses(); + + for (size_t i = 0, e = Supers.size(); i < e; ++i) + if (Supers[i]->getName() == SuperName) + return R->getSuperClassRanges()[i]; + + return SMRange(); +} + +/// \brief Invert the 1-[0/1] mapping of diags to group into a one to many +/// mapping of groups to diags in the group. +static void groupDiagnostics(const std::vector<Record*> &Diags, + const std::vector<Record*> &DiagGroups, + std::map<std::string, GroupInfo> &DiagsInGroup) { + + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + const Record *R = Diags[i]; + DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group")); + if (!DI) + continue; + assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" && + "Note can't be in a DiagGroup"); + std::string GroupName = DI->getDef()->getValueAsString("GroupName"); + DiagsInGroup[GroupName].DiagsInGroup.push_back(R); + } + + typedef SmallPtrSet<GroupInfo *, 16> GroupSetTy; + GroupSetTy ImplicitGroups; + + // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty + // groups (these are warnings that GCC supports that clang never produces). + for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { + Record *Group = DiagGroups[i]; + GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; + if (Group->isAnonymous()) { + if (GI.DiagsInGroup.size() > 1) + ImplicitGroups.insert(&GI); + } else { + if (GI.ExplicitDef) + assert(GI.ExplicitDef == Group); + else + GI.ExplicitDef = Group; + } + + std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups"); + for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) + GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName")); + } + + // Assign unique ID numbers to the groups. + unsigned IDNo = 0; + for (std::map<std::string, GroupInfo>::iterator + I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo) + I->second.IDNo = IDNo; + + // Sort the implicit groups, so we can warn about them deterministically. + SmallVector<GroupInfo *, 16> SortedGroups(ImplicitGroups.begin(), + ImplicitGroups.end()); + for (SmallVectorImpl<GroupInfo *>::iterator I = SortedGroups.begin(), + E = SortedGroups.end(); + I != E; ++I) { + MutableArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup; + std::sort(GroupDiags.begin(), GroupDiags.end(), beforeThanCompare); + } + std::sort(SortedGroups.begin(), SortedGroups.end(), beforeThanCompareGroups); + + // Warn about the same group being used anonymously in multiple places. + for (SmallVectorImpl<GroupInfo *>::const_iterator I = SortedGroups.begin(), + E = SortedGroups.end(); + I != E; ++I) { + ArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup; + + if ((*I)->ExplicitDef) { + std::string Name = (*I)->ExplicitDef->getValueAsString("GroupName"); + for (ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(), + DE = GroupDiags.end(); + DI != DE; ++DI) { + const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group")); + const Record *NextDiagGroup = GroupInit->getDef(); + if (NextDiagGroup == (*I)->ExplicitDef) + continue; + + SMRange InGroupRange = findSuperClassRange(*DI, "InGroup"); + SmallString<64> Replacement; + if (InGroupRange.isValid()) { + Replacement += "InGroup<"; + Replacement += (*I)->ExplicitDef->getName(); + Replacement += ">"; + } + SMFixIt FixIt(InGroupRange, Replacement); + + SrcMgr.PrintMessage(NextDiagGroup->getLoc().front(), + SourceMgr::DK_Error, + Twine("group '") + Name + + "' is referred to anonymously", + None, + InGroupRange.isValid() ? FixIt + : ArrayRef<SMFixIt>()); + SrcMgr.PrintMessage((*I)->ExplicitDef->getLoc().front(), + SourceMgr::DK_Note, "group defined here"); + } + } else { + // If there's no existing named group, we should just warn once and use + // notes to list all the other cases. + ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(), + DE = GroupDiags.end(); + assert(DI != DE && "We only care about groups with multiple uses!"); + + const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group")); + const Record *NextDiagGroup = GroupInit->getDef(); + std::string Name = NextDiagGroup->getValueAsString("GroupName"); + + SMRange InGroupRange = findSuperClassRange(*DI, "InGroup"); + SrcMgr.PrintMessage(NextDiagGroup->getLoc().front(), + SourceMgr::DK_Error, + Twine("group '") + Name + + "' is referred to anonymously", + InGroupRange); + + for (++DI; DI != DE; ++DI) { + GroupInit = cast<DefInit>((*DI)->getValueInit("Group")); + InGroupRange = findSuperClassRange(*DI, "InGroup"); + SrcMgr.PrintMessage(GroupInit->getDef()->getLoc().front(), + SourceMgr::DK_Note, "also referenced here", + InGroupRange); + } + } + } +} + +//===----------------------------------------------------------------------===// +// Infer members of -Wpedantic. +//===----------------------------------------------------------------------===// + +typedef std::vector<const Record *> RecordVec; +typedef llvm::DenseSet<const Record *> RecordSet; +typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet; + +namespace { +class InferPedantic { + typedef llvm::DenseMap<const Record*, + std::pair<unsigned, Optional<unsigned> > > GMap; + + DiagGroupParentMap &DiagGroupParents; + const std::vector<Record*> &Diags; + const std::vector<Record*> DiagGroups; + std::map<std::string, GroupInfo> &DiagsInGroup; + llvm::DenseSet<const Record*> DiagsSet; + GMap GroupCount; +public: + InferPedantic(DiagGroupParentMap &DiagGroupParents, + const std::vector<Record*> &Diags, + const std::vector<Record*> &DiagGroups, + std::map<std::string, GroupInfo> &DiagsInGroup) + : DiagGroupParents(DiagGroupParents), + Diags(Diags), + DiagGroups(DiagGroups), + DiagsInGroup(DiagsInGroup) {} + + /// Compute the set of diagnostics and groups that are immediately + /// in -Wpedantic. + void compute(VecOrSet DiagsInPedantic, + VecOrSet GroupsInPedantic); + +private: + /// Determine whether a group is a subgroup of another group. + bool isSubGroupOfGroup(const Record *Group, + llvm::StringRef RootGroupName); + + /// Determine if the diagnostic is an extension. + bool isExtension(const Record *Diag); + + /// Determine if the diagnostic is off by default. + bool isOffByDefault(const Record *Diag); + + /// Increment the count for a group, and transitively marked + /// parent groups when appropriate. + void markGroup(const Record *Group); + + /// Return true if the diagnostic is in a pedantic group. + bool groupInPedantic(const Record *Group, bool increment = false); +}; +} // end anonymous namespace + +bool InferPedantic::isSubGroupOfGroup(const Record *Group, + llvm::StringRef GName) { + + const std::string &GroupName = Group->getValueAsString("GroupName"); + if (GName == GroupName) + return true; + + const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group); + for (unsigned i = 0, e = Parents.size(); i != e; ++i) + if (isSubGroupOfGroup(Parents[i], GName)) + return true; + + return false; +} + +/// Determine if the diagnostic is an extension. +bool InferPedantic::isExtension(const Record *Diag) { + const std::string &ClsName = Diag->getValueAsDef("Class")->getName(); + return ClsName == "CLASS_EXTENSION"; +} + +bool InferPedantic::isOffByDefault(const Record *Diag) { + const std::string &DefSeverity = + Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"); + return DefSeverity == "Ignored"; +} + +bool InferPedantic::groupInPedantic(const Record *Group, bool increment) { + GMap::mapped_type &V = GroupCount[Group]; + // Lazily compute the threshold value for the group count. + if (!V.second.hasValue()) { + const GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; + V.second = GI.SubGroups.size() + GI.DiagsInGroup.size(); + } + + if (increment) + ++V.first; + + // 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(); +} + +void InferPedantic::markGroup(const Record *Group) { + // If all the diagnostics and subgroups have been marked as being + // covered by -Wpedantic, increment the count of parent groups. Once the + // group's count is equal to the number of subgroups and diagnostics in + // that group, we can safely add this group to -Wpedantic. + if (groupInPedantic(Group, /* increment */ true)) { + const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group); + for (unsigned i = 0, e = Parents.size(); i != e; ++i) + markGroup(Parents[i]); + } +} + +void InferPedantic::compute(VecOrSet DiagsInPedantic, + VecOrSet GroupsInPedantic) { + // All extensions that are not on by default are implicitly in the + // "pedantic" group. For those that aren't explicitly included in -Wpedantic, + // mark them for consideration to be included in -Wpedantic directly. + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + Record *R = Diags[i]; + if (isExtension(R) && isOffByDefault(R)) { + DiagsSet.insert(R); + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) { + const Record *GroupRec = Group->getDef(); + if (!isSubGroupOfGroup(GroupRec, "pedantic")) { + markGroup(GroupRec); + } + } + } + } + + // Compute the set of diagnostics that are directly in -Wpedantic. We + // march through Diags a second time to ensure the results are emitted + // in deterministic order. + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + Record *R = Diags[i]; + if (!DiagsSet.count(R)) + continue; + // Check if the group is implicitly in -Wpedantic. If so, + // the diagnostic should not be directly included in the -Wpedantic + // diagnostic group. + if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) + if (groupInPedantic(Group->getDef())) + continue; + + // The diagnostic is not included in a group that is (transitively) in + // -Wpedantic. Include it in -Wpedantic directly. + if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>()) + V->push_back(R); + else { + DiagsInPedantic.get<RecordSet*>()->insert(R); + } + } + + if (!GroupsInPedantic) + return; + + // Compute the set of groups that are directly in -Wpedantic. We + // march through the groups to ensure the results are emitted + /// in a deterministc order. + for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) { + Record *Group = DiagGroups[i]; + 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; + } + // 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()) + continue; + + if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>()) + V->push_back(Group); + else { + GroupsInPedantic.get<RecordSet*>()->insert(Group); + } + } +} + +//===----------------------------------------------------------------------===// +// Warning Tables (.inc file) generation. +//===----------------------------------------------------------------------===// + +static bool isError(const Record &Diag) { + const std::string &ClsName = Diag.getValueAsDef("Class")->getName(); + return ClsName == "CLASS_ERROR"; +} + +static bool isRemark(const Record &Diag) { + const std::string &ClsName = Diag.getValueAsDef("Class")->getName(); + return ClsName == "CLASS_REMARK"; +} + +/// ClangDiagsDefsEmitter - The top-level class emits .def files containing +/// declarations of Clang diagnostics. +namespace clang { +void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, + const std::string &Component) { + // Write the #if guard + if (!Component.empty()) { + std::string ComponentName = StringRef(Component).upper(); + OS << "#ifdef " << ComponentName << "START\n"; + OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName + << ",\n"; + OS << "#undef " << ComponentName << "START\n"; + OS << "#endif\n\n"; + } + + const std::vector<Record*> &Diags = + Records.getAllDerivedDefinitions("Diagnostic"); + + std::vector<Record*> DiagGroups + = Records.getAllDerivedDefinitions("DiagGroup"); + + std::map<std::string, GroupInfo> DiagsInGroup; + groupDiagnostics(Diags, DiagGroups, DiagsInGroup); + + DiagCategoryIDMap CategoryIDs(Records); + DiagGroupParentMap DGParentMap(Records); + + // Compute the set of diagnostics that are in -Wpedantic. + RecordSet DiagsInPedantic; + InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); + inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr); + + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + const Record &R = *Diags[i]; + + // Check if this is an error that is accidentally in a warning + // group. + if (isError(R)) { + if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) { + const Record *GroupRec = Group->getDef(); + const std::string &GroupName = GroupRec->getValueAsString("GroupName"); + PrintFatalError(R.getLoc(), "Error " + R.getName() + + " cannot be in a warning group [" + GroupName + "]"); + } + } + + // Check that all remarks have an associated diagnostic group. + if (isRemark(R)) { + if (!isa<DefInit>(R.getValueInit("Group"))) { + PrintFatalError(R.getLoc(), "Error " + R.getName() + + " not in any diagnostic group"); + } + } + + // Filter by component. + if (!Component.empty() && Component != R.getValueAsString("Component")) + continue; + + OS << "DIAG(" << R.getName() << ", "; + OS << R.getValueAsDef("Class")->getName(); + OS << ", (unsigned)diag::Severity::" + << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name"); + + // Description string. + OS << ", \""; + OS.write_escaped(R.getValueAsString("Text")) << '"'; + + // Warning associated with the diagnostic. This is stored as an index into + // the alphabetically sorted warning table. + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) { + std::map<std::string, GroupInfo>::iterator I = + DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName")); + assert(I != DiagsInGroup.end()); + OS << ", " << I->second.IDNo; + } else if (DiagsInPedantic.count(&R)) { + std::map<std::string, GroupInfo>::iterator I = + DiagsInGroup.find("pedantic"); + assert(I != DiagsInGroup.end() && "pedantic group not defined"); + OS << ", " << I->second.IDNo; + } else { + OS << ", 0"; + } + + // SFINAE response. + OS << ", " << R.getValueAsDef("SFINAE")->getName(); + + // Default warning has no Werror bit. + if (R.getValueAsBit("WarningNoWerror")) + OS << ", true"; + else + OS << ", false"; + + if (R.getValueAsBit("ShowInSystemHeader")) + OS << ", true"; + else + OS << ", false"; + + // Category number. + OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); + OS << ")\n"; + } +} +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Warning Group Tables generation +//===----------------------------------------------------------------------===// + +static std::string getDiagCategoryEnum(llvm::StringRef name) { + if (name.empty()) + return "DiagCat_None"; + SmallString<256> enumName = llvm::StringRef("DiagCat_"); + for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I) + enumName += isalnum(*I) ? *I : '_'; + return enumName.str(); +} + +/// \brief Emit the array of diagnostic subgroups. +/// +/// The array of diagnostic subgroups contains for each group a list of its +/// subgroups. The individual lists are separated by '-1'. Groups with no +/// subgroups are skipped. +/// +/// \code +/// static const int16_t DiagSubGroups[] = { +/// /* Empty */ -1, +/// /* DiagSubGroup0 */ 142, -1, +/// /* DiagSubGroup13 */ 265, 322, 399, -1 +/// } +/// \endcode +/// +static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup, + RecordVec &GroupsInPedantic, raw_ostream &OS) { + OS << "static const int16_t DiagSubGroups[] = {\n" + << " /* Empty */ -1,\n"; + for (auto const &I : DiagsInGroup) { + const bool IsPedantic = I.first == "pedantic"; + + const std::vector<std::string> &SubGroups = I.second.SubGroups; + if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) { + OS << " /* DiagSubGroup" << I.second.IDNo << " */ "; + for (auto const &SubGroup : SubGroups) { + std::map<std::string, GroupInfo>::const_iterator RI = + DiagsInGroup.find(SubGroup); + assert(RI != DiagsInGroup.end() && "Referenced without existing?"); + OS << RI->second.IDNo << ", "; + } + // Emit the groups implicitly in "pedantic". + if (IsPedantic) { + for (auto const &Group : GroupsInPedantic) { + const std::string &GroupName = Group->getValueAsString("GroupName"); + std::map<std::string, GroupInfo>::const_iterator RI = + DiagsInGroup.find(GroupName); + assert(RI != DiagsInGroup.end() && "Referenced without existing?"); + OS << RI->second.IDNo << ", "; + } + } + + OS << "-1,\n"; + } + } + OS << "};\n\n"; +} + +/// \brief Emit the list of diagnostic arrays. +/// +/// This data structure is a large array that contains itself arrays of varying +/// size. Each array represents a list of diagnostics. The different arrays are +/// separated by the value '-1'. +/// +/// \code +/// static const int16_t DiagArrays[] = { +/// /* Empty */ -1, +/// /* DiagArray1 */ diag::warn_pragma_message, +/// -1, +/// /* DiagArray2 */ diag::warn_abs_too_small, +/// diag::warn_unsigned_abs, +/// diag::warn_wrong_absolute_value_type, +/// -1 +/// }; +/// \endcode +/// +static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup, + RecordVec &DiagsInPedantic, raw_ostream &OS) { + OS << "static const int16_t DiagArrays[] = {\n" + << " /* Empty */ -1,\n"; + for (auto const &I : DiagsInGroup) { + const bool IsPedantic = I.first == "pedantic"; + + const std::vector<const Record *> &V = I.second.DiagsInGroup; + if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) { + OS << " /* DiagArray" << I.second.IDNo << " */ "; + for (auto *Record : V) + OS << "diag::" << Record->getName() << ", "; + // Emit the diagnostics implicitly in "pedantic". + if (IsPedantic) { + for (auto const &Diag : DiagsInPedantic) + OS << "diag::" << Diag->getName() << ", "; + } + OS << "-1,\n"; + } + } + OS << "};\n\n"; +} + +/// \brief Emit a list of group names. +/// +/// This creates a long string which by itself contains a list of pascal style +/// strings, which consist of a length byte directly followed by the string. +/// +/// \code +/// static const char DiagGroupNames[] = { +/// \000\020#pragma-messages\t#warnings\020CFString-literal" +/// }; +/// \endcode +static void emitDiagGroupNames(StringToOffsetTable &GroupNames, + raw_ostream &OS) { + OS << "static const char DiagGroupNames[] = {\n"; + GroupNames.EmitString(OS); + OS << "};\n\n"; +} + +/// \brief Emit diagnostic arrays and related data structures. +/// +/// This creates the actual diagnostic array, an array of diagnostic subgroups +/// and an array of subgroup names. +/// +/// \code +/// #ifdef GET_DIAG_ARRAYS +/// static const int16_t DiagArrays[]; +/// static const int16_t DiagSubGroups[]; +/// static const char DiagGroupNames[]; +/// #endif +/// \endcode +static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup, + RecordVec &DiagsInPedantic, + RecordVec &GroupsInPedantic, + StringToOffsetTable &GroupNames, + raw_ostream &OS) { + OS << "\n#ifdef GET_DIAG_ARRAYS\n"; + emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS); + emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS); + emitDiagGroupNames(GroupNames, OS); + OS << "#endif // GET_DIAG_ARRAYS\n\n"; +} + +/// \brief Emit diagnostic table. +/// +/// The table is sorted by the name of the diagnostic group. Each element +/// consists of the name of the diagnostic group (given as offset in the +/// group name table), a reference to a list of diagnostics (optional) and a +/// reference to a set of subgroups (optional). +/// +/// \code +/// #ifdef GET_DIAG_TABLE +/// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0}, +/// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0}, +/// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3}, +/// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9}, +/// #endif +/// \endcode +static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup, + RecordVec &DiagsInPedantic, + RecordVec &GroupsInPedantic, + StringToOffsetTable &GroupNames, raw_ostream &OS) { + unsigned MaxLen = 0; + + for (auto const &I: DiagsInGroup) + MaxLen = std::max(MaxLen, (unsigned)I.first.size()); + + OS << "\n#ifdef GET_DIAG_TABLE\n"; + unsigned SubGroupIndex = 1, DiagArrayIndex = 1; + for (auto const &I: DiagsInGroup) { + // Group option string. + OS << " { /* "; + 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(), ' '); + // 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) << ", "; + + // Special handling for 'pedantic'. + const bool IsPedantic = I.first == "pedantic"; + + // Diagnostics in the group. + const std::vector<const Record *> &V = I.second.DiagsInGroup; + const bool hasDiags = + !V.empty() || (IsPedantic && !DiagsInPedantic.empty()); + if (hasDiags) { + OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex + << ", "; + if (IsPedantic) + DiagArrayIndex += DiagsInPedantic.size(); + DiagArrayIndex += V.size() + 1; + } else { + OS << "/* Empty */ 0, "; + } + + // Subgroups. + const std::vector<std::string> &SubGroups = I.second.SubGroups; + const bool hasSubGroups = + !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty()); + if (hasSubGroups) { + OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex; + if (IsPedantic) + SubGroupIndex += GroupsInPedantic.size(); + SubGroupIndex += SubGroups.size() + 1; + } else { + OS << "/* Empty */ 0"; + } + + OS << " },\n"; + } + OS << "#endif // GET_DIAG_TABLE\n\n"; +} + +/// \brief Emit the table of diagnostic categories. +/// +/// The table has the form of macro calls that have two parameters. The +/// category's name as well as an enum that represents the category. The +/// table can be used by defining the macro 'CATEGORY' and including this +/// table right after. +/// +/// \code +/// #ifdef GET_CATEGORY_TABLE +/// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue) +/// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue) +/// #endif +/// \endcode +static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) { + DiagCategoryIDMap CategoriesByID(Records); + OS << "\n#ifdef GET_CATEGORY_TABLE\n"; + for (auto const &C : CategoriesByID) + OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n"; + OS << "#endif // GET_CATEGORY_TABLE\n\n"; +} + +namespace clang { +void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) { + // Compute a mapping from a DiagGroup to all of its parents. + DiagGroupParentMap DGParentMap(Records); + + std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic"); + + std::vector<Record *> DiagGroups = + Records.getAllDerivedDefinitions("DiagGroup"); + + std::map<std::string, GroupInfo> DiagsInGroup; + groupDiagnostics(Diags, DiagGroups, DiagsInGroup); + + // All extensions are implicitly in the "pedantic" group. Record the + // implicit set of groups in the "pedantic" group, and use this information + // later when emitting the group information for Pedantic. + RecordVec DiagsInPedantic; + RecordVec GroupsInPedantic; + InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); + inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic); + + StringToOffsetTable GroupNames; + for (std::map<std::string, GroupInfo>::const_iterator + I = DiagsInGroup.begin(), + E = DiagsInGroup.end(); + I != E; ++I) { + // Store a pascal-style length byte at the beginning of the string. + std::string Name = char(I->first.size()) + I->first; + GroupNames.GetOrAddStringOffset(Name, false); + } + + emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, + OS); + emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, + OS); + emitCategoryTable(Records, OS); +} +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Diagnostic name index generation +//===----------------------------------------------------------------------===// + +namespace { +struct RecordIndexElement +{ + RecordIndexElement() {} + explicit RecordIndexElement(Record const &R): + Name(R.getName()) {} + + std::string Name; +}; +} // end anonymous namespace. + +namespace clang { +void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) { + const std::vector<Record*> &Diags = + Records.getAllDerivedDefinitions("Diagnostic"); + + std::vector<RecordIndexElement> Index; + Index.reserve(Diags.size()); + for (unsigned i = 0, e = Diags.size(); i != e; ++i) { + const Record &R = *(Diags[i]); + Index.push_back(RecordIndexElement(R)); + } + + std::sort(Index.begin(), Index.end(), + [](const RecordIndexElement &Lhs, + const RecordIndexElement &Rhs) { return Lhs.Name < Rhs.Name; }); + + for (unsigned i = 0, e = Index.size(); i != e; ++i) { + const RecordIndexElement &R = Index[i]; + + OS << "DIAG_NAME_INDEX(" << R.Name << ")\n"; + } +} +} // end namespace clang diff --git a/contrib/llvm/tools/clang/utils/TableGen/ClangSACheckersEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/ClangSACheckersEmitter.cpp new file mode 100644 index 0000000..115527a --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/ClangSACheckersEmitter.cpp @@ -0,0 +1,322 @@ +//=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits Clang Static Analyzer checkers tables. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <map> +#include <string> +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Static Analyzer Checkers Tables generation +//===----------------------------------------------------------------------===// + +/// \brief True if it is specified hidden or a parent package is specified +/// as hidden, otherwise false. +static bool isHidden(const Record &R) { + if (R.getValueAsBit("Hidden")) + return true; + // Not declared as hidden, check the parent package if it is hidden. + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("ParentPackage"))) + return isHidden(*DI->getDef()); + + return false; +} + +static bool isCheckerNamed(const Record *R) { + return !R->getValueAsString("CheckerName").empty(); +} + +static std::string getPackageFullName(const Record *R); + +static std::string getParentPackageFullName(const Record *R) { + std::string name; + if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) + name = getPackageFullName(DI->getDef()); + return name; +} + +static std::string getPackageFullName(const Record *R) { + std::string name = getParentPackageFullName(R); + if (!name.empty()) name += "."; + return name + R->getValueAsString("PackageName"); +} + +static std::string getCheckerFullName(const Record *R) { + std::string name = getParentPackageFullName(R); + if (isCheckerNamed(R)) { + if (!name.empty()) name += "."; + name += R->getValueAsString("CheckerName"); + } + return name; +} + +static std::string getStringValue(const Record &R, StringRef field) { + if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field))) + return SI->getValue(); + return std::string(); +} + +namespace { +struct GroupInfo { + llvm::DenseSet<const Record*> Checkers; + llvm::DenseSet<const Record *> SubGroups; + bool Hidden; + unsigned Index; + + GroupInfo() : Hidden(false) { } +}; +} + +static void addPackageToCheckerGroup(const Record *package, const Record *group, + llvm::DenseMap<const Record *, GroupInfo *> &recordGroupMap) { + llvm::DenseSet<const Record *> &checkers = recordGroupMap[package]->Checkers; + for (llvm::DenseSet<const Record *>::iterator + I = checkers.begin(), E = checkers.end(); I != E; ++I) + recordGroupMap[group]->Checkers.insert(*I); + + llvm::DenseSet<const Record *> &subGroups = recordGroupMap[package]->SubGroups; + for (llvm::DenseSet<const Record *>::iterator + I = subGroups.begin(), E = subGroups.end(); I != E; ++I) + addPackageToCheckerGroup(*I, group, recordGroupMap); +} + +namespace clang { +void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { + std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); + llvm::DenseMap<const Record *, unsigned> checkerRecIndexMap; + for (unsigned i = 0, e = checkers.size(); i != e; ++i) + checkerRecIndexMap[checkers[i]] = i; + + // Invert the mapping of checkers to package/group into a one to many + // mapping of packages/groups to checkers. + std::map<std::string, GroupInfo> groupInfoByName; + llvm::DenseMap<const Record *, GroupInfo *> recordGroupMap; + + std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package"); + for (unsigned i = 0, e = packages.size(); i != e; ++i) { + Record *R = packages[i]; + std::string fullName = getPackageFullName(R); + if (!fullName.empty()) { + GroupInfo &info = groupInfoByName[fullName]; + info.Hidden = isHidden(*R); + recordGroupMap[R] = &info; + } + } + + std::vector<Record*> + checkerGroups = Records.getAllDerivedDefinitions("CheckerGroup"); + for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) { + Record *R = checkerGroups[i]; + std::string name = R->getValueAsString("GroupName"); + if (!name.empty()) { + GroupInfo &info = groupInfoByName[name]; + recordGroupMap[R] = &info; + } + } + + for (unsigned i = 0, e = checkers.size(); i != e; ++i) { + Record *R = checkers[i]; + Record *package = nullptr; + if (DefInit * + DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) + package = DI->getDef(); + if (!isCheckerNamed(R) && !package) + PrintFatalError(R->getLoc(), "Checker '" + R->getName() + + "' is neither named, nor in a package!"); + + if (isCheckerNamed(R)) { + // Create a pseudo-group to hold this checker. + std::string fullName = getCheckerFullName(R); + GroupInfo &info = groupInfoByName[fullName]; + info.Hidden = R->getValueAsBit("Hidden"); + recordGroupMap[R] = &info; + info.Checkers.insert(R); + } else { + recordGroupMap[package]->Checkers.insert(R); + } + + Record *currR = isCheckerNamed(R) ? R : package; + // Insert the checker and its parent packages into the subgroups set of + // the corresponding parent package. + while (DefInit *DI + = dyn_cast<DefInit>(currR->getValueInit("ParentPackage"))) { + Record *parentPackage = DI->getDef(); + recordGroupMap[parentPackage]->SubGroups.insert(currR); + currR = parentPackage; + } + // Insert the checker into the set of its group. + if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"))) + recordGroupMap[DI->getDef()]->Checkers.insert(R); + } + + // If a package is in group, add all its checkers and its sub-packages + // checkers into the group. + for (unsigned i = 0, e = packages.size(); i != e; ++i) + if (DefInit *DI = dyn_cast<DefInit>(packages[i]->getValueInit("Group"))) + addPackageToCheckerGroup(packages[i], DI->getDef(), recordGroupMap); + + typedef std::map<std::string, const Record *> SortedRecords; + typedef llvm::DenseMap<const Record *, unsigned> RecToSortIndex; + + SortedRecords sortedGroups; + RecToSortIndex groupToSortIndex; + OS << "\n#ifdef GET_GROUPS\n"; + { + for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) + sortedGroups[checkerGroups[i]->getValueAsString("GroupName")] + = checkerGroups[i]; + + unsigned sortIndex = 0; + for (SortedRecords::iterator + I = sortedGroups.begin(), E = sortedGroups.end(); I != E; ++I) { + const Record *R = I->second; + + OS << "GROUP(" << "\""; + OS.write_escaped(R->getValueAsString("GroupName")) << "\""; + OS << ")\n"; + + groupToSortIndex[R] = sortIndex++; + } + } + OS << "#endif // GET_GROUPS\n\n"; + + OS << "\n#ifdef GET_PACKAGES\n"; + { + SortedRecords sortedPackages; + for (unsigned i = 0, e = packages.size(); i != e; ++i) + sortedPackages[getPackageFullName(packages[i])] = packages[i]; + + for (SortedRecords::iterator + I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { + const Record &R = *I->second; + + OS << "PACKAGE(" << "\""; + OS.write_escaped(getPackageFullName(&R)) << "\", "; + // Group index + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) + OS << groupToSortIndex[DI->getDef()] << ", "; + else + OS << "-1, "; + // Hidden bit + if (isHidden(R)) + OS << "true"; + else + OS << "false"; + OS << ")\n"; + } + } + OS << "#endif // GET_PACKAGES\n\n"; + + OS << "\n#ifdef GET_CHECKERS\n"; + for (unsigned i = 0, e = checkers.size(); i != e; ++i) { + const Record &R = *checkers[i]; + + OS << "CHECKER(" << "\""; + std::string name; + if (isCheckerNamed(&R)) + name = getCheckerFullName(&R); + OS.write_escaped(name) << "\", "; + OS << R.getName() << ", "; + OS << getStringValue(R, "DescFile") << ", "; + OS << "\""; + OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; + // Group index + if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) + OS << groupToSortIndex[DI->getDef()] << ", "; + else + OS << "-1, "; + // Hidden bit + if (isHidden(R)) + OS << "true"; + else + OS << "false"; + OS << ")\n"; + } + OS << "#endif // GET_CHECKERS\n\n"; + + unsigned index = 0; + for (std::map<std::string, GroupInfo>::iterator + I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) + I->second.Index = index++; + + // Walk through the packages/groups/checkers emitting an array for each + // set of checkers and an array for each set of subpackages. + + OS << "\n#ifdef GET_MEMBER_ARRAYS\n"; + unsigned maxLen = 0; + for (std::map<std::string, GroupInfo>::iterator + I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { + maxLen = std::max(maxLen, (unsigned)I->first.size()); + + llvm::DenseSet<const Record *> &checkers = I->second.Checkers; + if (!checkers.empty()) { + OS << "static const short CheckerArray" << I->second.Index << "[] = { "; + // Make the output order deterministic. + std::map<int, const Record *> sorted; + for (llvm::DenseSet<const Record *>::iterator + I = checkers.begin(), E = checkers.end(); I != E; ++I) + sorted[(*I)->getID()] = *I; + + for (std::map<int, const Record *>::iterator + I = sorted.begin(), E = sorted.end(); I != E; ++I) + OS << checkerRecIndexMap[I->second] << ", "; + OS << "-1 };\n"; + } + + llvm::DenseSet<const Record *> &subGroups = I->second.SubGroups; + if (!subGroups.empty()) { + OS << "static const short SubPackageArray" << I->second.Index << "[] = { "; + // Make the output order deterministic. + std::map<int, const Record *> sorted; + for (llvm::DenseSet<const Record *>::iterator + I = subGroups.begin(), E = subGroups.end(); I != E; ++I) + sorted[(*I)->getID()] = *I; + + for (std::map<int, const Record *>::iterator + I = sorted.begin(), E = sorted.end(); I != E; ++I) { + OS << recordGroupMap[I->second]->Index << ", "; + } + OS << "-1 };\n"; + } + } + OS << "#endif // GET_MEMBER_ARRAYS\n\n"; + + OS << "\n#ifdef GET_CHECKNAME_TABLE\n"; + for (std::map<std::string, GroupInfo>::iterator + I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { + // Group option string. + OS << " { \""; + OS.write_escaped(I->first) << "\"," + << std::string(maxLen-I->first.size()+1, ' '); + + if (I->second.Checkers.empty()) + OS << "0, "; + else + OS << "CheckerArray" << I->second.Index << ", "; + + // Subgroups. + if (I->second.SubGroups.empty()) + OS << "0, "; + else + OS << "SubPackageArray" << I->second.Index << ", "; + + OS << (I->second.Hidden ? "true" : "false"); + + OS << " },\n"; + } + OS << "#endif // GET_CHECKNAME_TABLE\n\n"; +} +} // end namespace clang diff --git a/contrib/llvm/tools/clang/utils/TableGen/NeonEmitter.cpp b/contrib/llvm/tools/clang/utils/TableGen/NeonEmitter.cpp new file mode 100644 index 0000000..7644ae2 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/NeonEmitter.cpp @@ -0,0 +1,2404 @@ +//===- NeonEmitter.cpp - Generate arm_neon.h for use with clang -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend is responsible for emitting arm_neon.h, which includes +// a declaration and definition of each function specified by the ARM NEON +// compiler interface. See ARM document DUI0348B. +// +// Each NEON instruction is implemented in terms of 1 or more functions which +// are suffixed with the element type of the input vectors. Functions may be +// implemented in terms of generic vector operations such as +, *, -, etc. or +// by calling a __builtin_-prefixed function which will be handled by clang's +// CodeGen library. +// +// Additional validation code can be generated by this file when runHeader() is +// called, rather than the normal run() entry point. +// +// See also the documentation in include/clang/Basic/arm_neon.td. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/SetTheory.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <algorithm> +#include <map> +#include <sstream> +#include <string> +#include <vector> +using namespace llvm; + +namespace { + +// While globals are generally bad, this one allows us to perform assertions +// liberally and somehow still trace them back to the def they indirectly +// came from. +static Record *CurrentRecord = nullptr; +static void assert_with_loc(bool Assertion, const std::string &Str) { + if (!Assertion) { + if (CurrentRecord) + PrintFatalError(CurrentRecord->getLoc(), Str); + else + PrintFatalError(Str); + } +} + +enum ClassKind { + ClassNone, + ClassI, // generic integer instruction, e.g., "i8" suffix + ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix + ClassW, // width-specific instruction, e.g., "8" suffix + ClassB, // bitcast arguments with enum argument to specify type + ClassL, // Logical instructions which are op instructions + // but we need to not emit any suffix for in our + // tests. + ClassNoTest // Instructions which we do not test since they are + // not TRUE instructions. +}; + +/// NeonTypeFlags - Flags to identify the types for overloaded Neon +/// builtins. These must be kept in sync with the flags in +/// include/clang/Basic/TargetBuiltins.h. +namespace NeonTypeFlags { +enum { EltTypeMask = 0xf, UnsignedFlag = 0x10, QuadFlag = 0x20 }; + +enum EltType { + Int8, + Int16, + Int32, + Int64, + Poly8, + Poly16, + Poly64, + Poly128, + Float16, + Float32, + Float64 +}; +} + +class Intrinsic; +class NeonEmitter; +class Type; +class Variable; + +//===----------------------------------------------------------------------===// +// TypeSpec +//===----------------------------------------------------------------------===// + +/// A TypeSpec is just a simple wrapper around a string, but gets its own type +/// for strong typing purposes. +/// +/// A TypeSpec can be used to create a type. +class TypeSpec : public std::string { +public: + static std::vector<TypeSpec> fromTypeSpecs(StringRef Str) { + std::vector<TypeSpec> Ret; + TypeSpec Acc; + for (char I : Str.str()) { + if (islower(I)) { + Acc.push_back(I); + Ret.push_back(TypeSpec(Acc)); + Acc.clear(); + } else { + Acc.push_back(I); + } + } + return Ret; + } +}; + +//===----------------------------------------------------------------------===// +// Type +//===----------------------------------------------------------------------===// + +/// A Type. Not much more to say here. +class Type { +private: + TypeSpec TS; + + bool Float, Signed, Immediate, Void, Poly, Constant, Pointer; + // ScalarForMangling and NoManglingQ are really not suited to live here as + // they are not related to the type. But they live in the TypeSpec (not the + // prototype), so this is really the only place to store them. + bool ScalarForMangling, NoManglingQ; + unsigned Bitwidth, ElementBitwidth, NumVectors; + +public: + Type() + : Float(false), Signed(false), Immediate(false), Void(true), Poly(false), + Constant(false), Pointer(false), ScalarForMangling(false), + NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) {} + + Type(TypeSpec TS, char CharMod) + : TS(TS), Float(false), Signed(false), Immediate(false), Void(false), + Poly(false), Constant(false), Pointer(false), ScalarForMangling(false), + NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) { + applyModifier(CharMod); + } + + /// Returns a type representing "void". + static Type getVoid() { return Type(); } + + bool operator==(const Type &Other) const { return str() == Other.str(); } + bool operator!=(const Type &Other) const { return !operator==(Other); } + + // + // Query functions + // + bool isScalarForMangling() const { return ScalarForMangling; } + bool noManglingQ() const { return NoManglingQ; } + + bool isPointer() const { return Pointer; } + bool isFloating() const { return Float; } + bool isInteger() const { return !Float && !Poly; } + bool isSigned() const { return Signed; } + bool isImmediate() const { return Immediate; } + bool isScalar() const { return NumVectors == 0; } + bool isVector() const { return NumVectors > 0; } + bool isFloat() const { return Float && ElementBitwidth == 32; } + bool isDouble() const { return Float && ElementBitwidth == 64; } + bool isHalf() const { return Float && ElementBitwidth == 16; } + bool isPoly() const { return Poly; } + bool isChar() const { return ElementBitwidth == 8; } + bool isShort() const { return !Float && ElementBitwidth == 16; } + bool isInt() const { return !Float && ElementBitwidth == 32; } + bool isLong() const { return !Float && ElementBitwidth == 64; } + bool isVoid() const { return Void; } + unsigned getNumElements() const { return Bitwidth / ElementBitwidth; } + unsigned getSizeInBits() const { return Bitwidth; } + unsigned getElementSizeInBits() const { return ElementBitwidth; } + unsigned getNumVectors() const { return NumVectors; } + + // + // Mutator functions + // + void makeUnsigned() { Signed = false; } + void makeSigned() { Signed = true; } + void makeInteger(unsigned ElemWidth, bool Sign) { + Float = false; + Poly = false; + Signed = Sign; + Immediate = false; + ElementBitwidth = ElemWidth; + } + void makeImmediate(unsigned ElemWidth) { + Float = false; + Poly = false; + Signed = true; + Immediate = true; + ElementBitwidth = ElemWidth; + } + void makeScalar() { + Bitwidth = ElementBitwidth; + NumVectors = 0; + } + void makeOneVector() { + assert(isVector()); + NumVectors = 1; + } + void doubleLanes() { + assert_with_loc(Bitwidth != 128, "Can't get bigger than 128!"); + Bitwidth = 128; + } + void halveLanes() { + assert_with_loc(Bitwidth != 64, "Can't get smaller than 64!"); + Bitwidth = 64; + } + + /// Return the C string representation of a type, which is the typename + /// defined in stdint.h or arm_neon.h. + std::string str() const; + + /// Return the string representation of a type, which is an encoded + /// string for passing to the BUILTIN() macro in Builtins.def. + std::string builtin_str() const; + + /// Return the value in NeonTypeFlags for this type. + unsigned getNeonEnum() const; + + /// Parse a type from a stdint.h or arm_neon.h typedef name, + /// for example uint32x2_t or int64_t. + static Type fromTypedefName(StringRef Name); + +private: + /// Creates the type based on the typespec string in TS. + /// Sets "Quad" to true if the "Q" or "H" modifiers were + /// seen. This is needed by applyModifier as some modifiers + /// only take effect if the type size was changed by "Q" or "H". + void applyTypespec(bool &Quad); + /// Applies a prototype modifier to the type. + void applyModifier(char Mod); +}; + +//===----------------------------------------------------------------------===// +// Variable +//===----------------------------------------------------------------------===// + +/// A variable is a simple class that just has a type and a name. +class Variable { + Type T; + std::string N; + +public: + Variable() : T(Type::getVoid()), N("") {} + Variable(Type T, std::string N) : T(T), N(N) {} + + Type getType() const { return T; } + std::string getName() const { return "__" + N; } +}; + +//===----------------------------------------------------------------------===// +// Intrinsic +//===----------------------------------------------------------------------===// + +/// The main grunt class. This represents an instantiation of an intrinsic with +/// a particular typespec and prototype. +class Intrinsic { + friend class DagEmitter; + + /// The Record this intrinsic was created from. + Record *R; + /// The unmangled name and prototype. + std::string Name, Proto; + /// The input and output typespecs. InTS == OutTS except when + /// CartesianProductOfTypes is 1 - this is the case for vreinterpret. + TypeSpec OutTS, InTS; + /// The base class kind. Most intrinsics use ClassS, which has full type + /// info for integers (s32/u32). Some use ClassI, which doesn't care about + /// signedness (i32), while some (ClassB) have no type at all, only a width + /// (32). + ClassKind CK; + /// 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; + /// Set if the Unvailable bit is 1. This means we don't generate a body, + /// just an "unavailable" attribute on a declaration. + bool IsUnavailable; + /// Is this intrinsic safe for big-endian? or does it need its arguments + /// reversing? + bool BigEndianSafe; + + /// The types of return value [0] and parameters [1..]. + std::vector<Type> Types; + /// The local variables defined. + std::map<std::string, Variable> Variables; + /// NeededEarly - set if any other intrinsic depends on this intrinsic. + bool NeededEarly; + /// UseMacro - set if we should implement using a macro or unset for a + /// function. + bool UseMacro; + /// The set of intrinsics that this intrinsic uses/requires. + std::set<Intrinsic *> Dependencies; + /// The "base type", which is Type('d', OutTS). InBaseType is only + /// different if CartesianProductOfTypes = 1 (for vreinterpret). + Type BaseType, InBaseType; + /// The return variable. + Variable RetVar; + /// A postfix to apply to every variable. Defaults to "". + std::string VariablePostfix; + + NeonEmitter &Emitter; + std::stringstream OS; + +public: + Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS, + TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter, + StringRef Guard, bool IsUnavailable, bool BigEndianSafe) + : R(R), Name(Name.str()), Proto(Proto.str()), OutTS(OutTS), InTS(InTS), + CK(CK), Body(Body), Guard(Guard.str()), IsUnavailable(IsUnavailable), + BigEndianSafe(BigEndianSafe), NeededEarly(false), UseMacro(false), + BaseType(OutTS, 'd'), InBaseType(InTS, 'd'), Emitter(Emitter) { + // 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. + if (Proto.find('i') != std::string::npos) + UseMacro = true; + + // Pointer arguments need to use macros to avoid hiding aligned attributes + // from the pointer type. + if (Proto.find('p') != std::string::npos || + Proto.find('c') != std::string::npos) + UseMacro = true; + + // It is not permitted to pass or return an __fp16 by value, so intrinsics + // taking a scalar float16_t must be implemented as macros. + if (OutTS.find('h') != std::string::npos && + Proto.find('s') != std::string::npos) + UseMacro = true; + + // Modify the TypeSpec per-argument to get a concrete Type, and create + // known variables for each. + // Types[0] is the return value. + Types.emplace_back(OutTS, Proto[0]); + for (unsigned I = 1; I < Proto.size(); ++I) + Types.emplace_back(InTS, Proto[I]); + } + + /// Get the Record that this intrinsic is based off. + Record *getRecord() const { return R; } + /// Get the set of Intrinsics that this intrinsic calls. + /// this is the set of immediate dependencies, NOT the + /// transitive closure. + const std::set<Intrinsic *> &getDependencies() const { return Dependencies; } + /// Get the architectural guard string (#ifdef). + std::string getGuard() const { return Guard; } + /// Get the non-mangled name. + std::string getName() const { return Name; } + + /// Return true if the intrinsic takes an immediate operand. + bool hasImmediate() const { + return Proto.find('i') != std::string::npos; + } + /// Return the parameter index of the immediate operand. + unsigned getImmediateIdx() const { + assert(hasImmediate()); + unsigned Idx = Proto.find('i'); + assert(Idx > 0 && "Can't return an immediate!"); + return Idx - 1; + } + + /// Return true if the intrinsic takes an splat operand. + bool hasSplat() const { return Proto.find('a') != std::string::npos; } + /// Return the parameter index of the splat operand. + unsigned getSplatIdx() const { + assert(hasSplat()); + unsigned Idx = Proto.find('a'); + assert(Idx > 0 && "Can't return a splat!"); + return Idx - 1; + } + + unsigned getNumParams() const { return Proto.size() - 1; } + Type getReturnType() const { return Types[0]; } + Type getParamType(unsigned I) const { return Types[I + 1]; } + Type getBaseType() const { return BaseType; } + /// Return the raw prototype string. + std::string getProto() const { return Proto; } + + /// Return true if the prototype has a scalar argument. + /// This does not return true for the "splat" code ('a'). + bool protoHasScalar(); + + /// 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. + unsigned getGeneratedParamIdx(unsigned PIndex) { + unsigned Idx = 0; + if (getReturnType().getNumVectors() > 1) + // Multiple vectors are passed as sret. + ++Idx; + + for (unsigned I = 0; I < PIndex; ++I) + Idx += std::max(1U, getParamType(I).getNumVectors()); + + return Idx; + } + + bool hasBody() const { return Body && Body->getValues().size() > 0; } + + 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; + return Name < Other.Name; + } + + ClassKind getClassKind(bool UseClassBIfScalar = false) { + if (UseClassBIfScalar && !protoHasScalar()) + return ClassB; + return CK; + } + + /// Return the name, mangled with type information. + /// If ForceClassS is true, use ClassS (u32/s32) instead + /// of the intrinsic's own type class. + std::string getMangledName(bool ForceClassS = false); + /// Return the type code for a builtin function call. + std::string getInstTypeCode(Type T, ClassKind CK); + /// Return the type string for a BUILTIN() macro in Builtins.def. + std::string getBuiltinTypeStr(); + + /// Generate the intrinsic, returning code. + std::string generate(); + /// Perform type checking and populate the dependency graph, but + /// don't generate code yet. + void indexBody(); + +private: + std::string mangleName(std::string Name, ClassKind CK); + + void initVariables(); + std::string replaceParamsIn(std::string S); + + void emitBodyAsBuiltinCall(); + + void generateImpl(bool ReverseArguments, + StringRef NamePrefix, StringRef CallPrefix); + void emitReturn(); + void emitBody(StringRef CallPrefix); + void emitShadowedArgs(); + void emitArgumentReversal(); + void emitReturnReversal(); + void emitReverseVariable(Variable &Dest, Variable &Src); + void emitNewLine(); + void emitClosingBrace(); + void emitOpeningBrace(); + void emitPrototype(StringRef NamePrefix); + + class DagEmitter { + Intrinsic &Intr; + StringRef CallPrefix; + + public: + DagEmitter(Intrinsic &Intr, StringRef CallPrefix) : + Intr(Intr), CallPrefix(CallPrefix) { + } + std::pair<Type, std::string> emitDagArg(Init *Arg, std::string ArgName); + std::pair<Type, std::string> emitDagSaveTemp(DagInit *DI); + std::pair<Type, std::string> emitDagSplat(DagInit *DI); + std::pair<Type, std::string> emitDagDup(DagInit *DI); + std::pair<Type, std::string> emitDagShuffle(DagInit *DI); + std::pair<Type, std::string> emitDagCast(DagInit *DI, bool IsBitCast); + std::pair<Type, std::string> emitDagCall(DagInit *DI); + std::pair<Type, std::string> emitDagNameReplace(DagInit *DI); + std::pair<Type, std::string> emitDagLiteral(DagInit *DI); + std::pair<Type, std::string> emitDagOp(DagInit *DI); + std::pair<Type, std::string> emitDag(DagInit *DI); + }; + +}; + +//===----------------------------------------------------------------------===// +// NeonEmitter +//===----------------------------------------------------------------------===// + +class NeonEmitter { + RecordKeeper &Records; + DenseMap<Record *, ClassKind> ClassMap; + std::map<std::string, std::vector<Intrinsic *>> IntrinsicMap; + unsigned UniqueNumber; + + void createIntrinsic(Record *R, SmallVectorImpl<Intrinsic *> &Out); + void genBuiltinsDef(raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs); + void genOverloadTypeCheckCode(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs); + void genIntrinsicRangeCheckCode(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs); + +public: + /// Called by Intrinsic - this attempts to get an intrinsic that takes + /// the given types as arguments. + Intrinsic *getIntrinsic(StringRef Name, ArrayRef<Type> Types); + + /// Called by Intrinsic - returns a globally-unique number. + unsigned getUniqueNumber() { return UniqueNumber++; } + + NeonEmitter(RecordKeeper &R) : Records(R), UniqueNumber(0) { + Record *SI = R.getClass("SInst"); + Record *II = R.getClass("IInst"); + Record *WI = R.getClass("WInst"); + Record *SOpI = R.getClass("SOpInst"); + Record *IOpI = R.getClass("IOpInst"); + Record *WOpI = R.getClass("WOpInst"); + Record *LOpI = R.getClass("LOpInst"); + Record *NoTestOpI = R.getClass("NoTestOpInst"); + + ClassMap[SI] = ClassS; + ClassMap[II] = ClassI; + ClassMap[WI] = ClassW; + ClassMap[SOpI] = ClassS; + ClassMap[IOpI] = ClassI; + ClassMap[WOpI] = ClassW; + ClassMap[LOpI] = ClassL; + ClassMap[NoTestOpI] = ClassNoTest; + } + + // run - Emit arm_neon.h.inc + void run(raw_ostream &o); + + // runHeader - Emit all the __builtin prototypes used in arm_neon.h + void runHeader(raw_ostream &o); + + // runTests - Emit tests for all the Neon intrinsics. + void runTests(raw_ostream &o); +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Type implementation +//===----------------------------------------------------------------------===// + +std::string Type::str() const { + if (Void) + return "void"; + std::string S; + + if (!Signed && isInteger()) + S += "u"; + + if (Poly) + S += "poly"; + else if (Float) + S += "float"; + else + S += "int"; + + S += utostr(ElementBitwidth); + if (isVector()) + S += "x" + utostr(getNumElements()); + if (NumVectors > 1) + S += "x" + utostr(NumVectors); + S += "_t"; + + if (Constant) + S += " const"; + if (Pointer) + S += " *"; + + return S; +} + +std::string Type::builtin_str() const { + std::string S; + if (isVoid()) + return "v"; + + if (Pointer) + // All pointers are void pointers. + S += "v"; + else if (isInteger()) + switch (ElementBitwidth) { + case 8: S += "c"; break; + case 16: S += "s"; break; + case 32: S += "i"; break; + case 64: S += "Wi"; break; + case 128: S += "LLLi"; break; + default: llvm_unreachable("Unhandled case!"); + } + else + switch (ElementBitwidth) { + case 16: S += "h"; break; + case 32: S += "f"; break; + case 64: S += "d"; break; + default: llvm_unreachable("Unhandled case!"); + } + + if (isChar() && !Pointer) + // Make chars explicitly signed. + S = "S" + S; + else if (isInteger() && !Pointer && !Signed) + S = "U" + S; + + // Constant indices are "int", but have the "constant expression" modifier. + if (isImmediate()) { + assert(isInteger() && isSigned()); + S = "I" + S; + } + + if (isScalar()) { + if (Constant) S += "C"; + if (Pointer) S += "*"; + return S; + } + + std::string Ret; + for (unsigned I = 0; I < NumVectors; ++I) + Ret += "V" + utostr(getNumElements()) + S; + + return Ret; +} + +unsigned Type::getNeonEnum() const { + unsigned Addend; + switch (ElementBitwidth) { + case 8: Addend = 0; break; + case 16: Addend = 1; break; + case 32: Addend = 2; break; + case 64: Addend = 3; break; + case 128: Addend = 4; break; + default: llvm_unreachable("Unhandled element bitwidth!"); + } + + unsigned Base = (unsigned)NeonTypeFlags::Int8 + Addend; + if (Poly) { + // Adjustment needed because Poly32 doesn't exist. + if (Addend >= 2) + --Addend; + Base = (unsigned)NeonTypeFlags::Poly8 + Addend; + } + if (Float) { + assert(Addend != 0 && "Float8 doesn't exist!"); + Base = (unsigned)NeonTypeFlags::Float16 + (Addend - 1); + } + + if (Bitwidth == 128) + Base |= (unsigned)NeonTypeFlags::QuadFlag; + if (isInteger() && !Signed) + Base |= (unsigned)NeonTypeFlags::UnsignedFlag; + + return Base; +} + +Type Type::fromTypedefName(StringRef Name) { + Type T; + T.Void = false; + T.Float = false; + T.Poly = false; + + if (Name.front() == 'u') { + T.Signed = false; + Name = Name.drop_front(); + } else { + T.Signed = true; + } + + if (Name.startswith("float")) { + T.Float = true; + Name = Name.drop_front(5); + } else if (Name.startswith("poly")) { + T.Poly = true; + Name = Name.drop_front(4); + } else { + assert(Name.startswith("int")); + Name = Name.drop_front(3); + } + + unsigned I = 0; + for (I = 0; I < Name.size(); ++I) { + if (!isdigit(Name[I])) + break; + } + Name.substr(0, I).getAsInteger(10, T.ElementBitwidth); + Name = Name.drop_front(I); + + T.Bitwidth = T.ElementBitwidth; + T.NumVectors = 1; + + if (Name.front() == 'x') { + Name = Name.drop_front(); + unsigned I = 0; + for (I = 0; I < Name.size(); ++I) { + if (!isdigit(Name[I])) + break; + } + unsigned NumLanes; + Name.substr(0, I).getAsInteger(10, NumLanes); + Name = Name.drop_front(I); + T.Bitwidth = T.ElementBitwidth * NumLanes; + } else { + // Was scalar. + T.NumVectors = 0; + } + if (Name.front() == 'x') { + Name = Name.drop_front(); + unsigned I = 0; + for (I = 0; I < Name.size(); ++I) { + if (!isdigit(Name[I])) + break; + } + Name.substr(0, I).getAsInteger(10, T.NumVectors); + Name = Name.drop_front(I); + } + + assert(Name.startswith("_t") && "Malformed typedef!"); + return T; +} + +void Type::applyTypespec(bool &Quad) { + std::string S = TS; + ScalarForMangling = false; + Void = false; + Poly = Float = false; + ElementBitwidth = ~0U; + Signed = true; + NumVectors = 1; + + for (char I : S) { + switch (I) { + case 'S': + ScalarForMangling = true; + break; + case 'H': + NoManglingQ = true; + Quad = true; + break; + case 'Q': + Quad = true; + break; + case 'P': + Poly = true; + break; + case 'U': + Signed = false; + break; + case 'c': + ElementBitwidth = 8; + break; + case 'h': + Float = true; + // Fall through + case 's': + ElementBitwidth = 16; + break; + case 'f': + Float = true; + // Fall through + case 'i': + ElementBitwidth = 32; + break; + case 'd': + Float = true; + // Fall through + case 'l': + ElementBitwidth = 64; + break; + case 'k': + ElementBitwidth = 128; + // Poly doesn't have a 128x1 type. + if (Poly) + NumVectors = 0; + break; + default: + llvm_unreachable("Unhandled type code!"); + } + } + assert(ElementBitwidth != ~0U && "Bad element bitwidth!"); + + Bitwidth = Quad ? 128 : 64; +} + +void Type::applyModifier(char Mod) { + bool AppliedQuad = false; + applyTypespec(AppliedQuad); + + switch (Mod) { + case 'v': + Void = true; + break; + case 't': + if (Poly) { + Poly = false; + Signed = false; + } + break; + case 'b': + Signed = false; + Float = false; + Poly = false; + NumVectors = 0; + Bitwidth = ElementBitwidth; + break; + case '$': + Signed = true; + Float = false; + Poly = false; + NumVectors = 0; + Bitwidth = ElementBitwidth; + break; + case 'u': + Signed = false; + Poly = false; + Float = false; + break; + case 'x': + Signed = true; + assert(!Poly && "'u' can't be used with poly types!"); + Float = false; + break; + case 'o': + Bitwidth = ElementBitwidth = 64; + NumVectors = 0; + Float = true; + break; + case 'y': + Bitwidth = ElementBitwidth = 32; + NumVectors = 0; + Float = true; + break; + case 'f': + // Special case - if we're half-precision, a floating + // point argument needs to be 128-bits (double size). + if (isHalf()) + Bitwidth = 128; + Float = true; + ElementBitwidth = 32; + break; + case 'F': + Float = true; + ElementBitwidth = 64; + break; + case 'g': + if (AppliedQuad) + Bitwidth /= 2; + break; + case 'j': + if (!AppliedQuad) + Bitwidth *= 2; + break; + case 'w': + ElementBitwidth *= 2; + Bitwidth *= 2; + break; + case 'n': + ElementBitwidth *= 2; + break; + case 'i': + Float = false; + Poly = false; + ElementBitwidth = Bitwidth = 32; + NumVectors = 0; + Signed = true; + Immediate = true; + break; + case 'l': + Float = false; + Poly = false; + ElementBitwidth = Bitwidth = 64; + NumVectors = 0; + Signed = false; + Immediate = true; + break; + case 'z': + ElementBitwidth /= 2; + Bitwidth = ElementBitwidth; + NumVectors = 0; + break; + case 'r': + ElementBitwidth *= 2; + Bitwidth = ElementBitwidth; + NumVectors = 0; + break; + case 's': + case 'a': + Bitwidth = ElementBitwidth; + NumVectors = 0; + break; + case 'k': + Bitwidth *= 2; + break; + case 'c': + Constant = true; + // Fall through + case 'p': + Pointer = true; + Bitwidth = ElementBitwidth; + NumVectors = 0; + break; + case 'h': + ElementBitwidth /= 2; + break; + case 'q': + ElementBitwidth /= 2; + Bitwidth *= 2; + break; + case 'e': + ElementBitwidth /= 2; + Signed = false; + break; + case 'm': + ElementBitwidth /= 2; + Bitwidth /= 2; + break; + case 'd': + break; + case '2': + NumVectors = 2; + break; + case '3': + NumVectors = 3; + break; + case '4': + NumVectors = 4; + break; + case 'B': + NumVectors = 2; + if (!AppliedQuad) + Bitwidth *= 2; + break; + case 'C': + NumVectors = 3; + if (!AppliedQuad) + Bitwidth *= 2; + break; + case 'D': + NumVectors = 4; + if (!AppliedQuad) + Bitwidth *= 2; + break; + default: + llvm_unreachable("Unhandled character!"); + } +} + +//===----------------------------------------------------------------------===// +// Intrinsic implementation +//===----------------------------------------------------------------------===// + +std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) { + char typeCode = '\0'; + bool printNumber = true; + + if (CK == ClassB) + return ""; + + if (T.isPoly()) + typeCode = 'p'; + else if (T.isInteger()) + typeCode = T.isSigned() ? 's' : 'u'; + else + typeCode = 'f'; + + if (CK == ClassI) { + switch (typeCode) { + default: + break; + case 's': + case 'u': + case 'p': + typeCode = 'i'; + break; + } + } + if (CK == ClassB) { + typeCode = '\0'; + } + + std::string S; + if (typeCode != '\0') + S.push_back(typeCode); + if (printNumber) + S += utostr(T.getElementSizeInBits()); + + return S; +} + +std::string Intrinsic::getBuiltinTypeStr() { + ClassKind LocalCK = getClassKind(true); + std::string S; + + Type RetT = getReturnType(); + if ((LocalCK == ClassI || LocalCK == ClassW) && RetT.isScalar() && + !RetT.isFloating()) + RetT.makeInteger(RetT.getElementSizeInBits(), false); + + // Since the return value must be one type, return a vector type of the + // appropriate width which we will bitcast. An exception is made for + // returning structs of 2, 3, or 4 vectors which are returned in a sret-like + // fashion, storing them to a pointer arg. + if (RetT.getNumVectors() > 1) { + S += "vv*"; // void result with void* first argument + } else { + if (RetT.isPoly()) + RetT.makeInteger(RetT.getElementSizeInBits(), false); + if (!RetT.isScalar() && !RetT.isSigned()) + RetT.makeSigned(); + + bool ForcedVectorFloatingType = Proto[0] == 'F' || Proto[0] == 'f'; + if (LocalCK == ClassB && !RetT.isScalar() && !ForcedVectorFloatingType) + // Cast to vector of 8-bit elements. + RetT.makeInteger(8, true); + + S += RetT.builtin_str(); + } + + for (unsigned I = 0; I < getNumParams(); ++I) { + Type T = getParamType(I); + if (T.isPoly()) + T.makeInteger(T.getElementSizeInBits(), false); + + bool ForcedFloatingType = Proto[I + 1] == 'F' || Proto[I + 1] == 'f'; + if (LocalCK == ClassB && !T.isScalar() && !ForcedFloatingType) + T.makeInteger(8, true); + // Halves always get converted to 8-bit elements. + if (T.isHalf() && T.isVector() && !T.isScalarForMangling()) + T.makeInteger(8, true); + + if (LocalCK == ClassI) + T.makeSigned(); + + if (hasImmediate() && getImmediateIdx() == I) + T.makeImmediate(32); + + S += T.builtin_str(); + } + + // Extra constant integer to hold type class enum for this function, e.g. s8 + if (LocalCK == ClassB) + S += "i"; + + return S; +} + +std::string Intrinsic::getMangledName(bool ForceClassS) { + // Check if the prototype has a scalar operand with the type of the vector + // elements. If not, bitcasting the args will take care of arg checking. + // The actual signedness etc. will be taken care of with special enums. + ClassKind LocalCK = CK; + if (!protoHasScalar()) + LocalCK = ClassB; + + return mangleName(Name, ForceClassS ? ClassS : LocalCK); +} + +std::string Intrinsic::mangleName(std::string Name, ClassKind LocalCK) { + std::string typeCode = getInstTypeCode(BaseType, LocalCK); + std::string S = Name; + + if (Name == "vcvt_f32_f16" || Name == "vcvt_f32_f64" || + Name == "vcvt_f64_f32") + return Name; + + if (typeCode.size() > 0) { + // If the name ends with _xN (N = 2,3,4), insert the typeCode before _xN. + if (Name.size() >= 3 && isdigit(Name.back()) && + Name[Name.length() - 2] == 'x' && Name[Name.length() - 3] == '_') + S.insert(S.length() - 3, "_" + typeCode); + else + S += "_" + typeCode; + } + + if (BaseType != InBaseType) { + // A reinterpret - out the input base type at the end. + S += "_" + getInstTypeCode(InBaseType, LocalCK); + } + + if (LocalCK == ClassB) + S += "_v"; + + // Insert a 'q' before the first '_' character so that it ends up before + // _lane or _n on vector-scalar operations. + if (BaseType.getSizeInBits() == 128 && !BaseType.noManglingQ()) { + size_t Pos = S.find('_'); + S.insert(Pos, "q"); + } + + char Suffix = '\0'; + if (BaseType.isScalarForMangling()) { + switch (BaseType.getElementSizeInBits()) { + case 8: Suffix = 'b'; break; + case 16: Suffix = 'h'; break; + case 32: Suffix = 's'; break; + case 64: Suffix = 'd'; break; + default: llvm_unreachable("Bad suffix!"); + } + } + if (Suffix != '\0') { + size_t Pos = S.find('_'); + S.insert(Pos, &Suffix, 1); + } + + return S; +} + +std::string Intrinsic::replaceParamsIn(std::string S) { + while (S.find('$') != std::string::npos) { + size_t Pos = S.find('$'); + size_t End = Pos + 1; + while (isalpha(S[End])) + ++End; + + std::string VarName = S.substr(Pos + 1, End - Pos - 1); + assert_with_loc(Variables.find(VarName) != Variables.end(), + "Variable not defined!"); + S.replace(Pos, End - Pos, Variables.find(VarName)->second.getName()); + } + + return S; +} + +void Intrinsic::initVariables() { + Variables.clear(); + + // Modify the TypeSpec per-argument to get a concrete Type, and create + // known variables for each. + for (unsigned I = 1; I < Proto.size(); ++I) { + char NameC = '0' + (I - 1); + std::string Name = "p"; + Name.push_back(NameC); + + Variables[Name] = Variable(Types[I], Name + VariablePostfix); + } + RetVar = Variable(Types[0], "ret" + VariablePostfix); +} + +void Intrinsic::emitPrototype(StringRef NamePrefix) { + if (UseMacro) + OS << "#define "; + else + OS << "__ai " << Types[0].str() << " "; + + OS << NamePrefix.str() << mangleName(Name, ClassS) << "("; + + for (unsigned I = 0; I < getNumParams(); ++I) { + if (I != 0) + OS << ", "; + + char NameC = '0' + I; + std::string Name = "p"; + Name.push_back(NameC); + assert(Variables.find(Name) != Variables.end()); + Variable &V = Variables[Name]; + + if (!UseMacro) + OS << V.getType().str() << " "; + OS << V.getName(); + } + + OS << ")"; +} + +void Intrinsic::emitOpeningBrace() { + if (UseMacro) + OS << " __extension__ ({"; + else + OS << " {"; + emitNewLine(); +} + +void Intrinsic::emitClosingBrace() { + if (UseMacro) + OS << "})"; + else + OS << "}"; +} + +void Intrinsic::emitNewLine() { + if (UseMacro) + OS << " \\\n"; + else + OS << "\n"; +} + +void Intrinsic::emitReverseVariable(Variable &Dest, Variable &Src) { + if (Dest.getType().getNumVectors() > 1) { + emitNewLine(); + + for (unsigned K = 0; K < Dest.getType().getNumVectors(); ++K) { + OS << " " << Dest.getName() << ".val[" << utostr(K) << "] = " + << "__builtin_shufflevector(" + << Src.getName() << ".val[" << utostr(K) << "], " + << Src.getName() << ".val[" << utostr(K) << "]"; + for (int J = Dest.getType().getNumElements() - 1; J >= 0; --J) + OS << ", " << utostr(J); + OS << ");"; + emitNewLine(); + } + } else { + OS << " " << Dest.getName() + << " = __builtin_shufflevector(" << Src.getName() << ", " << Src.getName(); + for (int J = Dest.getType().getNumElements() - 1; J >= 0; --J) + OS << ", " << utostr(J); + OS << ");"; + emitNewLine(); + } +} + +void Intrinsic::emitArgumentReversal() { + if (BigEndianSafe) + return; + + // Reverse all vector arguments. + for (unsigned I = 0; I < getNumParams(); ++I) { + std::string Name = "p" + utostr(I); + std::string NewName = "rev" + utostr(I); + + Variable &V = Variables[Name]; + Variable NewV(V.getType(), NewName + VariablePostfix); + + if (!NewV.getType().isVector() || NewV.getType().getNumElements() == 1) + continue; + + OS << " " << NewV.getType().str() << " " << NewV.getName() << ";"; + emitReverseVariable(NewV, V); + V = NewV; + } +} + +void Intrinsic::emitReturnReversal() { + if (BigEndianSafe) + return; + if (!getReturnType().isVector() || getReturnType().isVoid() || + getReturnType().getNumElements() == 1) + return; + emitReverseVariable(RetVar, RetVar); +} + + +void Intrinsic::emitShadowedArgs() { + // Macro arguments are not type-checked like inline function arguments, + // so assign them to local temporaries to get the right type checking. + if (!UseMacro) + return; + + for (unsigned I = 0; I < getNumParams(); ++I) { + // Do not create a temporary for an immediate argument. + // That would defeat the whole point of using a macro! + if (hasImmediate() && Proto[I+1] == 'i') + continue; + // Do not create a temporary for pointer arguments. The input + // pointer may have an alignment hint. + if (getParamType(I).isPointer()) + continue; + + std::string Name = "p" + utostr(I); + + assert(Variables.find(Name) != Variables.end()); + Variable &V = Variables[Name]; + + std::string NewName = "s" + utostr(I); + Variable V2(V.getType(), NewName + VariablePostfix); + + OS << " " << V2.getType().str() << " " << V2.getName() << " = " + << V.getName() << ";"; + emitNewLine(); + + V = V2; + } +} + +// We don't check 'a' in this function, because for builtin function the +// argument matching to 'a' uses a vector type splatted from a scalar type. +bool Intrinsic::protoHasScalar() { + return (Proto.find('s') != std::string::npos || + Proto.find('z') != std::string::npos || + Proto.find('r') != std::string::npos || + Proto.find('b') != std::string::npos || + Proto.find('$') != std::string::npos || + Proto.find('y') != std::string::npos || + Proto.find('o') != std::string::npos); +} + +void Intrinsic::emitBodyAsBuiltinCall() { + std::string S; + + // If this builtin returns a struct 2, 3, or 4 vectors, pass it as an implicit + // sret-like argument. + bool SRet = getReturnType().getNumVectors() >= 2; + + StringRef N = Name; + if (hasSplat()) { + // Call the non-splat builtin: chop off the "_n" suffix from the name. + assert(N.endswith("_n")); + N = N.drop_back(2); + } + + ClassKind LocalCK = CK; + if (!protoHasScalar()) + LocalCK = ClassB; + + if (!getReturnType().isVoid() && !SRet) + S += "(" + RetVar.getType().str() + ") "; + + S += "__builtin_neon_" + mangleName(N, LocalCK) + "("; + + if (SRet) + S += "&" + RetVar.getName() + ", "; + + for (unsigned I = 0; I < getNumParams(); ++I) { + Variable &V = Variables["p" + utostr(I)]; + Type T = V.getType(); + + // Handle multiple-vector values specially, emitting each subvector as an + // argument to the builtin. + if (T.getNumVectors() > 1) { + // Check if an explicit cast is needed. + std::string Cast; + if (T.isChar() || T.isPoly() || !T.isSigned()) { + Type T2 = T; + T2.makeOneVector(); + T2.makeInteger(8, /*Signed=*/true); + Cast = "(" + T2.str() + ")"; + } + + for (unsigned J = 0; J < T.getNumVectors(); ++J) + S += Cast + V.getName() + ".val[" + utostr(J) + "], "; + continue; + } + + std::string Arg; + Type CastToType = T; + if (hasSplat() && I == getSplatIdx()) { + Arg = "(" + BaseType.str() + ") {"; + for (unsigned J = 0; J < BaseType.getNumElements(); ++J) { + if (J != 0) + Arg += ", "; + Arg += V.getName(); + } + Arg += "}"; + + CastToType = BaseType; + } else { + Arg = V.getName(); + } + + // Check if an explicit cast is needed. + if (CastToType.isVector()) { + CastToType.makeInteger(8, true); + Arg = "(" + CastToType.str() + ")" + Arg; + } + + S += Arg + ", "; + } + + // Extra constant integer to hold type class enum for this function, e.g. s8 + if (getClassKind(true) == ClassB) { + Type ThisTy = getReturnType(); + if (Proto[0] == 'v' || Proto[0] == 'f' || Proto[0] == 'F') + ThisTy = getParamType(0); + if (ThisTy.isPointer()) + ThisTy = getParamType(1); + + S += utostr(ThisTy.getNeonEnum()); + } else { + // Remove extraneous ", ". + S.pop_back(); + S.pop_back(); + } + S += ");"; + + std::string RetExpr; + if (!SRet && !RetVar.getType().isVoid()) + RetExpr = RetVar.getName() + " = "; + + OS << " " << RetExpr << S; + emitNewLine(); +} + +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().size() == 0) { + // Nothing specific to output - must output a builtin. + emitBodyAsBuiltinCall(); + return; + } + + // We have a list of "things to output". The last should be returned. + for (auto *I : Body->getValues()) { + if (StringInit *SI = dyn_cast<StringInit>(I)) { + Lines.push_back(replaceParamsIn(SI->getAsString())); + } else if (DagInit *DI = dyn_cast<DagInit>(I)) { + DagEmitter DE(*this, CallPrefix); + Lines.push_back(DE.emitDag(DI).second + ";"); + } + } + + assert(!Lines.empty() && "Empty def?"); + if (!RetVar.getType().isVoid()) + Lines.back().insert(0, RetVar.getName() + " = "); + + for (auto &L : Lines) { + OS << " " << L; + emitNewLine(); + } +} + +void Intrinsic::emitReturn() { + if (RetVar.getType().isVoid()) + return; + if (UseMacro) + OS << " " << RetVar.getName() << ";"; + else + OS << " return " << RetVar.getName() << ";"; + emitNewLine(); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDag(DagInit *DI) { + // At this point we should only be seeing a def. + DefInit *DefI = cast<DefInit>(DI->getOperator()); + std::string Op = DefI->getAsString(); + + if (Op == "cast" || Op == "bitcast") + return emitDagCast(DI, Op == "bitcast"); + if (Op == "shuffle") + return emitDagShuffle(DI); + if (Op == "dup") + return emitDagDup(DI); + if (Op == "splat") + return emitDagSplat(DI); + if (Op == "save_temp") + return emitDagSaveTemp(DI); + if (Op == "op") + return emitDagOp(DI); + if (Op == "call") + return emitDagCall(DI); + if (Op == "name_replace") + return emitDagNameReplace(DI); + if (Op == "literal") + return emitDagLiteral(DI); + assert_with_loc(false, "Unknown operation!"); + return std::make_pair(Type::getVoid(), ""); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagOp(DagInit *DI) { + std::string Op = cast<StringInit>(DI->getArg(0))->getAsUnquotedString(); + if (DI->getNumArgs() == 2) { + // Unary op. + std::pair<Type, std::string> R = + emitDagArg(DI->getArg(1), DI->getArgName(1)); + return std::make_pair(R.first, Op + R.second); + } else { + assert(DI->getNumArgs() == 3 && "Can only handle unary and binary ops!"); + std::pair<Type, std::string> R1 = + emitDagArg(DI->getArg(1), DI->getArgName(1)); + std::pair<Type, std::string> R2 = + emitDagArg(DI->getArg(2), DI->getArgName(2)); + assert_with_loc(R1.first == R2.first, "Argument type mismatch!"); + return std::make_pair(R1.first, R1.second + " " + Op + " " + R2.second); + } +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCall(DagInit *DI) { + std::vector<Type> Types; + std::vector<std::string> Values; + for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) { + std::pair<Type, std::string> R = + emitDagArg(DI->getArg(I + 1), DI->getArgName(I + 1)); + Types.push_back(R.first); + Values.push_back(R.second); + } + + // Look up the called intrinsic. + std::string N; + if (StringInit *SI = dyn_cast<StringInit>(DI->getArg(0))) + N = SI->getAsUnquotedString(); + else + N = emitDagArg(DI->getArg(0), "").second; + Intrinsic *Callee = Intr.Emitter.getIntrinsic(N, Types); + assert(Callee && "getIntrinsic should not return us nullptr!"); + + // Make sure the callee is known as an early def. + Callee->setNeededEarly(); + Intr.Dependencies.insert(Callee); + + // Now create the call itself. + std::string S = CallPrefix.str() + Callee->getMangledName(true) + "("; + for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) { + if (I != 0) + S += ", "; + S += Values[I]; + } + S += ")"; + + return std::make_pair(Callee->getReturnType(), S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagCast(DagInit *DI, + bool IsBitCast){ + // (cast MOD* VAL) -> cast VAL to type given by MOD. + std::pair<Type, std::string> R = emitDagArg( + DI->getArg(DI->getNumArgs() - 1), DI->getArgName(DI->getNumArgs() - 1)); + Type castToType = R.first; + for (unsigned ArgIdx = 0; ArgIdx < DI->getNumArgs() - 1; ++ArgIdx) { + + // MOD can take several forms: + // 1. $X - take the type of parameter / variable X. + // 2. The value "R" - take the type of the return type. + // 3. a type string + // 4. The value "U" or "S" to switch the signedness. + // 5. The value "H" or "D" to half or double the bitwidth. + // 6. The value "8" to convert to 8-bit (signed) integer lanes. + if (DI->getArgName(ArgIdx).size()) { + assert_with_loc(Intr.Variables.find(DI->getArgName(ArgIdx)) != + Intr.Variables.end(), + "Variable not found"); + castToType = Intr.Variables[DI->getArgName(ArgIdx)].getType(); + } else { + StringInit *SI = dyn_cast<StringInit>(DI->getArg(ArgIdx)); + assert_with_loc(SI, "Expected string type or $Name for cast type"); + + if (SI->getAsUnquotedString() == "R") { + castToType = Intr.getReturnType(); + } else if (SI->getAsUnquotedString() == "U") { + castToType.makeUnsigned(); + } else if (SI->getAsUnquotedString() == "S") { + castToType.makeSigned(); + } else if (SI->getAsUnquotedString() == "H") { + castToType.halveLanes(); + } else if (SI->getAsUnquotedString() == "D") { + castToType.doubleLanes(); + } else if (SI->getAsUnquotedString() == "8") { + castToType.makeInteger(8, true); + } else { + castToType = Type::fromTypedefName(SI->getAsUnquotedString()); + assert_with_loc(!castToType.isVoid(), "Unknown typedef"); + } + } + } + + std::string S; + if (IsBitCast) { + // Emit a reinterpret cast. The second operand must be an lvalue, so create + // a temporary. + std::string N = "reint"; + unsigned I = 0; + while (Intr.Variables.find(N) != Intr.Variables.end()) + N = "reint" + utostr(++I); + Intr.Variables[N] = Variable(R.first, N + Intr.VariablePostfix); + + Intr.OS << R.first.str() << " " << Intr.Variables[N].getName() << " = " + << R.second << ";"; + Intr.emitNewLine(); + + S = "*(" + castToType.str() + " *) &" + Intr.Variables[N].getName() + ""; + } else { + // Emit a normal (static) cast. + S = "(" + castToType.str() + ")(" + R.second + ")"; + } + + return std::make_pair(castToType, S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagShuffle(DagInit *DI){ + // See the documentation in arm_neon.td for a description of these operators. + class LowHalf : public SetTheory::Operator { + public: + void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + SetTheory::RecSet Elts2; + ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts2, Loc); + Elts.insert(Elts2.begin(), Elts2.begin() + (Elts2.size() / 2)); + } + }; + class HighHalf : public SetTheory::Operator { + public: + void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + SetTheory::RecSet Elts2; + ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts2, Loc); + Elts.insert(Elts2.begin() + (Elts2.size() / 2), Elts2.end()); + } + }; + class Rev : public SetTheory::Operator { + unsigned ElementSize; + + public: + Rev(unsigned ElementSize) : ElementSize(ElementSize) {} + void apply(SetTheory &ST, DagInit *Expr, SetTheory::RecSet &Elts, + ArrayRef<SMLoc> Loc) override { + SetTheory::RecSet Elts2; + ST.evaluate(Expr->arg_begin() + 1, Expr->arg_end(), Elts2, Loc); + + int64_t VectorSize = cast<IntInit>(Expr->getArg(0))->getValue(); + VectorSize /= ElementSize; + + std::vector<Record *> Revved; + for (unsigned VI = 0; VI < Elts2.size(); VI += VectorSize) { + for (int LI = VectorSize - 1; LI >= 0; --LI) { + Revved.push_back(Elts2[VI + LI]); + } + } + + Elts.insert(Revved.begin(), Revved.end()); + } + }; + class MaskExpander : public SetTheory::Expander { + unsigned N; + + public: + MaskExpander(unsigned N) : N(N) {} + void expand(SetTheory &ST, Record *R, SetTheory::RecSet &Elts) override { + unsigned Addend = 0; + if (R->getName() == "mask0") + Addend = 0; + else if (R->getName() == "mask1") + Addend = N; + else + return; + for (unsigned I = 0; I < N; ++I) + Elts.insert(R->getRecords().getDef("sv" + utostr(I + Addend))); + } + }; + + // (shuffle arg1, arg2, sequence) + std::pair<Type, std::string> Arg1 = + emitDagArg(DI->getArg(0), DI->getArgName(0)); + std::pair<Type, std::string> Arg2 = + emitDagArg(DI->getArg(1), DI->getArgName(1)); + assert_with_loc(Arg1.first == Arg2.first, + "Different types in arguments to shuffle!"); + + SetTheory ST; + SetTheory::RecSet Elts; + ST.addOperator("lowhalf", llvm::make_unique<LowHalf>()); + ST.addOperator("highhalf", llvm::make_unique<HighHalf>()); + ST.addOperator("rev", + llvm::make_unique<Rev>(Arg1.first.getElementSizeInBits())); + ST.addExpander("MaskExpand", + llvm::make_unique<MaskExpander>(Arg1.first.getNumElements())); + ST.evaluate(DI->getArg(2), Elts, None); + + std::string S = "__builtin_shufflevector(" + Arg1.second + ", " + Arg2.second; + for (auto &E : Elts) { + StringRef Name = E->getName(); + assert_with_loc(Name.startswith("sv"), + "Incorrect element kind in shuffle mask!"); + S += ", " + Name.drop_front(2).str(); + } + S += ")"; + + // Recalculate the return type - the shuffle may have halved or doubled it. + Type T(Arg1.first); + if (Elts.size() > T.getNumElements()) { + assert_with_loc( + Elts.size() == T.getNumElements() * 2, + "Can only double or half the number of elements in a shuffle!"); + T.doubleLanes(); + } else if (Elts.size() < T.getNumElements()) { + assert_with_loc( + Elts.size() == T.getNumElements() / 2, + "Can only double or half the number of elements in a shuffle!"); + T.halveLanes(); + } + + return std::make_pair(T, S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagDup(DagInit *DI) { + assert_with_loc(DI->getNumArgs() == 1, "dup() expects one argument"); + std::pair<Type, std::string> A = emitDagArg(DI->getArg(0), DI->getArgName(0)); + assert_with_loc(A.first.isScalar(), "dup() expects a scalar argument"); + + Type T = Intr.getBaseType(); + assert_with_loc(T.isVector(), "dup() used but default type is scalar!"); + std::string S = "(" + T.str() + ") {"; + for (unsigned I = 0; I < T.getNumElements(); ++I) { + if (I != 0) + S += ", "; + S += A.second; + } + S += "}"; + + return std::make_pair(T, S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSplat(DagInit *DI) { + assert_with_loc(DI->getNumArgs() == 2, "splat() expects two arguments"); + std::pair<Type, std::string> A = emitDagArg(DI->getArg(0), DI->getArgName(0)); + std::pair<Type, std::string> B = emitDagArg(DI->getArg(1), DI->getArgName(1)); + + assert_with_loc(B.first.isScalar(), + "splat() requires a scalar int as the second argument"); + + std::string S = "__builtin_shufflevector(" + A.second + ", " + A.second; + for (unsigned I = 0; I < Intr.getBaseType().getNumElements(); ++I) { + S += ", " + B.second; + } + S += ")"; + + return std::make_pair(Intr.getBaseType(), S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagSaveTemp(DagInit *DI) { + assert_with_loc(DI->getNumArgs() == 2, "save_temp() expects two arguments"); + std::pair<Type, std::string> A = emitDagArg(DI->getArg(1), DI->getArgName(1)); + + assert_with_loc(!A.first.isVoid(), + "Argument to save_temp() must have non-void type!"); + + std::string N = DI->getArgName(0); + assert_with_loc(N.size(), "save_temp() expects a name as the first argument"); + + assert_with_loc(Intr.Variables.find(N) == Intr.Variables.end(), + "Variable already defined!"); + Intr.Variables[N] = Variable(A.first, N + Intr.VariablePostfix); + + std::string S = + A.first.str() + " " + Intr.Variables[N].getName() + " = " + A.second; + + return std::make_pair(Type::getVoid(), S); +} + +std::pair<Type, std::string> +Intrinsic::DagEmitter::emitDagNameReplace(DagInit *DI) { + std::string S = Intr.Name; + + assert_with_loc(DI->getNumArgs() == 2, "name_replace requires 2 arguments!"); + std::string ToReplace = cast<StringInit>(DI->getArg(0))->getAsUnquotedString(); + std::string ReplaceWith = cast<StringInit>(DI->getArg(1))->getAsUnquotedString(); + + size_t Idx = S.find(ToReplace); + + assert_with_loc(Idx != std::string::npos, "name should contain '" + ToReplace + "'!"); + S.replace(Idx, ToReplace.size(), ReplaceWith); + + return std::make_pair(Type::getVoid(), S); +} + +std::pair<Type, std::string> Intrinsic::DagEmitter::emitDagLiteral(DagInit *DI){ + std::string Ty = cast<StringInit>(DI->getArg(0))->getAsUnquotedString(); + std::string Value = cast<StringInit>(DI->getArg(1))->getAsUnquotedString(); + return std::make_pair(Type::fromTypedefName(Ty), Value); +} + +std::pair<Type, std::string> +Intrinsic::DagEmitter::emitDagArg(Init *Arg, std::string ArgName) { + if (ArgName.size()) { + assert_with_loc(!Arg->isComplete(), + "Arguments must either be DAGs or names, not both!"); + assert_with_loc(Intr.Variables.find(ArgName) != Intr.Variables.end(), + "Variable not defined!"); + Variable &V = Intr.Variables[ArgName]; + return std::make_pair(V.getType(), V.getName()); + } + + assert(Arg && "Neither ArgName nor Arg?!"); + DagInit *DI = dyn_cast<DagInit>(Arg); + assert_with_loc(DI, "Arguments must either be DAGs or names!"); + + return emitDag(DI); +} + +std::string Intrinsic::generate() { + // Little endian intrinsics are simple and don't require any argument + // swapping. + OS << "#ifdef __LITTLE_ENDIAN__\n"; + + generateImpl(false, "", ""); + + OS << "#else\n"; + + // Big endian intrinsics are more complex. The user intended these + // intrinsics to operate on a vector "as-if" loaded by (V)LDR, + // but we load as-if (V)LD1. So we should swap all arguments and + // swap the return value too. + // + // If we call sub-intrinsics, we should call a version that does + // not re-swap the arguments! + generateImpl(true, "", "__noswap_"); + + // If we're needed early, create a non-swapping variant for + // big-endian. + if (NeededEarly) { + generateImpl(false, "__noswap_", "__noswap_"); + } + OS << "#endif\n\n"; + + return OS.str(); +} + +void Intrinsic::generateImpl(bool ReverseArguments, + StringRef NamePrefix, StringRef CallPrefix) { + CurrentRecord = R; + + // If we call a macro, our local variables may be corrupted due to + // lack of proper lexical scoping. So, add a globally unique postfix + // to every variable. + // + // indexBody() should have set up the Dependencies set by now. + for (auto *I : Dependencies) + if (I->UseMacro) { + VariablePostfix = "_" + utostr(Emitter.getUniqueNumber()); + break; + } + + initVariables(); + + emitPrototype(NamePrefix); + + if (IsUnavailable) { + OS << " __attribute__((unavailable));"; + } else { + emitOpeningBrace(); + emitShadowedArgs(); + if (ReverseArguments) + emitArgumentReversal(); + emitBody(CallPrefix); + if (ReverseArguments) + emitReturnReversal(); + emitReturn(); + emitClosingBrace(); + } + OS << "\n"; + + CurrentRecord = nullptr; +} + +void Intrinsic::indexBody() { + CurrentRecord = R; + + initVariables(); + emitBody(""); + OS.str(""); + + CurrentRecord = nullptr; +} + +//===----------------------------------------------------------------------===// +// NeonEmitter implementation +//===----------------------------------------------------------------------===// + +Intrinsic *NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types) { + // First, look up the name in the intrinsic map. + assert_with_loc(IntrinsicMap.find(Name.str()) != IntrinsicMap.end(), + ("Intrinsic '" + Name + "' not found!").str()); + std::vector<Intrinsic *> &V = IntrinsicMap[Name.str()]; + std::vector<Intrinsic *> GoodVec; + + // Create a string to print if we end up failing. + std::string ErrMsg = "looking up intrinsic '" + Name.str() + "("; + for (unsigned I = 0; I < Types.size(); ++I) { + if (I != 0) + ErrMsg += ", "; + ErrMsg += Types[I].str(); + } + ErrMsg += ")'\n"; + ErrMsg += "Available overloads:\n"; + + // Now, look through each intrinsic implementation and see if the types are + // compatible. + for (auto *I : V) { + ErrMsg += " - " + I->getReturnType().str() + " " + I->getMangledName(); + ErrMsg += "("; + for (unsigned A = 0; A < I->getNumParams(); ++A) { + if (A != 0) + ErrMsg += ", "; + ErrMsg += I->getParamType(A).str(); + } + ErrMsg += ")\n"; + + if (I->getNumParams() != Types.size()) + continue; + + bool Good = true; + for (unsigned Arg = 0; Arg < Types.size(); ++Arg) { + if (I->getParamType(Arg) != Types[Arg]) { + Good = false; + break; + } + } + if (Good) + GoodVec.push_back(I); + } + + assert_with_loc(GoodVec.size() > 0, + "No compatible intrinsic found - " + ErrMsg); + assert_with_loc(GoodVec.size() == 1, "Multiple overloads found - " + ErrMsg); + + return GoodVec.front(); +} + +void NeonEmitter::createIntrinsic(Record *R, + SmallVectorImpl<Intrinsic *> &Out) { + std::string Name = R->getValueAsString("Name"); + std::string Proto = R->getValueAsString("Prototype"); + std::string Types = R->getValueAsString("Types"); + Record *OperationRec = R->getValueAsDef("Operation"); + bool CartesianProductOfTypes = R->getValueAsBit("CartesianProductOfTypes"); + bool BigEndianSafe = R->getValueAsBit("BigEndianSafe"); + std::string Guard = R->getValueAsString("ArchGuard"); + bool IsUnavailable = OperationRec->getValueAsBit("Unavailable"); + + // Set the global current record. This allows assert_with_loc to produce + // decent location information even when highly nested. + CurrentRecord = R; + + ListInit *Body = OperationRec->getValueAsListInit("Ops"); + + std::vector<TypeSpec> TypeSpecs = TypeSpec::fromTypeSpecs(Types); + + ClassKind CK = ClassNone; + if (R->getSuperClasses().size() >= 2) + CK = ClassMap[R->getSuperClasses()[1]]; + + std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs; + for (auto TS : TypeSpecs) { + if (CartesianProductOfTypes) { + Type DefaultT(TS, 'd'); + for (auto SrcTS : TypeSpecs) { + Type DefaultSrcT(SrcTS, 'd'); + if (TS == SrcTS || + DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits()) + continue; + NewTypeSpecs.push_back(std::make_pair(TS, SrcTS)); + } + } else { + NewTypeSpecs.push_back(std::make_pair(TS, TS)); + } + } + + std::sort(NewTypeSpecs.begin(), NewTypeSpecs.end()); + NewTypeSpecs.erase(std::unique(NewTypeSpecs.begin(), NewTypeSpecs.end()), + NewTypeSpecs.end()); + + for (auto &I : NewTypeSpecs) { + Intrinsic *IT = new Intrinsic(R, Name, Proto, I.first, I.second, CK, Body, + *this, Guard, IsUnavailable, BigEndianSafe); + + IntrinsicMap[Name].push_back(IT); + Out.push_back(IT); + } + + CurrentRecord = nullptr; +} + +/// genBuiltinsDef: Generate the BuiltinsARM.def and BuiltinsAArch64.def +/// declaration of builtins, checking for unique builtin declarations. +void NeonEmitter::genBuiltinsDef(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs) { + OS << "#ifdef GET_NEON_BUILTINS\n"; + + // 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; + + for (auto *Def : Defs) { + if (Def->hasBody()) + continue; + // Functions with 'a' (the splat code) in the type prototype should not get + // their own builtin as they use the non-splat variant. + if (Def->hasSplat()) + continue; + + std::string S = "BUILTIN(__builtin_neon_" + Def->getMangledName() + ", \""; + + S += Def->getBuiltinTypeStr(); + S += "\", \"n\")"; + + Builtins.insert(S); + } + + for (auto &S : Builtins) + OS << S << "\n"; + OS << "#endif\n\n"; +} + +/// Generate the ARM and AArch64 overloaded type checking code for +/// SemaChecking.cpp, checking for unique builtin declarations. +void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs) { + OS << "#ifdef GET_NEON_OVERLOAD_CHECK\n"; + + // We record each overload check line before emitting because subsequent Inst + // 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) {} + }; + std::map<std::string, OverloadInfo> OverloadMap; + + 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; + // Functions with 'a' (the splat code) in the type prototype should not get + // their own builtin as they use the non-splat variant. + if (Def->hasSplat()) + continue; + // Functions which have a scalar argument cannot be overloaded, no need to + // check them if we are emitting the type checking code. + if (Def->protoHasScalar()) + continue; + + uint64_t Mask = 0ULL; + Type Ty = Def->getReturnType(); + if (Def->getProto()[0] == 'v' || Def->getProto()[0] == 'f' || + Def->getProto()[0] == 'F') + Ty = Def->getParamType(0); + if (Ty.isPointer()) + Ty = Def->getParamType(1); + + Mask |= 1ULL << Ty.getNeonEnum(); + + // Check if the function has a pointer or const pointer argument. + std::string Proto = Def->getProto(); + int PtrArgNum = -1; + bool HasConstPtr = false; + for (unsigned I = 0; I < Def->getNumParams(); ++I) { + char ArgType = Proto[I + 1]; + if (ArgType == 'c') { + HasConstPtr = true; + PtrArgNum = I; + break; + } + if (ArgType == 'p') { + PtrArgNum = I; + break; + } + } + // For sret builtins, adjust the pointer argument index. + if (PtrArgNum >= 0 && Def->getReturnType().getNumVectors() > 1) + PtrArgNum += 1; + + 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") { + PtrArgNum = -1; + HasConstPtr = false; + } + + if (Mask) { + std::string Name = Def->getMangledName(); + OverloadMap.insert(std::make_pair(Name, OverloadInfo())); + OverloadInfo &OI = OverloadMap[Name]; + OI.Mask |= Mask; + OI.PtrArgNum |= PtrArgNum; + OI.HasConstPtr = HasConstPtr; + } + } + + for (auto &I : OverloadMap) { + OverloadInfo &OI = I.second; + + OS << "case NEON::BI__builtin_neon_" << I.first << ": "; + OS << "mask = 0x" << utohexstr(OI.Mask) << "ULL"; + if (OI.PtrArgNum >= 0) + OS << "; PtrArgNum = " << OI.PtrArgNum; + if (OI.HasConstPtr) + OS << "; HasConstPtr = true"; + OS << "; break;\n"; + } + OS << "#endif\n\n"; +} + +void +NeonEmitter::genIntrinsicRangeCheckCode(raw_ostream &OS, + SmallVectorImpl<Intrinsic *> &Defs) { + OS << "#ifdef GET_NEON_IMMEDIATE_CHECK\n"; + + std::set<std::string> Emitted; + + for (auto *Def : Defs) { + if (Def->hasBody()) + continue; + // Functions with 'a' (the splat code) in the type prototype should not get + // their own builtin as they use the non-splat variant. + if (Def->hasSplat()) + continue; + // Functions which do not have an immediate do not need to have range + // checking code emitted. + if (!Def->hasImmediate()) + continue; + if (Emitted.find(Def->getMangledName()) != Emitted.end()) + continue; + + std::string LowerBound, UpperBound; + + Record *R = Def->getRecord(); + if (R->getValueAsBit("isVCVT_N")) { + // VCVT between floating- and fixed-point values takes an immediate + // in the range [1, 32) for f32 or [1, 64) for f64. + LowerBound = "1"; + if (Def->getBaseType().getElementSizeInBits() == 32) + UpperBound = "31"; + else + UpperBound = "63"; + } else if (R->getValueAsBit("isScalarShift")) { + // Right shifts have an 'r' in the name, left shifts do not. Convert + // instructions have the same bounds and right shifts. + if (Def->getName().find('r') != std::string::npos || + Def->getName().find("cvt") != std::string::npos) + LowerBound = "1"; + + UpperBound = utostr(Def->getReturnType().getElementSizeInBits() - 1); + } else if (R->getValueAsBit("isShift")) { + // Builtins which are overloaded by type will need to have their upper + // bound computed at Sema time based on the type constant. + + // Right shifts have an 'r' in the name, left shifts do not. + if (Def->getName().find('r') != std::string::npos) + LowerBound = "1"; + UpperBound = "RFT(TV, true)"; + } else if (Def->getClassKind(true) == ClassB) { + // ClassB intrinsics have a type (and hence lane number) that is only + // known at runtime. + if (R->getValueAsBit("isLaneQ")) + UpperBound = "RFT(TV, false, true)"; + else + UpperBound = "RFT(TV, false, false)"; + } else { + // The immediate generally refers to a lane in the preceding argument. + assert(Def->getImmediateIdx() > 0); + Type T = Def->getParamType(Def->getImmediateIdx() - 1); + UpperBound = utostr(T.getNumElements() - 1); + } + + // Calculate the index of the immediate that should be range checked. + unsigned Idx = Def->getNumParams(); + if (Def->hasImmediate()) + Idx = Def->getGeneratedParamIdx(Def->getImmediateIdx()); + + OS << "case NEON::BI__builtin_neon_" << Def->getMangledName() << ": " + << "i = " << Idx << ";"; + if (LowerBound.size()) + OS << " l = " << LowerBound << ";"; + if (UpperBound.size()) + OS << " u = " << UpperBound << ";"; + OS << " break;\n"; + + Emitted.insert(Def->getMangledName()); + } + + OS << "#endif\n\n"; +} + +/// runHeader - Emit a file with sections defining: +/// 1. the NEON section of BuiltinsARM.def and BuiltinsAArch64.def. +/// 2. the SemaChecking code for the type overload checking. +/// 3. the SemaChecking code for validation of intrinsic immediate arguments. +void NeonEmitter::runHeader(raw_ostream &OS) { + std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); + + SmallVector<Intrinsic *, 128> Defs; + for (auto *R : RV) + createIntrinsic(R, Defs); + + // Generate shared BuiltinsXXX.def + genBuiltinsDef(OS, Defs); + + // Generate ARM overloaded type checking code for SemaChecking.cpp + genOverloadTypeCheckCode(OS, Defs); + + // Generate ARM range checking code for shift/lane immediates. + genIntrinsicRangeCheckCode(OS, Defs); +} + +/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h +/// is comprised of type definitions and function declarations. +void NeonEmitter::run(raw_ostream &OS) { + OS << "/*===---- arm_neon.h - ARM Neon intrinsics " + "------------------------------" + "---===\n" + " *\n" + " * Permission is hereby granted, free of charge, to any person " + "obtaining " + "a copy\n" + " * of this software and associated documentation files (the " + "\"Software\")," + " to deal\n" + " * in the Software without restriction, including without limitation " + "the " + "rights\n" + " * to use, copy, modify, merge, publish, distribute, sublicense, " + "and/or sell\n" + " * copies of the Software, and to permit persons to whom the Software " + "is\n" + " * furnished to do so, subject to the following conditions:\n" + " *\n" + " * The above copyright notice and this permission notice shall be " + "included in\n" + " * all copies or substantial portions of the Software.\n" + " *\n" + " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, " + "EXPRESS OR\n" + " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " + "MERCHANTABILITY,\n" + " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT " + "SHALL THE\n" + " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR " + "OTHER\n" + " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, " + "ARISING FROM,\n" + " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " + "DEALINGS IN\n" + " * THE SOFTWARE.\n" + " *\n" + " *===-----------------------------------------------------------------" + "---" + "---===\n" + " */\n\n"; + + OS << "#ifndef __ARM_NEON_H\n"; + OS << "#define __ARM_NEON_H\n\n"; + + OS << "#if !defined(__ARM_NEON)\n"; + OS << "#error \"NEON support not enabled\"\n"; + OS << "#endif\n\n"; + + OS << "#include <stdint.h>\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"; + + // For now, signedness of polynomial types depends on target + OS << "#ifdef __aarch64__\n"; + OS << "typedef uint8_t poly8_t;\n"; + OS << "typedef uint16_t poly16_t;\n"; + OS << "typedef uint64_t poly64_t;\n"; + OS << "typedef __uint128_t poly128_t;\n"; + OS << "#else\n"; + OS << "typedef int8_t poly8_t;\n"; + OS << "typedef int16_t poly16_t;\n"; + OS << "#endif\n"; + + // Emit Neon vector typedefs. + std::string TypedefTypes( + "cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQdPcQPcPsQPsPlQPl"); + std::vector<TypeSpec> TDTypeVec = TypeSpec::fromTypeSpecs(TypedefTypes); + + // Emit vector typedefs. + bool InIfdef = false; + for (auto &TS : TDTypeVec) { + bool IsA64 = false; + Type T(TS, 'd'); + if (T.isDouble() || (T.isPoly() && T.isLong())) + IsA64 = true; + + if (InIfdef && !IsA64) { + OS << "#endif\n"; + InIfdef = false; + } + if (!InIfdef && IsA64) { + OS << "#ifdef __aarch64__\n"; + InIfdef = true; + } + + if (T.isPoly()) + OS << "typedef __attribute__((neon_polyvector_type("; + else + OS << "typedef __attribute__((neon_vector_type("; + + Type T2 = T; + T2.makeScalar(); + OS << utostr(T.getNumElements()) << "))) "; + OS << T2.str(); + OS << " " << T.str() << ";\n"; + } + if (InIfdef) + OS << "#endif\n"; + OS << "\n"; + + // Emit struct typedefs. + InIfdef = false; + for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) { + for (auto &TS : TDTypeVec) { + bool IsA64 = false; + Type T(TS, 'd'); + if (T.isDouble() || (T.isPoly() && T.isLong())) + IsA64 = true; + + if (InIfdef && !IsA64) { + OS << "#endif\n"; + InIfdef = false; + } + if (!InIfdef && IsA64) { + OS << "#ifdef __aarch64__\n"; + InIfdef = true; + } + + char M = '2' + (NumMembers - 2); + Type VT(TS, M); + OS << "typedef struct " << VT.str() << " {\n"; + OS << " " << T.str() << " val"; + OS << "[" << utostr(NumMembers) << "]"; + OS << ";\n} "; + OS << VT.str() << ";\n"; + OS << "\n"; + } + } + if (InIfdef) + OS << "#endif\n"; + OS << "\n"; + + OS << "#define __ai static inline __attribute__((__always_inline__, " + "__nodebug__))\n\n"; + + SmallVector<Intrinsic *, 128> Defs; + std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); + for (auto *R : RV) + createIntrinsic(R, Defs); + + for (auto *I : Defs) + I->indexBody(); + + std::stable_sort( + Defs.begin(), Defs.end(), + [](const Intrinsic *A, const Intrinsic *B) { return *A < *B; }); + + // Only emit a def when its requirements have been met. + // FIXME: This loop could be made faster, but it's fast enough for now. + bool MadeProgress = true; + std::string InGuard = ""; + while (!Defs.empty() && MadeProgress) { + MadeProgress = false; + + for (SmallVector<Intrinsic *, 128>::iterator I = Defs.begin(); + I != Defs.end(); /*No step*/) { + bool DependenciesSatisfied = true; + for (auto *II : (*I)->getDependencies()) { + if (std::find(Defs.begin(), Defs.end(), II) != Defs.end()) + DependenciesSatisfied = false; + } + if (!DependenciesSatisfied) { + // Try the next one. + ++I; + continue; + } + + // Emit #endif/#if pair if needed. + if ((*I)->getGuard() != InGuard) { + if (!InGuard.empty()) + OS << "#endif\n"; + InGuard = (*I)->getGuard(); + if (!InGuard.empty()) + OS << "#if " << InGuard << "\n"; + } + + // Actually generate the intrinsic code. + OS << (*I)->generate(); + + MadeProgress = true; + I = Defs.erase(I); + } + } + assert(Defs.empty() && "Some requirements were not satisfied!"); + if (!InGuard.empty()) + OS << "#endif\n"; + + OS << "\n"; + OS << "#undef __ai\n\n"; + OS << "#endif /* __ARM_NEON_H */\n"; +} + +namespace clang { +void EmitNeon(RecordKeeper &Records, raw_ostream &OS) { + NeonEmitter(Records).run(OS); +} +void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { + NeonEmitter(Records).runHeader(OS); +} +void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { + llvm_unreachable("Neon test generation no longer implemented!"); +} +} // End namespace clang diff --git a/contrib/llvm/tools/clang/utils/TableGen/TableGen.cpp b/contrib/llvm/tools/clang/utils/TableGen/TableGen.cpp new file mode 100644 index 0000000..4484e65 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/TableGen.cpp @@ -0,0 +1,257 @@ +//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the main function for Clang's TableGen. +// +//===----------------------------------------------------------------------===// + +#include "TableGenBackends.h" // Declares all backends. +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Main.h" +#include "llvm/TableGen/Record.h" + +using namespace llvm; +using namespace clang; + +enum ActionType { + GenClangAttrClasses, + GenClangAttrParserStringSwitches, + GenClangAttrImpl, + GenClangAttrList, + GenClangAttrPCHRead, + GenClangAttrPCHWrite, + GenClangAttrHasAttributeImpl, + GenClangAttrSpellingListIndex, + GenClangAttrASTVisitor, + GenClangAttrTemplateInstantiate, + GenClangAttrParsedAttrList, + GenClangAttrParsedAttrImpl, + GenClangAttrParsedAttrKinds, + GenClangAttrDump, + GenClangDiagsDefs, + GenClangDiagGroups, + GenClangDiagsIndexName, + GenClangCommentNodes, + GenClangDeclNodes, + GenClangStmtNodes, + GenClangSACheckers, + GenClangCommentHTMLTags, + GenClangCommentHTMLTagsProperties, + GenClangCommentHTMLNamedCharacterReferences, + GenClangCommentCommandInfo, + GenClangCommentCommandList, + GenArmNeon, + GenArmNeonSema, + GenArmNeonTest, + GenAttrDocs +}; + +namespace { +cl::opt<ActionType> Action( + cl::desc("Action to perform:"), + cl::values( + clEnumValN(GenClangAttrClasses, "gen-clang-attr-classes", + "Generate clang attribute clases"), + clEnumValN(GenClangAttrParserStringSwitches, + "gen-clang-attr-parser-string-switches", + "Generate all parser-related attribute string switches"), + clEnumValN(GenClangAttrImpl, "gen-clang-attr-impl", + "Generate clang attribute implementations"), + clEnumValN(GenClangAttrList, "gen-clang-attr-list", + "Generate a clang attribute list"), + clEnumValN(GenClangAttrPCHRead, "gen-clang-attr-pch-read", + "Generate clang PCH attribute reader"), + clEnumValN(GenClangAttrPCHWrite, "gen-clang-attr-pch-write", + "Generate clang PCH attribute writer"), + clEnumValN(GenClangAttrHasAttributeImpl, + "gen-clang-attr-has-attribute-impl", + "Generate a clang attribute spelling list"), + clEnumValN(GenClangAttrSpellingListIndex, + "gen-clang-attr-spelling-index", + "Generate a clang attribute spelling index"), + clEnumValN(GenClangAttrASTVisitor, + "gen-clang-attr-ast-visitor", + "Generate a recursive AST visitor for clang attributes"), + clEnumValN(GenClangAttrTemplateInstantiate, + "gen-clang-attr-template-instantiate", + "Generate a clang template instantiate code"), + clEnumValN(GenClangAttrParsedAttrList, + "gen-clang-attr-parsed-attr-list", + "Generate a clang parsed attribute list"), + clEnumValN(GenClangAttrParsedAttrImpl, + "gen-clang-attr-parsed-attr-impl", + "Generate the clang parsed attribute helpers"), + clEnumValN(GenClangAttrParsedAttrKinds, + "gen-clang-attr-parsed-attr-kinds", + "Generate a clang parsed attribute kinds"), + clEnumValN(GenClangAttrDump, "gen-clang-attr-dump", + "Generate clang attribute dumper"), + clEnumValN(GenClangDiagsDefs, "gen-clang-diags-defs", + "Generate Clang diagnostics definitions"), + clEnumValN(GenClangDiagGroups, "gen-clang-diag-groups", + "Generate Clang diagnostic groups"), + clEnumValN(GenClangDiagsIndexName, "gen-clang-diags-index-name", + "Generate Clang diagnostic name index"), + clEnumValN(GenClangCommentNodes, "gen-clang-comment-nodes", + "Generate Clang AST comment nodes"), + clEnumValN(GenClangDeclNodes, "gen-clang-decl-nodes", + "Generate Clang AST declaration nodes"), + clEnumValN(GenClangStmtNodes, "gen-clang-stmt-nodes", + "Generate Clang AST statement nodes"), + clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers", + "Generate Clang Static Analyzer checkers"), + clEnumValN(GenClangCommentHTMLTags, "gen-clang-comment-html-tags", + "Generate efficient matchers for HTML tag " + "names that are used in documentation comments"), + clEnumValN(GenClangCommentHTMLTagsProperties, + "gen-clang-comment-html-tags-properties", + "Generate efficient matchers for HTML tag " + "properties"), + clEnumValN(GenClangCommentHTMLNamedCharacterReferences, + "gen-clang-comment-html-named-character-references", + "Generate function to translate named character " + "references to UTF-8 sequences"), + clEnumValN(GenClangCommentCommandInfo, "gen-clang-comment-command-info", + "Generate command properties for commands that " + "are used in documentation comments"), + clEnumValN(GenClangCommentCommandList, "gen-clang-comment-command-list", + "Generate list of commands that are used in " + "documentation comments"), + clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"), + clEnumValN(GenArmNeonSema, "gen-arm-neon-sema", + "Generate ARM NEON sema support for clang"), + clEnumValN(GenArmNeonTest, "gen-arm-neon-test", + "Generate ARM NEON tests for clang"), + clEnumValN(GenAttrDocs, "gen-attr-docs", + "Generate attribute documentation"), + clEnumValEnd)); + +cl::opt<std::string> +ClangComponent("clang-component", + cl::desc("Only use warnings from specified component"), + cl::value_desc("component"), cl::Hidden); + +bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { + switch (Action) { + case GenClangAttrClasses: + EmitClangAttrClass(Records, OS); + break; + case GenClangAttrParserStringSwitches: + EmitClangAttrParserStringSwitches(Records, OS); + break; + case GenClangAttrImpl: + EmitClangAttrImpl(Records, OS); + break; + case GenClangAttrList: + EmitClangAttrList(Records, OS); + break; + case GenClangAttrPCHRead: + EmitClangAttrPCHRead(Records, OS); + break; + case GenClangAttrPCHWrite: + EmitClangAttrPCHWrite(Records, OS); + break; + case GenClangAttrHasAttributeImpl: + EmitClangAttrHasAttrImpl(Records, OS); + break; + case GenClangAttrSpellingListIndex: + EmitClangAttrSpellingListIndex(Records, OS); + break; + case GenClangAttrASTVisitor: + EmitClangAttrASTVisitor(Records, OS); + break; + case GenClangAttrTemplateInstantiate: + EmitClangAttrTemplateInstantiate(Records, OS); + break; + case GenClangAttrParsedAttrList: + EmitClangAttrParsedAttrList(Records, OS); + break; + case GenClangAttrParsedAttrImpl: + EmitClangAttrParsedAttrImpl(Records, OS); + break; + case GenClangAttrParsedAttrKinds: + EmitClangAttrParsedAttrKinds(Records, OS); + break; + case GenClangAttrDump: + EmitClangAttrDump(Records, OS); + break; + case GenClangDiagsDefs: + EmitClangDiagsDefs(Records, OS, ClangComponent); + break; + case GenClangDiagGroups: + EmitClangDiagGroups(Records, OS); + break; + case GenClangDiagsIndexName: + EmitClangDiagsIndexName(Records, OS); + break; + case GenClangCommentNodes: + EmitClangASTNodes(Records, OS, "Comment", ""); + break; + case GenClangDeclNodes: + EmitClangASTNodes(Records, OS, "Decl", "Decl"); + EmitClangDeclContext(Records, OS); + break; + case GenClangStmtNodes: + EmitClangASTNodes(Records, OS, "Stmt", ""); + break; + case GenClangSACheckers: + EmitClangSACheckers(Records, OS); + break; + case GenClangCommentHTMLTags: + EmitClangCommentHTMLTags(Records, OS); + break; + case GenClangCommentHTMLTagsProperties: + EmitClangCommentHTMLTagsProperties(Records, OS); + break; + case GenClangCommentHTMLNamedCharacterReferences: + EmitClangCommentHTMLNamedCharacterReferences(Records, OS); + break; + case GenClangCommentCommandInfo: + EmitClangCommentCommandInfo(Records, OS); + break; + case GenClangCommentCommandList: + EmitClangCommentCommandList(Records, OS); + break; + case GenArmNeon: + EmitNeon(Records, OS); + break; + case GenArmNeonSema: + EmitNeonSema(Records, OS); + break; + case GenArmNeonTest: + EmitNeonTest(Records, OS); + break; + case GenAttrDocs: + EmitClangAttrDocs(Records, OS); + break; + } + + return false; +} +} + +int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); + + return TableGenMain(argv[0], &ClangTableGenMain); +} + +#ifdef __has_feature +#if __has_feature(address_sanitizer) +#include <sanitizer/lsan_interface.h> +// Disable LeakSanitizer for this binary as it has too many leaks that are not +// very interesting to fix. See compiler-rt/include/sanitizer/lsan_interface.h . +int __lsan_is_turned_off() { return 1; } +#endif // __has_feature(address_sanitizer) +#endif // defined(__has_feature) diff --git a/contrib/llvm/tools/clang/utils/TableGen/TableGenBackends.h b/contrib/llvm/tools/clang/utils/TableGen/TableGenBackends.h new file mode 100644 index 0000000..4adf368 --- /dev/null +++ b/contrib/llvm/tools/clang/utils/TableGen/TableGenBackends.h @@ -0,0 +1,75 @@ +//===- TableGenBackends.h - Declarations for Clang TableGen Backends ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations for all of the Clang TableGen +// backends. A "TableGen backend" is just a function. See +// "$LLVM_ROOT/utils/TableGen/TableGenBackends.h" for more info. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UTILS_TABLEGEN_TABLEGENBACKENDS_H +#define LLVM_CLANG_UTILS_TABLEGEN_TABLEGENBACKENDS_H + +#include <string> + +namespace llvm { + class raw_ostream; + class RecordKeeper; +} + +using llvm::raw_ostream; +using llvm::RecordKeeper; + +namespace clang { + +void EmitClangDeclContext(RecordKeeper &RK, raw_ostream &OS); +void EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS, + const std::string &N, const std::string &S); + +void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS); +void EmitClangAttrDump(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, + const std::string &Component); +void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS); +void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS); +void EmitClangCommentHTMLTagsProperties(RecordKeeper &Records, raw_ostream &OS); +void EmitClangCommentHTMLNamedCharacterReferences(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS); +void EmitClangCommentCommandList(RecordKeeper &Records, raw_ostream &OS); + +void EmitNeon(RecordKeeper &Records, raw_ostream &OS); +void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS); +void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS); +void EmitNeon2(RecordKeeper &Records, raw_ostream &OS); +void EmitNeonSema2(RecordKeeper &Records, raw_ostream &OS); +void EmitNeonTest2(RecordKeeper &Records, raw_ostream &OS); + +void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS); + +} // end namespace clang + +#endif |