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