| 1 | //===-- ClangASTNodesEmitter.cpp - Generate Clang AST node tables ---------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // These tablegen backends emit Clang AST node tables |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "ASTTableGen.h" |
| 14 | #include "TableGenBackends.h" |
| 15 | |
| 16 | #include "llvm/TableGen/Error.h" |
| 17 | #include "llvm/TableGen/Record.h" |
| 18 | #include "llvm/TableGen/TableGenBackend.h" |
| 19 | #include <map> |
| 20 | #include <set> |
| 21 | #include <string> |
| 22 | using namespace llvm; |
| 23 | using namespace clang; |
| 24 | using namespace clang::tblgen; |
| 25 | |
| 26 | /// ClangASTNodesEmitter - The top-level class emits .inc files containing |
| 27 | /// declarations of Clang statements. |
| 28 | /// |
| 29 | namespace { |
| 30 | class ClangASTNodesEmitter { |
| 31 | // A map from a node to each of its derived nodes. |
| 32 | typedef std::multimap<ASTNode, ASTNode> ChildMap; |
| 33 | typedef ChildMap::const_iterator ChildIterator; |
| 34 | |
| 35 | std::set<ASTNode> PrioritizedClasses; |
| 36 | const RecordKeeper &Records; |
| 37 | ASTNode Root; |
| 38 | const std::string &NodeClassName; |
| 39 | const std::string &BaseSuffix; |
| 40 | std::string MacroHierarchyName; |
| 41 | ChildMap Tree; |
| 42 | |
| 43 | // Create a macro-ized version of a name |
| 44 | static std::string macroName(StringRef S) { return S.upper(); } |
| 45 | |
| 46 | const std::string ¯oHierarchyName() { |
| 47 | assert(Root && "root node not yet derived!" ); |
| 48 | if (MacroHierarchyName.empty()) |
| 49 | MacroHierarchyName = macroName(S: Root.getName()); |
| 50 | return MacroHierarchyName; |
| 51 | } |
| 52 | |
| 53 | // Return the name to be printed in the base field. Normally this is |
| 54 | // the record's name plus the base suffix, but if it is the root node and |
| 55 | // the suffix is non-empty, it's just the suffix. |
| 56 | std::string baseName(ASTNode node) { |
| 57 | if (node == Root && !BaseSuffix.empty()) |
| 58 | return BaseSuffix; |
| 59 | |
| 60 | return node.getName().str() + BaseSuffix; |
| 61 | } |
| 62 | |
| 63 | void deriveChildTree(); |
| 64 | |
| 65 | std::pair<ASTNode, ASTNode> EmitNode(raw_ostream& OS, ASTNode Base); |
| 66 | public: |
| 67 | explicit ClangASTNodesEmitter(const RecordKeeper &R, const std::string &N, |
| 68 | const std::string &S, |
| 69 | std::string_view PriorizeIfSubclassOf) |
| 70 | : Records(R), NodeClassName(N), BaseSuffix(S) { |
| 71 | ArrayRef<const Record *> vecPrioritized = |
| 72 | R.getAllDerivedDefinitionsIfDefined(ClassName: PriorizeIfSubclassOf); |
| 73 | PrioritizedClasses = |
| 74 | std::set<ASTNode>(vecPrioritized.begin(), vecPrioritized.end()); |
| 75 | } |
| 76 | |
| 77 | // run - Output the .inc file contents |
| 78 | void run(raw_ostream &OS); |
| 79 | }; |
| 80 | } // end anonymous namespace |
| 81 | |
| 82 | //===----------------------------------------------------------------------===// |
| 83 | // Statement Node Tables (.inc file) generation. |
| 84 | //===----------------------------------------------------------------------===// |
| 85 | |
| 86 | // Returns the first and last non-abstract subrecords |
| 87 | // Called recursively to ensure that nodes remain contiguous |
| 88 | std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS, |
| 89 | ASTNode Base) { |
| 90 | std::string BaseName = macroName(S: Base.getName()); |
| 91 | |
| 92 | auto [II, E] = Tree.equal_range(x: Base); |
| 93 | bool HasChildren = II != E; |
| 94 | |
| 95 | ASTNode First, Last; |
| 96 | if (!Base.isAbstract()) |
| 97 | First = Last = Base; |
| 98 | |
| 99 | auto Comp = [this](const ASTNode &LHS, const ASTNode &RHS) { |
| 100 | bool LHSPrioritized = PrioritizedClasses.count(x: LHS) > 0; |
| 101 | bool RHSPrioritized = PrioritizedClasses.count(x: RHS) > 0; |
| 102 | return std::tuple(LHSPrioritized, LHS.getName()) > |
| 103 | std::tuple(RHSPrioritized, RHS.getName()); |
| 104 | }; |
| 105 | auto SortedChildren = std::set<ASTNode, decltype(Comp)>(Comp); |
| 106 | |
| 107 | for (; II != E; ++II) { |
| 108 | SortedChildren.insert(x: II->second); |
| 109 | } |
| 110 | |
| 111 | for (const auto &Child : SortedChildren) { |
| 112 | bool Abstract = Child.isAbstract(); |
| 113 | std::string NodeName = macroName(S: Child.getName()); |
| 114 | |
| 115 | OS << "#ifndef " << NodeName << "\n" ; |
| 116 | OS << "# define " << NodeName << "(Type, Base) " |
| 117 | << BaseName << "(Type, Base)\n" ; |
| 118 | OS << "#endif\n" ; |
| 119 | |
| 120 | if (Abstract) OS << "ABSTRACT_" << macroHierarchyName() << "(" ; |
| 121 | OS << NodeName << "(" << Child.getName() << ", " << baseName(node: Base) << ")" ; |
| 122 | if (Abstract) OS << ")" ; |
| 123 | OS << "\n" ; |
| 124 | |
| 125 | auto Result = EmitNode(OS, Base: Child); |
| 126 | assert(Result.first && Result.second && "node didn't have children?" ); |
| 127 | |
| 128 | // Update the range of Base. |
| 129 | if (!First) First = Result.first; |
| 130 | Last = Result.second; |
| 131 | |
| 132 | OS << "#undef " << NodeName << "\n\n" ; |
| 133 | } |
| 134 | |
| 135 | // If there aren't first/last nodes, it must be because there were no |
| 136 | // children and this node was abstract, which is not a sensible combination. |
| 137 | if (!First) |
| 138 | PrintFatalError(ErrorLoc: Base.getLoc(), Msg: "abstract node has no children" ); |
| 139 | assert(Last && "set First without Last" ); |
| 140 | |
| 141 | if (HasChildren) { |
| 142 | // Use FOO_RANGE unless this is the last of the ranges, in which case |
| 143 | // use LAST_FOO_RANGE. |
| 144 | if (Base == Root) |
| 145 | OS << "LAST_" << macroHierarchyName() << "_RANGE(" ; |
| 146 | else |
| 147 | OS << macroHierarchyName() << "_RANGE(" ; |
| 148 | OS << Base.getName() << ", " << First.getName() << ", " |
| 149 | << Last.getName() << ")\n\n" ; |
| 150 | } |
| 151 | |
| 152 | return std::make_pair(x&: First, y&: Last); |
| 153 | } |
| 154 | |
| 155 | void ClangASTNodesEmitter::deriveChildTree() { |
| 156 | assert(!Root && "already computed tree" ); |
| 157 | |
| 158 | // Emit statements |
| 159 | for (const Record *R : Records.getAllDerivedDefinitions(ClassName: NodeClassName)) { |
| 160 | if (auto B = R->getValueAsOptionalDef(BaseFieldName)) |
| 161 | Tree.insert(x: {B, R}); |
| 162 | else if (Root) |
| 163 | PrintFatalError(ErrorLoc: R->getLoc(), |
| 164 | Msg: Twine("multiple root nodes in \"" ) + NodeClassName |
| 165 | + "\" hierarchy" ); |
| 166 | else |
| 167 | Root = R; |
| 168 | } |
| 169 | |
| 170 | if (!Root) |
| 171 | PrintFatalError(Msg: Twine("didn't find root node in \"" ) + NodeClassName |
| 172 | + "\" hierarchy" ); |
| 173 | } |
| 174 | |
| 175 | void ClangASTNodesEmitter::run(raw_ostream &OS) { |
| 176 | deriveChildTree(); |
| 177 | |
| 178 | emitSourceFileHeader(Desc: "List of AST nodes of a particular kind" , OS, Record: Records); |
| 179 | |
| 180 | // Write the preamble |
| 181 | OS << "#ifndef ABSTRACT_" << macroHierarchyName() << "\n" ; |
| 182 | OS << "# define ABSTRACT_" << macroHierarchyName() << "(Type) Type\n" ; |
| 183 | OS << "#endif\n" ; |
| 184 | |
| 185 | OS << "#ifndef " << macroHierarchyName() << "_RANGE\n" ; |
| 186 | OS << "# define " |
| 187 | << macroHierarchyName() << "_RANGE(Base, First, Last)\n" ; |
| 188 | OS << "#endif\n\n" ; |
| 189 | |
| 190 | OS << "#ifndef LAST_" << macroHierarchyName() << "_RANGE\n" ; |
| 191 | OS << "# define LAST_" << macroHierarchyName() |
| 192 | << "_RANGE(Base, First, Last) " << macroHierarchyName() |
| 193 | << "_RANGE(Base, First, Last)\n" ; |
| 194 | OS << "#endif\n\n" ; |
| 195 | |
| 196 | EmitNode(OS, Base: Root); |
| 197 | |
| 198 | OS << "#undef " << macroHierarchyName() << "\n" ; |
| 199 | OS << "#undef " << macroHierarchyName() << "_RANGE\n" ; |
| 200 | OS << "#undef LAST_" << macroHierarchyName() << "_RANGE\n" ; |
| 201 | OS << "#undef ABSTRACT_" << macroHierarchyName() << "\n" ; |
| 202 | } |
| 203 | |
| 204 | void clang::EmitClangASTNodes(const RecordKeeper &RK, raw_ostream &OS, |
| 205 | const std::string &N, const std::string &S, |
| 206 | std::string_view PriorizeIfSubclassOf) { |
| 207 | ClangASTNodesEmitter(RK, N, S, PriorizeIfSubclassOf).run(OS); |
| 208 | } |
| 209 | |
| 210 | static void |
| 211 | printDeclContext(const std::multimap<const Record *, const Record *> &Tree, |
| 212 | const Record *DeclContext, raw_ostream &OS) { |
| 213 | if (!DeclContext->getValueAsBit(AbstractFieldName)) |
| 214 | OS << "DECL_CONTEXT(" << DeclContext->getName() << ")\n" ; |
| 215 | auto [II, E] = Tree.equal_range(x: DeclContext); |
| 216 | for (; II != E; ++II) { |
| 217 | printDeclContext(Tree, DeclContext: II->second, OS); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | // Emits and addendum to a .inc file to enumerate the clang declaration |
| 222 | // contexts. |
| 223 | void clang::EmitClangDeclContext(const RecordKeeper &Records, raw_ostream &OS) { |
| 224 | // FIXME: Find a .td file format to allow for this to be represented better. |
| 225 | |
| 226 | emitSourceFileHeader(Desc: "List of AST Decl nodes" , OS, Record: Records); |
| 227 | |
| 228 | OS << "#ifndef DECL_CONTEXT\n" ; |
| 229 | OS << "# define DECL_CONTEXT(DECL)\n" ; |
| 230 | OS << "#endif\n" ; |
| 231 | |
| 232 | std::multimap<const Record *, const Record *> Tree; |
| 233 | |
| 234 | for (const Record *R : Records.getAllDerivedDefinitions(DeclNodeClassName)) { |
| 235 | if (auto *B = R->getValueAsOptionalDef(BaseFieldName)) |
| 236 | Tree.insert(x: {B, R}); |
| 237 | } |
| 238 | |
| 239 | for (const Record *DeclContext : |
| 240 | Records.getAllDerivedDefinitions(DeclContextNodeClassName)) { |
| 241 | printDeclContext(Tree, DeclContext, OS); |
| 242 | } |
| 243 | |
| 244 | OS << "#undef DECL_CONTEXT\n" ; |
| 245 | } |
| 246 | |