1 | //===- DirectiveEmitter.cpp - Directive Language Emitter ------------------===// |
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 | // DirectiveEmitter uses the descriptions of directives and clauses to construct |
10 | // common code declarations to be used in Frontends. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/TableGen/DirectiveEmitter.h" |
15 | #include "llvm/ADT/DenseMap.h" |
16 | #include "llvm/ADT/DenseSet.h" |
17 | #include "llvm/ADT/STLExtras.h" |
18 | #include "llvm/ADT/SmallVector.h" |
19 | #include "llvm/ADT/StringSet.h" |
20 | #include "llvm/ADT/StringSwitch.h" |
21 | #include "llvm/TableGen/Error.h" |
22 | #include "llvm/TableGen/Record.h" |
23 | #include "llvm/TableGen/TableGenBackend.h" |
24 | |
25 | #include <numeric> |
26 | #include <vector> |
27 | |
28 | using namespace llvm; |
29 | |
30 | namespace { |
31 | // Simple RAII helper for defining ifdef-undef-endif scopes. |
32 | class IfDefScope { |
33 | public: |
34 | IfDefScope(StringRef Name, raw_ostream &OS) : Name(Name), OS(OS) { |
35 | OS << "#ifdef " << Name << "\n" |
36 | << "#undef " << Name << "\n" ; |
37 | } |
38 | |
39 | ~IfDefScope() { OS << "\n#endif // " << Name << "\n\n" ; } |
40 | |
41 | private: |
42 | StringRef Name; |
43 | raw_ostream &OS; |
44 | }; |
45 | } // namespace |
46 | |
47 | // Generate enum class. Entries are emitted in the order in which they appear |
48 | // in the `Records` vector. |
49 | static void GenerateEnumClass(const std::vector<Record *> &Records, |
50 | raw_ostream &OS, StringRef Enum, StringRef Prefix, |
51 | const DirectiveLanguage &DirLang, |
52 | bool ExportEnums) { |
53 | OS << "\n" ; |
54 | OS << "enum class " << Enum << " {\n" ; |
55 | for (const auto &R : Records) { |
56 | BaseRecord Rec{R}; |
57 | OS << " " << Prefix << Rec.getFormattedName() << ",\n" ; |
58 | } |
59 | OS << "};\n" ; |
60 | OS << "\n" ; |
61 | OS << "static constexpr std::size_t " << Enum |
62 | << "_enumSize = " << Records.size() << ";\n" ; |
63 | |
64 | // Make the enum values available in the defined namespace. This allows us to |
65 | // write something like Enum_X if we have a `using namespace <CppNamespace>`. |
66 | // At the same time we do not loose the strong type guarantees of the enum |
67 | // class, that is we cannot pass an unsigned as Directive without an explicit |
68 | // cast. |
69 | if (ExportEnums) { |
70 | OS << "\n" ; |
71 | for (const auto &R : Records) { |
72 | BaseRecord Rec{R}; |
73 | OS << "constexpr auto " << Prefix << Rec.getFormattedName() << " = " |
74 | << "llvm::" << DirLang.getCppNamespace() << "::" << Enum |
75 | << "::" << Prefix << Rec.getFormattedName() << ";\n" ; |
76 | } |
77 | } |
78 | } |
79 | |
80 | // Generate enums for values that clauses can take. |
81 | // Also generate function declarations for get<Enum>Name(StringRef Str). |
82 | static void GenerateEnumClauseVal(const std::vector<Record *> &Records, |
83 | raw_ostream &OS, |
84 | const DirectiveLanguage &DirLang, |
85 | std::string &EnumHelperFuncs) { |
86 | for (const auto &R : Records) { |
87 | Clause C{R}; |
88 | const auto &ClauseVals = C.getClauseVals(); |
89 | if (ClauseVals.size() <= 0) |
90 | continue; |
91 | |
92 | const auto &EnumName = C.getEnumName(); |
93 | if (EnumName.size() == 0) { |
94 | PrintError(Msg: "enumClauseValue field not set in Clause" + |
95 | C.getFormattedName() + "." ); |
96 | return; |
97 | } |
98 | |
99 | OS << "\n" ; |
100 | OS << "enum class " << EnumName << " {\n" ; |
101 | for (const auto &CV : ClauseVals) { |
102 | ClauseVal CVal{CV}; |
103 | OS << " " << CV->getName() << "=" << CVal.getValue() << ",\n" ; |
104 | } |
105 | OS << "};\n" ; |
106 | |
107 | if (DirLang.hasMakeEnumAvailableInNamespace()) { |
108 | OS << "\n" ; |
109 | for (const auto &CV : ClauseVals) { |
110 | OS << "constexpr auto " << CV->getName() << " = " |
111 | << "llvm::" << DirLang.getCppNamespace() << "::" << EnumName |
112 | << "::" << CV->getName() << ";\n" ; |
113 | } |
114 | EnumHelperFuncs += (llvm::Twine(EnumName) + llvm::Twine(" get" ) + |
115 | llvm::Twine(EnumName) + llvm::Twine("(StringRef);\n" )) |
116 | .str(); |
117 | |
118 | EnumHelperFuncs += |
119 | (llvm::Twine("llvm::StringRef get" ) + llvm::Twine(DirLang.getName()) + |
120 | llvm::Twine(EnumName) + llvm::Twine("Name(" ) + |
121 | llvm::Twine(EnumName) + llvm::Twine(");\n" )) |
122 | .str(); |
123 | } |
124 | } |
125 | } |
126 | |
127 | static bool HasDuplicateClauses(const std::vector<Record *> &Clauses, |
128 | const Directive &Directive, |
129 | llvm::StringSet<> &CrtClauses) { |
130 | bool HasError = false; |
131 | for (const auto &C : Clauses) { |
132 | VersionedClause VerClause{C}; |
133 | const auto insRes = CrtClauses.insert(key: VerClause.getClause().getName()); |
134 | if (!insRes.second) { |
135 | PrintError(Msg: "Clause " + VerClause.getClause().getRecordName() + |
136 | " already defined on directive " + Directive.getRecordName()); |
137 | HasError = true; |
138 | } |
139 | } |
140 | return HasError; |
141 | } |
142 | |
143 | // Check for duplicate clauses in lists. Clauses cannot appear twice in the |
144 | // three allowed list. Also, since required implies allowed, clauses cannot |
145 | // appear in both the allowedClauses and requiredClauses lists. |
146 | static bool |
147 | HasDuplicateClausesInDirectives(const std::vector<Record *> &Directives) { |
148 | bool HasDuplicate = false; |
149 | for (const auto &D : Directives) { |
150 | Directive Dir{D}; |
151 | llvm::StringSet<> Clauses; |
152 | // Check for duplicates in the three allowed lists. |
153 | if (HasDuplicateClauses(Clauses: Dir.getAllowedClauses(), Directive: Dir, CrtClauses&: Clauses) || |
154 | HasDuplicateClauses(Clauses: Dir.getAllowedOnceClauses(), Directive: Dir, CrtClauses&: Clauses) || |
155 | HasDuplicateClauses(Clauses: Dir.getAllowedExclusiveClauses(), Directive: Dir, CrtClauses&: Clauses)) { |
156 | HasDuplicate = true; |
157 | } |
158 | // Check for duplicate between allowedClauses and required |
159 | Clauses.clear(); |
160 | if (HasDuplicateClauses(Clauses: Dir.getAllowedClauses(), Directive: Dir, CrtClauses&: Clauses) || |
161 | HasDuplicateClauses(Clauses: Dir.getRequiredClauses(), Directive: Dir, CrtClauses&: Clauses)) { |
162 | HasDuplicate = true; |
163 | } |
164 | if (HasDuplicate) |
165 | PrintFatalError(Msg: "One or more clauses are defined multiple times on" |
166 | " directive " + |
167 | Dir.getRecordName()); |
168 | } |
169 | |
170 | return HasDuplicate; |
171 | } |
172 | |
173 | // Check consitency of records. Return true if an error has been detected. |
174 | // Return false if the records are valid. |
175 | bool DirectiveLanguage::HasValidityErrors() const { |
176 | if (getDirectiveLanguages().size() != 1) { |
177 | PrintFatalError(Msg: "A single definition of DirectiveLanguage is needed." ); |
178 | return true; |
179 | } |
180 | |
181 | return HasDuplicateClausesInDirectives(Directives: getDirectives()); |
182 | } |
183 | |
184 | // Count the maximum number of leaf constituents per construct. |
185 | static size_t GetMaxLeafCount(const DirectiveLanguage &DirLang) { |
186 | size_t MaxCount = 0; |
187 | for (Record *R : DirLang.getDirectives()) { |
188 | size_t Count = Directive{R}.getLeafConstructs().size(); |
189 | MaxCount = std::max(a: MaxCount, b: Count); |
190 | } |
191 | return MaxCount; |
192 | } |
193 | |
194 | // Generate the declaration section for the enumeration in the directive |
195 | // language |
196 | static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) { |
197 | const auto DirLang = DirectiveLanguage{Records}; |
198 | if (DirLang.HasValidityErrors()) |
199 | return; |
200 | |
201 | OS << "#ifndef LLVM_" << DirLang.getName() << "_INC\n" ; |
202 | OS << "#define LLVM_" << DirLang.getName() << "_INC\n" ; |
203 | OS << "\n#include \"llvm/ADT/ArrayRef.h\"\n" ; |
204 | |
205 | if (DirLang.hasEnableBitmaskEnumInNamespace()) |
206 | OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n" ; |
207 | |
208 | OS << "#include <cstddef>\n" ; // for size_t |
209 | OS << "\n" ; |
210 | OS << "namespace llvm {\n" ; |
211 | OS << "class StringRef;\n" ; |
212 | |
213 | // Open namespaces defined in the directive language |
214 | llvm::SmallVector<StringRef, 2> Namespaces; |
215 | llvm::SplitString(Source: DirLang.getCppNamespace(), OutFragments&: Namespaces, Delimiters: "::" ); |
216 | for (auto Ns : Namespaces) |
217 | OS << "namespace " << Ns << " {\n" ; |
218 | |
219 | if (DirLang.hasEnableBitmaskEnumInNamespace()) |
220 | OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n" ; |
221 | |
222 | // Emit Directive associations |
223 | std::vector<Record *> associations; |
224 | llvm::copy_if( |
225 | Range: DirLang.getAssociations(), Out: std::back_inserter(x&: associations), |
226 | // Skip the "special" value |
227 | P: [](const Record *Def) { return Def->getName() != "AS_FromLeaves" ; }); |
228 | GenerateEnumClass(Records: associations, OS, Enum: "Association" , |
229 | /*Prefix=*/"" , DirLang, /*ExportEnums=*/false); |
230 | |
231 | GenerateEnumClass(Records: DirLang.getCategories(), OS, Enum: "Category" , /*Prefix=*/"" , |
232 | DirLang, /*ExportEnums=*/false); |
233 | |
234 | // Emit Directive enumeration |
235 | GenerateEnumClass(Records: DirLang.getDirectives(), OS, Enum: "Directive" , |
236 | Prefix: DirLang.getDirectivePrefix(), DirLang, |
237 | ExportEnums: DirLang.hasMakeEnumAvailableInNamespace()); |
238 | |
239 | // Emit Clause enumeration |
240 | GenerateEnumClass(Records: DirLang.getClauses(), OS, Enum: "Clause" , |
241 | Prefix: DirLang.getClausePrefix(), DirLang, |
242 | ExportEnums: DirLang.hasMakeEnumAvailableInNamespace()); |
243 | |
244 | // Emit ClauseVal enumeration |
245 | std::string EnumHelperFuncs; |
246 | GenerateEnumClauseVal(Records: DirLang.getClauses(), OS, DirLang, EnumHelperFuncs); |
247 | |
248 | // Generic function signatures |
249 | OS << "\n" ; |
250 | OS << "// Enumeration helper functions\n" ; |
251 | OS << "Directive get" << DirLang.getName() |
252 | << "DirectiveKind(llvm::StringRef Str);\n" ; |
253 | OS << "\n" ; |
254 | OS << "llvm::StringRef get" << DirLang.getName() |
255 | << "DirectiveName(Directive D);\n" ; |
256 | OS << "\n" ; |
257 | OS << "Clause get" << DirLang.getName() |
258 | << "ClauseKind(llvm::StringRef Str);\n" ; |
259 | OS << "\n" ; |
260 | OS << "llvm::StringRef get" << DirLang.getName() << "ClauseName(Clause C);\n" ; |
261 | OS << "\n" ; |
262 | OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p " |
263 | << "Version.\n" ; |
264 | OS << "bool isAllowedClauseForDirective(Directive D, " |
265 | << "Clause C, unsigned Version);\n" ; |
266 | OS << "\n" ; |
267 | OS << "constexpr std::size_t getMaxLeafCount() { return " |
268 | << GetMaxLeafCount(DirLang) << "; }\n" ; |
269 | OS << "Association getDirectiveAssociation(Directive D);\n" ; |
270 | OS << "Category getDirectiveCategory(Directive D);\n" ; |
271 | if (EnumHelperFuncs.length() > 0) { |
272 | OS << EnumHelperFuncs; |
273 | OS << "\n" ; |
274 | } |
275 | |
276 | // Closing namespaces |
277 | for (auto Ns : llvm::reverse(C&: Namespaces)) |
278 | OS << "} // namespace " << Ns << "\n" ; |
279 | |
280 | OS << "} // namespace llvm\n" ; |
281 | |
282 | OS << "#endif // LLVM_" << DirLang.getName() << "_INC\n" ; |
283 | } |
284 | |
285 | // Generate function implementation for get<Enum>Name(StringRef Str) |
286 | static void GenerateGetName(const std::vector<Record *> &Records, |
287 | raw_ostream &OS, StringRef Enum, |
288 | const DirectiveLanguage &DirLang, |
289 | StringRef Prefix) { |
290 | OS << "\n" ; |
291 | OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get" |
292 | << DirLang.getName() << Enum << "Name(" << Enum << " Kind) {\n" ; |
293 | OS << " switch (Kind) {\n" ; |
294 | for (const auto &R : Records) { |
295 | BaseRecord Rec{R}; |
296 | OS << " case " << Prefix << Rec.getFormattedName() << ":\n" ; |
297 | OS << " return \"" ; |
298 | if (Rec.getAlternativeName().empty()) |
299 | OS << Rec.getName(); |
300 | else |
301 | OS << Rec.getAlternativeName(); |
302 | OS << "\";\n" ; |
303 | } |
304 | OS << " }\n" ; // switch |
305 | OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " " << Enum |
306 | << " kind\");\n" ; |
307 | OS << "}\n" ; |
308 | } |
309 | |
310 | // Generate function implementation for get<Enum>Kind(StringRef Str) |
311 | static void GenerateGetKind(const std::vector<Record *> &Records, |
312 | raw_ostream &OS, StringRef Enum, |
313 | const DirectiveLanguage &DirLang, StringRef Prefix, |
314 | bool ImplicitAsUnknown) { |
315 | |
316 | auto DefaultIt = llvm::find_if( |
317 | Range: Records, P: [](Record *R) { return R->getValueAsBit(FieldName: "isDefault" ) == true; }); |
318 | |
319 | if (DefaultIt == Records.end()) { |
320 | PrintError(Msg: "At least one " + Enum + " must be defined as default." ); |
321 | return; |
322 | } |
323 | |
324 | BaseRecord DefaultRec{(*DefaultIt)}; |
325 | |
326 | OS << "\n" ; |
327 | OS << Enum << " llvm::" << DirLang.getCppNamespace() << "::get" |
328 | << DirLang.getName() << Enum << "Kind(llvm::StringRef Str) {\n" ; |
329 | OS << " return llvm::StringSwitch<" << Enum << ">(Str)\n" ; |
330 | |
331 | for (const auto &R : Records) { |
332 | BaseRecord Rec{R}; |
333 | if (ImplicitAsUnknown && R->getValueAsBit(FieldName: "isImplicit" )) { |
334 | OS << " .Case(\"" << Rec.getName() << "\"," << Prefix |
335 | << DefaultRec.getFormattedName() << ")\n" ; |
336 | } else { |
337 | OS << " .Case(\"" << Rec.getName() << "\"," << Prefix |
338 | << Rec.getFormattedName() << ")\n" ; |
339 | } |
340 | } |
341 | OS << " .Default(" << Prefix << DefaultRec.getFormattedName() << ");\n" ; |
342 | OS << "}\n" ; |
343 | } |
344 | |
345 | // Generate function implementation for get<ClauseVal>Kind(StringRef Str) |
346 | static void GenerateGetKindClauseVal(const DirectiveLanguage &DirLang, |
347 | raw_ostream &OS) { |
348 | for (const auto &R : DirLang.getClauses()) { |
349 | Clause C{R}; |
350 | const auto &ClauseVals = C.getClauseVals(); |
351 | if (ClauseVals.size() <= 0) |
352 | continue; |
353 | |
354 | auto DefaultIt = llvm::find_if(Range: ClauseVals, P: [](Record *CV) { |
355 | return CV->getValueAsBit(FieldName: "isDefault" ) == true; |
356 | }); |
357 | |
358 | if (DefaultIt == ClauseVals.end()) { |
359 | PrintError(Msg: "At least one val in Clause " + C.getFormattedName() + |
360 | " must be defined as default." ); |
361 | return; |
362 | } |
363 | const auto DefaultName = (*DefaultIt)->getName(); |
364 | |
365 | const auto &EnumName = C.getEnumName(); |
366 | if (EnumName.size() == 0) { |
367 | PrintError(Msg: "enumClauseValue field not set in Clause" + |
368 | C.getFormattedName() + "." ); |
369 | return; |
370 | } |
371 | |
372 | OS << "\n" ; |
373 | OS << EnumName << " llvm::" << DirLang.getCppNamespace() << "::get" |
374 | << EnumName << "(llvm::StringRef Str) {\n" ; |
375 | OS << " return llvm::StringSwitch<" << EnumName << ">(Str)\n" ; |
376 | for (const auto &CV : ClauseVals) { |
377 | ClauseVal CVal{CV}; |
378 | OS << " .Case(\"" << CVal.getFormattedName() << "\"," << CV->getName() |
379 | << ")\n" ; |
380 | } |
381 | OS << " .Default(" << DefaultName << ");\n" ; |
382 | OS << "}\n" ; |
383 | |
384 | OS << "\n" ; |
385 | OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get" |
386 | << DirLang.getName() << EnumName |
387 | << "Name(llvm::" << DirLang.getCppNamespace() << "::" << EnumName |
388 | << " x) {\n" ; |
389 | OS << " switch (x) {\n" ; |
390 | for (const auto &CV : ClauseVals) { |
391 | ClauseVal CVal{CV}; |
392 | OS << " case " << CV->getName() << ":\n" ; |
393 | OS << " return \"" << CVal.getFormattedName() << "\";\n" ; |
394 | } |
395 | OS << " }\n" ; // switch |
396 | OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " " |
397 | << EnumName << " kind\");\n" ; |
398 | OS << "}\n" ; |
399 | } |
400 | } |
401 | |
402 | static void |
403 | GenerateCaseForVersionedClauses(const std::vector<Record *> &Clauses, |
404 | raw_ostream &OS, StringRef DirectiveName, |
405 | const DirectiveLanguage &DirLang, |
406 | llvm::StringSet<> &Cases) { |
407 | for (const auto &C : Clauses) { |
408 | VersionedClause VerClause{C}; |
409 | |
410 | const auto ClauseFormattedName = VerClause.getClause().getFormattedName(); |
411 | |
412 | if (Cases.insert(key: ClauseFormattedName).second) { |
413 | OS << " case " << DirLang.getClausePrefix() << ClauseFormattedName |
414 | << ":\n" ; |
415 | OS << " return " << VerClause.getMinVersion() |
416 | << " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n" ; |
417 | } |
418 | } |
419 | } |
420 | |
421 | static std::string GetDirectiveName(const DirectiveLanguage &DirLang, |
422 | const Record *Rec) { |
423 | Directive Dir{Rec}; |
424 | return (llvm::Twine("llvm::" ) + DirLang.getCppNamespace() + |
425 | "::" + DirLang.getDirectivePrefix() + Dir.getFormattedName()) |
426 | .str(); |
427 | } |
428 | |
429 | static std::string GetDirectiveType(const DirectiveLanguage &DirLang) { |
430 | return (llvm::Twine("llvm::" ) + DirLang.getCppNamespace() + "::Directive" ) |
431 | .str(); |
432 | } |
433 | |
434 | // Generate the isAllowedClauseForDirective function implementation. |
435 | static void GenerateIsAllowedClause(const DirectiveLanguage &DirLang, |
436 | raw_ostream &OS) { |
437 | OS << "\n" ; |
438 | OS << "bool llvm::" << DirLang.getCppNamespace() |
439 | << "::isAllowedClauseForDirective(" |
440 | << "Directive D, Clause C, unsigned Version) {\n" ; |
441 | OS << " assert(unsigned(D) <= llvm::" << DirLang.getCppNamespace() |
442 | << "::Directive_enumSize);\n" ; |
443 | OS << " assert(unsigned(C) <= llvm::" << DirLang.getCppNamespace() |
444 | << "::Clause_enumSize);\n" ; |
445 | |
446 | OS << " switch (D) {\n" ; |
447 | |
448 | for (const auto &D : DirLang.getDirectives()) { |
449 | Directive Dir{D}; |
450 | |
451 | OS << " case " << DirLang.getDirectivePrefix() << Dir.getFormattedName() |
452 | << ":\n" ; |
453 | if (Dir.getAllowedClauses().size() == 0 && |
454 | Dir.getAllowedOnceClauses().size() == 0 && |
455 | Dir.getAllowedExclusiveClauses().size() == 0 && |
456 | Dir.getRequiredClauses().size() == 0) { |
457 | OS << " return false;\n" ; |
458 | } else { |
459 | OS << " switch (C) {\n" ; |
460 | |
461 | llvm::StringSet<> Cases; |
462 | |
463 | GenerateCaseForVersionedClauses(Clauses: Dir.getAllowedClauses(), OS, |
464 | DirectiveName: Dir.getName(), DirLang, Cases); |
465 | |
466 | GenerateCaseForVersionedClauses(Clauses: Dir.getAllowedOnceClauses(), OS, |
467 | DirectiveName: Dir.getName(), DirLang, Cases); |
468 | |
469 | GenerateCaseForVersionedClauses(Clauses: Dir.getAllowedExclusiveClauses(), OS, |
470 | DirectiveName: Dir.getName(), DirLang, Cases); |
471 | |
472 | GenerateCaseForVersionedClauses(Clauses: Dir.getRequiredClauses(), OS, |
473 | DirectiveName: Dir.getName(), DirLang, Cases); |
474 | |
475 | OS << " default:\n" ; |
476 | OS << " return false;\n" ; |
477 | OS << " }\n" ; // End of clauses switch |
478 | } |
479 | OS << " break;\n" ; |
480 | } |
481 | |
482 | OS << " }\n" ; // End of directives switch |
483 | OS << " llvm_unreachable(\"Invalid " << DirLang.getName() |
484 | << " Directive kind\");\n" ; |
485 | OS << "}\n" ; // End of function isAllowedClauseForDirective |
486 | } |
487 | |
488 | static void EmitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS, |
489 | StringRef TableName) { |
490 | // The leaf constructs are emitted in a form of a 2D table, where each |
491 | // row corresponds to a directive (and there is a row for each directive). |
492 | // |
493 | // Each row consists of |
494 | // - the id of the directive itself, |
495 | // - number of leaf constructs that will follow (0 for leafs), |
496 | // - ids of the leaf constructs (none if the directive is itself a leaf). |
497 | // The total number of these entries is at most MaxLeafCount+2. If this |
498 | // number is less than that, it is padded to occupy exactly MaxLeafCount+2 |
499 | // entries in memory. |
500 | // |
501 | // The rows are stored in the table in the lexicographical order. This |
502 | // is intended to enable binary search when mapping a sequence of leafs |
503 | // back to the compound directive. |
504 | // The consequence of that is that in order to find a row corresponding |
505 | // to the given directive, we'd need to scan the first element of each |
506 | // row. To avoid this, an auxiliary ordering table is created, such that |
507 | // row for Dir_A = table[auxiliary[Dir_A]]. |
508 | |
509 | std::vector<Record *> Directives = DirLang.getDirectives(); |
510 | DenseMap<Record *, int> DirId; // Record * -> llvm::omp::Directive |
511 | |
512 | for (auto [Idx, Rec] : llvm::enumerate(First&: Directives)) |
513 | DirId.insert(KV: std::make_pair(x&: Rec, y&: Idx)); |
514 | |
515 | using LeafList = std::vector<int>; |
516 | int MaxLeafCount = GetMaxLeafCount(DirLang); |
517 | |
518 | // The initial leaf table, rows order is same as directive order. |
519 | std::vector<LeafList> LeafTable(Directives.size()); |
520 | for (auto [Idx, Rec] : llvm::enumerate(First&: Directives)) { |
521 | Directive Dir{Rec}; |
522 | std::vector<Record *> Leaves = Dir.getLeafConstructs(); |
523 | |
524 | auto &List = LeafTable[Idx]; |
525 | List.resize(new_size: MaxLeafCount + 2); |
526 | List[0] = Idx; // The id of the directive itself. |
527 | List[1] = Leaves.size(); // The number of leaves to follow. |
528 | |
529 | for (int I = 0; I != MaxLeafCount; ++I) |
530 | List[I + 2] = |
531 | static_cast<size_t>(I) < Leaves.size() ? DirId.at(Val: Leaves[I]) : -1; |
532 | } |
533 | |
534 | // Some Fortran directives are delimited, i.e. they have the form of |
535 | // "directive"---"end directive". If "directive" is a compound construct, |
536 | // then the set of leaf constituents will be nonempty and the same for |
537 | // both directives. Given this set of leafs, looking up the corresponding |
538 | // compound directive should return "directive", and not "end directive". |
539 | // To avoid this problem, gather all "end directives" at the end of the |
540 | // leaf table, and only do the search on the initial segment of the table |
541 | // that excludes the "end directives". |
542 | // It's safe to find all directives whose names begin with "end ". The |
543 | // problem only exists for compound directives, like "end do simd". |
544 | // All existing directives with names starting with "end " are either |
545 | // "end directives" for an existing "directive", or leaf directives |
546 | // (such as "end declare target"). |
547 | DenseSet<int> EndDirectives; |
548 | for (auto [Rec, Id] : DirId) { |
549 | if (Directive{Rec}.getName().starts_with_insensitive(Prefix: "end " )) |
550 | EndDirectives.insert(V: Id); |
551 | } |
552 | |
553 | // Avoid sorting the vector<vector> array, instead sort an index array. |
554 | // It will also be useful later to create the auxiliary indexing array. |
555 | std::vector<int> Ordering(Directives.size()); |
556 | std::iota(first: Ordering.begin(), last: Ordering.end(), value: 0); |
557 | |
558 | llvm::sort(C&: Ordering, Comp: [&](int A, int B) { |
559 | auto &LeavesA = LeafTable[A]; |
560 | auto &LeavesB = LeafTable[B]; |
561 | int DirA = LeavesA[0], DirB = LeavesB[0]; |
562 | // First of all, end directives compare greater than non-end directives. |
563 | int IsEndA = EndDirectives.count(V: DirA), IsEndB = EndDirectives.count(V: DirB); |
564 | if (IsEndA != IsEndB) |
565 | return IsEndA < IsEndB; |
566 | if (LeavesA[1] == 0 && LeavesB[1] == 0) |
567 | return DirA < DirB; |
568 | return std::lexicographical_compare(&LeavesA[2], &LeavesA[2] + LeavesA[1], |
569 | &LeavesB[2], &LeavesB[2] + LeavesB[1]); |
570 | }); |
571 | |
572 | // Emit the table |
573 | |
574 | // The directives are emitted into a scoped enum, for which the underlying |
575 | // type is `int` (by default). The code above uses `int` to store directive |
576 | // ids, so make sure that we catch it when something changes in the |
577 | // underlying type. |
578 | std::string DirectiveType = GetDirectiveType(DirLang); |
579 | OS << "\nstatic_assert(sizeof(" << DirectiveType << ") == sizeof(int));\n" ; |
580 | |
581 | OS << "[[maybe_unused]] static const " << DirectiveType << ' ' << TableName |
582 | << "[][" << MaxLeafCount + 2 << "] = {\n" ; |
583 | for (size_t I = 0, E = Directives.size(); I != E; ++I) { |
584 | auto &Leaves = LeafTable[Ordering[I]]; |
585 | OS << " {" << GetDirectiveName(DirLang, Rec: Directives[Leaves[0]]); |
586 | OS << ", static_cast<" << DirectiveType << ">(" << Leaves[1] << ")," ; |
587 | for (size_t I = 2, E = Leaves.size(); I != E; ++I) { |
588 | int Idx = Leaves[I]; |
589 | if (Idx >= 0) |
590 | OS << ' ' << GetDirectiveName(DirLang, Rec: Directives[Leaves[I]]) << ','; |
591 | else |
592 | OS << " static_cast<" << DirectiveType << ">(-1)," ; |
593 | } |
594 | OS << "},\n" ; |
595 | } |
596 | OS << "};\n\n" ; |
597 | |
598 | // Emit a marker where the first "end directive" is. |
599 | auto FirstE = llvm::find_if(Range&: Ordering, P: [&](int RowIdx) { |
600 | return EndDirectives.count(V: LeafTable[RowIdx][0]); |
601 | }); |
602 | OS << "[[maybe_unused]] static auto " << TableName |
603 | << "EndDirective = " << TableName << " + " |
604 | << std::distance(first: Ordering.begin(), last: FirstE) << ";\n\n" ; |
605 | |
606 | // Emit the auxiliary index table: it's the inverse of the `Ordering` |
607 | // table above. |
608 | OS << "[[maybe_unused]] static const int " << TableName << "Ordering[] = {\n" ; |
609 | OS << " " ; |
610 | std::vector<int> Reverse(Ordering.size()); |
611 | for (int I = 0, E = Ordering.size(); I != E; ++I) |
612 | Reverse[Ordering[I]] = I; |
613 | for (int Idx : Reverse) |
614 | OS << ' ' << Idx << ','; |
615 | OS << "\n};\n" ; |
616 | } |
617 | |
618 | static void GenerateGetDirectiveAssociation(const DirectiveLanguage &DirLang, |
619 | raw_ostream &OS) { |
620 | enum struct Association { |
621 | None = 0, // None should be the smallest value. |
622 | Block, // The values of the rest don't matter. |
623 | Declaration, |
624 | Delimited, |
625 | Loop, |
626 | Separating, |
627 | FromLeaves, |
628 | Invalid, |
629 | }; |
630 | |
631 | std::vector<Record *> associations = DirLang.getAssociations(); |
632 | |
633 | auto getAssocValue = [](StringRef name) -> Association { |
634 | return StringSwitch<Association>(name) |
635 | .Case(S: "AS_Block" , Value: Association::Block) |
636 | .Case(S: "AS_Declaration" , Value: Association::Declaration) |
637 | .Case(S: "AS_Delimited" , Value: Association::Delimited) |
638 | .Case(S: "AS_Loop" , Value: Association::Loop) |
639 | .Case(S: "AS_None" , Value: Association::None) |
640 | .Case(S: "AS_Separating" , Value: Association::Separating) |
641 | .Case(S: "AS_FromLeaves" , Value: Association::FromLeaves) |
642 | .Default(Value: Association::Invalid); |
643 | }; |
644 | |
645 | auto getAssocName = [&](Association A) -> StringRef { |
646 | if (A != Association::Invalid && A != Association::FromLeaves) { |
647 | auto F = llvm::find_if(Range&: associations, P: [&](const Record *R) { |
648 | return getAssocValue(R->getName()) == A; |
649 | }); |
650 | if (F != associations.end()) |
651 | return (*F)->getValueAsString(FieldName: "name" ); // enum name |
652 | } |
653 | llvm_unreachable("Unexpected association value" ); |
654 | }; |
655 | |
656 | auto errorPrefixFor = [&](Directive D) -> std::string { |
657 | return (Twine("Directive '" ) + D.getName() + "' in namespace '" + |
658 | DirLang.getCppNamespace() + "' " ) |
659 | .str(); |
660 | }; |
661 | |
662 | auto reduce = [&](Association A, Association B) -> Association { |
663 | if (A > B) |
664 | std::swap(a&: A, b&: B); |
665 | |
666 | // Calculate the result using the following rules: |
667 | // x + x = x |
668 | // AS_None + x = x |
669 | // AS_Block + AS_Loop = AS_Loop |
670 | if (A == Association::None || A == B) |
671 | return B; |
672 | if (A == Association::Block && B == Association::Loop) |
673 | return B; |
674 | if (A == Association::Loop && B == Association::Block) |
675 | return A; |
676 | return Association::Invalid; |
677 | }; |
678 | |
679 | llvm::DenseMap<const Record *, Association> AsMap; |
680 | |
681 | auto compAssocImpl = [&](const Record *R, auto &&Self) -> Association { |
682 | if (auto F = AsMap.find(Val: R); F != AsMap.end()) |
683 | return F->second; |
684 | |
685 | Directive D{R}; |
686 | Association AS = getAssocValue(D.getAssociation()->getName()); |
687 | if (AS == Association::Invalid) { |
688 | PrintFatalError(Msg: errorPrefixFor(D) + |
689 | "has an unrecognized value for association: '" + |
690 | D.getAssociation()->getName() + "'" ); |
691 | } |
692 | if (AS != Association::FromLeaves) { |
693 | AsMap.insert(KV: std::make_pair(x&: R, y&: AS)); |
694 | return AS; |
695 | } |
696 | // Compute the association from leaf constructs. |
697 | std::vector<Record *> leaves = D.getLeafConstructs(); |
698 | if (leaves.empty()) { |
699 | llvm::errs() << D.getName() << '\n'; |
700 | PrintFatalError(Msg: errorPrefixFor(D) + |
701 | "requests association to be computed from leaves, " |
702 | "but it has no leaves" ); |
703 | } |
704 | |
705 | Association Result = Self(leaves[0], Self); |
706 | for (int I = 1, E = leaves.size(); I < E; ++I) { |
707 | Association A = Self(leaves[I], Self); |
708 | Association R = reduce(Result, A); |
709 | if (R == Association::Invalid) { |
710 | PrintFatalError(Msg: errorPrefixFor(D) + |
711 | "has leaves with incompatible association values: " + |
712 | getAssocName(A) + " and " + getAssocName(R)); |
713 | } |
714 | Result = R; |
715 | } |
716 | |
717 | assert(Result != Association::Invalid); |
718 | assert(Result != Association::FromLeaves); |
719 | AsMap.insert(KV: std::make_pair(x&: R, y&: Result)); |
720 | return Result; |
721 | }; |
722 | |
723 | for (Record *R : DirLang.getDirectives()) |
724 | compAssocImpl(R, compAssocImpl); // Updates AsMap. |
725 | |
726 | OS << '\n'; |
727 | |
728 | auto getQualifiedName = [&](StringRef Formatted) -> std::string { |
729 | return (llvm::Twine("llvm::" ) + DirLang.getCppNamespace() + |
730 | "::Directive::" + DirLang.getDirectivePrefix() + Formatted) |
731 | .str(); |
732 | }; |
733 | |
734 | std::string DirectiveTypeName = |
735 | std::string("llvm::" ) + DirLang.getCppNamespace().str() + "::Directive" ; |
736 | std::string AssociationTypeName = |
737 | std::string("llvm::" ) + DirLang.getCppNamespace().str() + "::Association" ; |
738 | |
739 | OS << AssociationTypeName << " llvm::" << DirLang.getCppNamespace() |
740 | << "::getDirectiveAssociation(" << DirectiveTypeName << " Dir) {\n" ; |
741 | OS << " switch (Dir) {\n" ; |
742 | for (Record *R : DirLang.getDirectives()) { |
743 | if (auto F = AsMap.find(Val: R); F != AsMap.end()) { |
744 | Directive Dir{R}; |
745 | OS << " case " << getQualifiedName(Dir.getFormattedName()) << ":\n" ; |
746 | OS << " return " << AssociationTypeName |
747 | << "::" << getAssocName(F->second) << ";\n" ; |
748 | } |
749 | } |
750 | OS << " } // switch (Dir)\n" ; |
751 | OS << " llvm_unreachable(\"Unexpected directive\");\n" ; |
752 | OS << "}\n" ; |
753 | } |
754 | |
755 | static void GenerateGetDirectiveCategory(const DirectiveLanguage &DirLang, |
756 | raw_ostream &OS) { |
757 | std::string LangNamespace = "llvm::" + DirLang.getCppNamespace().str(); |
758 | std::string CategoryTypeName = LangNamespace + "::Category" ; |
759 | std::string CategoryNamespace = CategoryTypeName + "::" ; |
760 | |
761 | OS << '\n'; |
762 | OS << CategoryTypeName << ' ' << LangNamespace << "::getDirectiveCategory(" |
763 | << GetDirectiveType(DirLang) << " Dir) {\n" ; |
764 | OS << " switch (Dir) {\n" ; |
765 | |
766 | for (Record *R : DirLang.getDirectives()) { |
767 | Directive D{R}; |
768 | OS << " case " << GetDirectiveName(DirLang, Rec: R) << ":\n" ; |
769 | OS << " return " << CategoryNamespace |
770 | << D.getCategory()->getValueAsString(FieldName: "name" ) << ";\n" ; |
771 | } |
772 | OS << " } // switch (Dir)\n" ; |
773 | OS << " llvm_unreachable(\"Unexpected directive\");\n" ; |
774 | OS << "}\n" ; |
775 | } |
776 | |
777 | // Generate a simple enum set with the give clauses. |
778 | static void GenerateClauseSet(const std::vector<Record *> &Clauses, |
779 | raw_ostream &OS, StringRef ClauseSetPrefix, |
780 | Directive &Dir, |
781 | const DirectiveLanguage &DirLang) { |
782 | |
783 | OS << "\n" ; |
784 | OS << " static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix |
785 | << DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n" ; |
786 | |
787 | for (const auto &C : Clauses) { |
788 | VersionedClause VerClause{C}; |
789 | OS << " llvm::" << DirLang.getCppNamespace() |
790 | << "::Clause::" << DirLang.getClausePrefix() |
791 | << VerClause.getClause().getFormattedName() << ",\n" ; |
792 | } |
793 | OS << " };\n" ; |
794 | } |
795 | |
796 | // Generate an enum set for the 4 kinds of clauses linked to a directive. |
797 | static void GenerateDirectiveClauseSets(const DirectiveLanguage &DirLang, |
798 | raw_ostream &OS) { |
799 | |
800 | IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_SETS" , OS); |
801 | |
802 | OS << "\n" ; |
803 | OS << "namespace llvm {\n" ; |
804 | |
805 | // Open namespaces defined in the directive language. |
806 | llvm::SmallVector<StringRef, 2> Namespaces; |
807 | llvm::SplitString(Source: DirLang.getCppNamespace(), OutFragments&: Namespaces, Delimiters: "::" ); |
808 | for (auto Ns : Namespaces) |
809 | OS << "namespace " << Ns << " {\n" ; |
810 | |
811 | for (const auto &D : DirLang.getDirectives()) { |
812 | Directive Dir{D}; |
813 | |
814 | OS << "\n" ; |
815 | OS << " // Sets for " << Dir.getName() << "\n" ; |
816 | |
817 | GenerateClauseSet(Clauses: Dir.getAllowedClauses(), OS, ClauseSetPrefix: "allowedClauses_" , Dir, |
818 | DirLang); |
819 | GenerateClauseSet(Clauses: Dir.getAllowedOnceClauses(), OS, ClauseSetPrefix: "allowedOnceClauses_" , |
820 | Dir, DirLang); |
821 | GenerateClauseSet(Clauses: Dir.getAllowedExclusiveClauses(), OS, |
822 | ClauseSetPrefix: "allowedExclusiveClauses_" , Dir, DirLang); |
823 | GenerateClauseSet(Clauses: Dir.getRequiredClauses(), OS, ClauseSetPrefix: "requiredClauses_" , Dir, |
824 | DirLang); |
825 | } |
826 | |
827 | // Closing namespaces |
828 | for (auto Ns : llvm::reverse(C&: Namespaces)) |
829 | OS << "} // namespace " << Ns << "\n" ; |
830 | |
831 | OS << "} // namespace llvm\n" ; |
832 | } |
833 | |
834 | // Generate a map of directive (key) with DirectiveClauses struct as values. |
835 | // The struct holds the 4 sets of enumeration for the 4 kinds of clauses |
836 | // allowances (allowed, allowed once, allowed exclusive and required). |
837 | static void GenerateDirectiveClauseMap(const DirectiveLanguage &DirLang, |
838 | raw_ostream &OS) { |
839 | |
840 | IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP" , OS); |
841 | |
842 | OS << "\n" ; |
843 | OS << "{\n" ; |
844 | |
845 | for (const auto &D : DirLang.getDirectives()) { |
846 | Directive Dir{D}; |
847 | OS << " {llvm::" << DirLang.getCppNamespace() |
848 | << "::Directive::" << DirLang.getDirectivePrefix() |
849 | << Dir.getFormattedName() << ",\n" ; |
850 | OS << " {\n" ; |
851 | OS << " llvm::" << DirLang.getCppNamespace() << "::allowedClauses_" |
852 | << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n" ; |
853 | OS << " llvm::" << DirLang.getCppNamespace() << "::allowedOnceClauses_" |
854 | << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n" ; |
855 | OS << " llvm::" << DirLang.getCppNamespace() |
856 | << "::allowedExclusiveClauses_" << DirLang.getDirectivePrefix() |
857 | << Dir.getFormattedName() << ",\n" ; |
858 | OS << " llvm::" << DirLang.getCppNamespace() << "::requiredClauses_" |
859 | << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n" ; |
860 | OS << " }\n" ; |
861 | OS << " },\n" ; |
862 | } |
863 | |
864 | OS << "}\n" ; |
865 | } |
866 | |
867 | // Generate classes entry for Flang clauses in the Flang parse-tree |
868 | // If the clause as a non-generic class, no entry is generated. |
869 | // If the clause does not hold a value, an EMPTY_CLASS is used. |
870 | // If the clause class is generic then a WRAPPER_CLASS is used. When the value |
871 | // is optional, the value class is wrapped into a std::optional. |
872 | static void GenerateFlangClauseParserClass(const DirectiveLanguage &DirLang, |
873 | raw_ostream &OS) { |
874 | |
875 | IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES" , OS); |
876 | |
877 | OS << "\n" ; |
878 | |
879 | for (const auto &C : DirLang.getClauses()) { |
880 | Clause Clause{C}; |
881 | if (!Clause.getFlangClass().empty()) { |
882 | OS << "WRAPPER_CLASS(" << Clause.getFormattedParserClassName() << ", " ; |
883 | if (Clause.isValueOptional() && Clause.isValueList()) { |
884 | OS << "std::optional<std::list<" << Clause.getFlangClass() << ">>" ; |
885 | } else if (Clause.isValueOptional()) { |
886 | OS << "std::optional<" << Clause.getFlangClass() << ">" ; |
887 | } else if (Clause.isValueList()) { |
888 | OS << "std::list<" << Clause.getFlangClass() << ">" ; |
889 | } else { |
890 | OS << Clause.getFlangClass(); |
891 | } |
892 | } else { |
893 | OS << "EMPTY_CLASS(" << Clause.getFormattedParserClassName(); |
894 | } |
895 | OS << ");\n" ; |
896 | } |
897 | } |
898 | |
899 | // Generate a list of the different clause classes for Flang. |
900 | static void GenerateFlangClauseParserClassList(const DirectiveLanguage &DirLang, |
901 | raw_ostream &OS) { |
902 | |
903 | IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES_LIST" , OS); |
904 | |
905 | OS << "\n" ; |
906 | llvm::interleaveComma(c: DirLang.getClauses(), os&: OS, each_fn: [&](Record *C) { |
907 | Clause Clause{C}; |
908 | OS << Clause.getFormattedParserClassName() << "\n" ; |
909 | }); |
910 | } |
911 | |
912 | // Generate dump node list for the clauses holding a generic class name. |
913 | static void GenerateFlangClauseDump(const DirectiveLanguage &DirLang, |
914 | raw_ostream &OS) { |
915 | |
916 | IfDefScope Scope("GEN_FLANG_DUMP_PARSE_TREE_CLAUSES" , OS); |
917 | |
918 | OS << "\n" ; |
919 | for (const auto &C : DirLang.getClauses()) { |
920 | Clause Clause{C}; |
921 | OS << "NODE(" << DirLang.getFlangClauseBaseClass() << ", " |
922 | << Clause.getFormattedParserClassName() << ")\n" ; |
923 | } |
924 | } |
925 | |
926 | // Generate Unparse functions for clauses classes in the Flang parse-tree |
927 | // If the clause is a non-generic class, no entry is generated. |
928 | static void GenerateFlangClauseUnparse(const DirectiveLanguage &DirLang, |
929 | raw_ostream &OS) { |
930 | |
931 | IfDefScope Scope("GEN_FLANG_CLAUSE_UNPARSE" , OS); |
932 | |
933 | OS << "\n" ; |
934 | |
935 | for (const auto &C : DirLang.getClauses()) { |
936 | Clause Clause{C}; |
937 | if (!Clause.getFlangClass().empty()) { |
938 | if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) { |
939 | OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() |
940 | << "::" << Clause.getFormattedParserClassName() << " &x) {\n" ; |
941 | OS << " Word(\"" << Clause.getName().upper() << "\");\n" ; |
942 | |
943 | OS << " Walk(\"(\", x.v, \")\");\n" ; |
944 | OS << "}\n" ; |
945 | } else if (Clause.isValueOptional()) { |
946 | OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() |
947 | << "::" << Clause.getFormattedParserClassName() << " &x) {\n" ; |
948 | OS << " Word(\"" << Clause.getName().upper() << "\");\n" ; |
949 | OS << " Put(\"(\");\n" ; |
950 | OS << " if (x.v.has_value())\n" ; |
951 | if (Clause.isValueList()) |
952 | OS << " Walk(x.v, \",\");\n" ; |
953 | else |
954 | OS << " Walk(x.v);\n" ; |
955 | OS << " else\n" ; |
956 | OS << " Put(\"" << Clause.getDefaultValue() << "\");\n" ; |
957 | OS << " Put(\")\");\n" ; |
958 | OS << "}\n" ; |
959 | } else { |
960 | OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() |
961 | << "::" << Clause.getFormattedParserClassName() << " &x) {\n" ; |
962 | OS << " Word(\"" << Clause.getName().upper() << "\");\n" ; |
963 | OS << " Put(\"(\");\n" ; |
964 | if (Clause.isValueList()) |
965 | OS << " Walk(x.v, \",\");\n" ; |
966 | else |
967 | OS << " Walk(x.v);\n" ; |
968 | OS << " Put(\")\");\n" ; |
969 | OS << "}\n" ; |
970 | } |
971 | } else { |
972 | OS << "void Before(const " << DirLang.getFlangClauseBaseClass() |
973 | << "::" << Clause.getFormattedParserClassName() << " &) { Word(\"" |
974 | << Clause.getName().upper() << "\"); }\n" ; |
975 | } |
976 | } |
977 | } |
978 | |
979 | // Generate check in the Enter functions for clauses classes. |
980 | static void GenerateFlangClauseCheckPrototypes(const DirectiveLanguage &DirLang, |
981 | raw_ostream &OS) { |
982 | |
983 | IfDefScope Scope("GEN_FLANG_CLAUSE_CHECK_ENTER" , OS); |
984 | |
985 | OS << "\n" ; |
986 | for (const auto &C : DirLang.getClauses()) { |
987 | Clause Clause{C}; |
988 | OS << "void Enter(const parser::" << DirLang.getFlangClauseBaseClass() |
989 | << "::" << Clause.getFormattedParserClassName() << " &);\n" ; |
990 | } |
991 | } |
992 | |
993 | // Generate the mapping for clauses between the parser class and the |
994 | // corresponding clause Kind |
995 | static void GenerateFlangClauseParserKindMap(const DirectiveLanguage &DirLang, |
996 | raw_ostream &OS) { |
997 | |
998 | IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_KIND_MAP" , OS); |
999 | |
1000 | OS << "\n" ; |
1001 | for (const auto &C : DirLang.getClauses()) { |
1002 | Clause Clause{C}; |
1003 | OS << "if constexpr (std::is_same_v<A, parser::" |
1004 | << DirLang.getFlangClauseBaseClass() |
1005 | << "::" << Clause.getFormattedParserClassName(); |
1006 | OS << ">)\n" ; |
1007 | OS << " return llvm::" << DirLang.getCppNamespace() |
1008 | << "::Clause::" << DirLang.getClausePrefix() << Clause.getFormattedName() |
1009 | << ";\n" ; |
1010 | } |
1011 | |
1012 | OS << "llvm_unreachable(\"Invalid " << DirLang.getName() |
1013 | << " Parser clause\");\n" ; |
1014 | } |
1015 | |
1016 | static bool compareClauseName(Record *R1, Record *R2) { |
1017 | Clause C1{R1}; |
1018 | Clause C2{R2}; |
1019 | return (C1.getName() > C2.getName()); |
1020 | } |
1021 | |
1022 | // Generate the parser for the clauses. |
1023 | static void GenerateFlangClausesParser(const DirectiveLanguage &DirLang, |
1024 | raw_ostream &OS) { |
1025 | std::vector<Record *> Clauses = DirLang.getClauses(); |
1026 | // Sort clauses in reverse alphabetical order so with clauses with same |
1027 | // beginning, the longer option is tried before. |
1028 | llvm::sort(C&: Clauses, Comp: compareClauseName); |
1029 | IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER" , OS); |
1030 | OS << "\n" ; |
1031 | unsigned index = 0; |
1032 | unsigned lastClauseIndex = DirLang.getClauses().size() - 1; |
1033 | OS << "TYPE_PARSER(\n" ; |
1034 | for (const auto &C : Clauses) { |
1035 | Clause Clause{C}; |
1036 | if (Clause.getAliases().empty()) { |
1037 | OS << " \"" << Clause.getName() << "\"" ; |
1038 | } else { |
1039 | OS << " (" |
1040 | << "\"" << Clause.getName() << "\"_tok" ; |
1041 | for (StringRef alias : Clause.getAliases()) { |
1042 | OS << " || \"" << alias << "\"_tok" ; |
1043 | } |
1044 | OS << ")" ; |
1045 | } |
1046 | |
1047 | OS << " >> construct<" << DirLang.getFlangClauseBaseClass() |
1048 | << ">(construct<" << DirLang.getFlangClauseBaseClass() |
1049 | << "::" << Clause.getFormattedParserClassName() << ">(" ; |
1050 | if (Clause.getFlangClass().empty()) { |
1051 | OS << "))" ; |
1052 | if (index != lastClauseIndex) |
1053 | OS << " ||" ; |
1054 | OS << "\n" ; |
1055 | ++index; |
1056 | continue; |
1057 | } |
1058 | |
1059 | if (Clause.isValueOptional()) |
1060 | OS << "maybe(" ; |
1061 | OS << "parenthesized(" ; |
1062 | if (Clause.isValueList()) |
1063 | OS << "nonemptyList(" ; |
1064 | |
1065 | if (!Clause.getPrefix().empty()) |
1066 | OS << "\"" << Clause.getPrefix() << ":\" >> " ; |
1067 | |
1068 | // The common Flang parser are used directly. Their name is identical to |
1069 | // the Flang class with first letter as lowercase. If the Flang class is |
1070 | // not a common class, we assume there is a specific Parser<>{} with the |
1071 | // Flang class name provided. |
1072 | llvm::SmallString<128> Scratch; |
1073 | StringRef Parser = |
1074 | llvm::StringSwitch<StringRef>(Clause.getFlangClass()) |
1075 | .Case(S: "Name" , Value: "name" ) |
1076 | .Case(S: "ScalarIntConstantExpr" , Value: "scalarIntConstantExpr" ) |
1077 | .Case(S: "ScalarIntExpr" , Value: "scalarIntExpr" ) |
1078 | .Case(S: "ScalarExpr" , Value: "scalarExpr" ) |
1079 | .Case(S: "ScalarLogicalExpr" , Value: "scalarLogicalExpr" ) |
1080 | .Default(Value: ("Parser<" + Clause.getFlangClass() + ">{}" ) |
1081 | .toStringRef(Out&: Scratch)); |
1082 | OS << Parser; |
1083 | if (!Clause.getPrefix().empty() && Clause.isPrefixOptional()) |
1084 | OS << " || " << Parser; |
1085 | if (Clause.isValueList()) // close nonemptyList(. |
1086 | OS << ")" ; |
1087 | OS << ")" ; // close parenthesized(. |
1088 | |
1089 | if (Clause.isValueOptional()) // close maybe(. |
1090 | OS << ")" ; |
1091 | OS << "))" ; |
1092 | if (index != lastClauseIndex) |
1093 | OS << " ||" ; |
1094 | OS << "\n" ; |
1095 | ++index; |
1096 | } |
1097 | OS << ")\n" ; |
1098 | } |
1099 | |
1100 | // Generate the implementation section for the enumeration in the directive |
1101 | // language |
1102 | static void EmitDirectivesFlangImpl(const DirectiveLanguage &DirLang, |
1103 | raw_ostream &OS) { |
1104 | |
1105 | GenerateDirectiveClauseSets(DirLang, OS); |
1106 | |
1107 | GenerateDirectiveClauseMap(DirLang, OS); |
1108 | |
1109 | GenerateFlangClauseParserClass(DirLang, OS); |
1110 | |
1111 | GenerateFlangClauseParserClassList(DirLang, OS); |
1112 | |
1113 | GenerateFlangClauseDump(DirLang, OS); |
1114 | |
1115 | GenerateFlangClauseUnparse(DirLang, OS); |
1116 | |
1117 | GenerateFlangClauseCheckPrototypes(DirLang, OS); |
1118 | |
1119 | GenerateFlangClauseParserKindMap(DirLang, OS); |
1120 | |
1121 | GenerateFlangClausesParser(DirLang, OS); |
1122 | } |
1123 | |
1124 | static void GenerateClauseClassMacro(const DirectiveLanguage &DirLang, |
1125 | raw_ostream &OS) { |
1126 | // Generate macros style information for legacy code in clang |
1127 | IfDefScope Scope("GEN_CLANG_CLAUSE_CLASS" , OS); |
1128 | |
1129 | OS << "\n" ; |
1130 | |
1131 | OS << "#ifndef CLAUSE\n" ; |
1132 | OS << "#define CLAUSE(Enum, Str, Implicit)\n" ; |
1133 | OS << "#endif\n" ; |
1134 | OS << "#ifndef CLAUSE_CLASS\n" ; |
1135 | OS << "#define CLAUSE_CLASS(Enum, Str, Class)\n" ; |
1136 | OS << "#endif\n" ; |
1137 | OS << "#ifndef CLAUSE_NO_CLASS\n" ; |
1138 | OS << "#define CLAUSE_NO_CLASS(Enum, Str)\n" ; |
1139 | OS << "#endif\n" ; |
1140 | OS << "\n" ; |
1141 | OS << "#define __CLAUSE(Name, Class) \\\n" ; |
1142 | OS << " CLAUSE(" << DirLang.getClausePrefix() |
1143 | << "##Name, #Name, /* Implicit */ false) \\\n" ; |
1144 | OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix() |
1145 | << "##Name, #Name, Class)\n" ; |
1146 | OS << "#define __CLAUSE_NO_CLASS(Name) \\\n" ; |
1147 | OS << " CLAUSE(" << DirLang.getClausePrefix() |
1148 | << "##Name, #Name, /* Implicit */ false) \\\n" ; |
1149 | OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, #Name)\n" ; |
1150 | OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class) \\\n" ; |
1151 | OS << " CLAUSE(" << DirLang.getClausePrefix() |
1152 | << "##Name, Str, /* Implicit */ true) \\\n" ; |
1153 | OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix() |
1154 | << "##Name, Str, Class)\n" ; |
1155 | OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str) \\\n" ; |
1156 | OS << " CLAUSE(" << DirLang.getClausePrefix() |
1157 | << "##Name, Str, /* Implicit */ true) \\\n" ; |
1158 | OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, Str)\n" ; |
1159 | OS << "\n" ; |
1160 | |
1161 | for (const auto &R : DirLang.getClauses()) { |
1162 | Clause C{R}; |
1163 | if (C.getClangClass().empty()) { // NO_CLASS |
1164 | if (C.isImplicit()) { |
1165 | OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << C.getFormattedName() << ", \"" |
1166 | << C.getFormattedName() << "\")\n" ; |
1167 | } else { |
1168 | OS << "__CLAUSE_NO_CLASS(" << C.getFormattedName() << ")\n" ; |
1169 | } |
1170 | } else { // CLASS |
1171 | if (C.isImplicit()) { |
1172 | OS << "__IMPLICIT_CLAUSE_CLASS(" << C.getFormattedName() << ", \"" |
1173 | << C.getFormattedName() << "\", " << C.getClangClass() << ")\n" ; |
1174 | } else { |
1175 | OS << "__CLAUSE(" << C.getFormattedName() << ", " << C.getClangClass() |
1176 | << ")\n" ; |
1177 | } |
1178 | } |
1179 | } |
1180 | |
1181 | OS << "\n" ; |
1182 | OS << "#undef __IMPLICIT_CLAUSE_NO_CLASS\n" ; |
1183 | OS << "#undef __IMPLICIT_CLAUSE_CLASS\n" ; |
1184 | OS << "#undef __CLAUSE_NO_CLASS\n" ; |
1185 | OS << "#undef __CLAUSE\n" ; |
1186 | OS << "#undef CLAUSE_NO_CLASS\n" ; |
1187 | OS << "#undef CLAUSE_CLASS\n" ; |
1188 | OS << "#undef CLAUSE\n" ; |
1189 | } |
1190 | |
1191 | // Generate the implemenation for the enumeration in the directive |
1192 | // language. This code can be included in library. |
1193 | void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang, |
1194 | raw_ostream &OS) { |
1195 | IfDefScope Scope("GEN_DIRECTIVES_IMPL" , OS); |
1196 | |
1197 | OS << "\n#include \"llvm/Support/ErrorHandling.h\"\n" ; |
1198 | |
1199 | // getDirectiveKind(StringRef Str) |
1200 | GenerateGetKind(Records: DirLang.getDirectives(), OS, Enum: "Directive" , DirLang, |
1201 | Prefix: DirLang.getDirectivePrefix(), /*ImplicitAsUnknown=*/false); |
1202 | |
1203 | // getDirectiveName(Directive Kind) |
1204 | GenerateGetName(Records: DirLang.getDirectives(), OS, Enum: "Directive" , DirLang, |
1205 | Prefix: DirLang.getDirectivePrefix()); |
1206 | |
1207 | // getClauseKind(StringRef Str) |
1208 | GenerateGetKind(Records: DirLang.getClauses(), OS, Enum: "Clause" , DirLang, |
1209 | Prefix: DirLang.getClausePrefix(), |
1210 | /*ImplicitAsUnknown=*/true); |
1211 | |
1212 | // getClauseName(Clause Kind) |
1213 | GenerateGetName(Records: DirLang.getClauses(), OS, Enum: "Clause" , DirLang, |
1214 | Prefix: DirLang.getClausePrefix()); |
1215 | |
1216 | // get<ClauseVal>Kind(StringRef Str) |
1217 | GenerateGetKindClauseVal(DirLang, OS); |
1218 | |
1219 | // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version) |
1220 | GenerateIsAllowedClause(DirLang, OS); |
1221 | |
1222 | // getDirectiveAssociation(Directive D) |
1223 | GenerateGetDirectiveAssociation(DirLang, OS); |
1224 | |
1225 | // getDirectiveCategory(Directive D) |
1226 | GenerateGetDirectiveCategory(DirLang, OS); |
1227 | |
1228 | // Leaf table for getLeafConstructs, etc. |
1229 | EmitLeafTable(DirLang, OS, TableName: "LeafConstructTable" ); |
1230 | } |
1231 | |
1232 | // Generate the implemenation section for the enumeration in the directive |
1233 | // language. |
1234 | static void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) { |
1235 | const auto DirLang = DirectiveLanguage{Records}; |
1236 | if (DirLang.HasValidityErrors()) |
1237 | return; |
1238 | |
1239 | EmitDirectivesFlangImpl(DirLang, OS); |
1240 | |
1241 | GenerateClauseClassMacro(DirLang, OS); |
1242 | |
1243 | EmitDirectivesBasicImpl(DirLang, OS); |
1244 | } |
1245 | |
1246 | static TableGen::Emitter::Opt |
1247 | X("gen-directive-decl" , EmitDirectivesDecl, |
1248 | "Generate directive related declaration code (header file)" ); |
1249 | |
1250 | static TableGen::Emitter::Opt |
1251 | Y("gen-directive-impl" , EmitDirectivesImpl, |
1252 | "Generate directive related implementation code" ); |
1253 | |