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
16#include "llvm/ADT/DenseMap.h"
17#include "llvm/ADT/DenseSet.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/StringSet.h"
21#include "llvm/ADT/StringSwitch.h"
22#include "llvm/TableGen/CodeGenHelpers.h"
23#include "llvm/TableGen/Error.h"
24#include "llvm/TableGen/Record.h"
25#include "llvm/TableGen/TableGenBackend.h"
26
27#include <numeric>
28#include <string>
29#include <vector>
30
31using namespace llvm;
32
33namespace {
34enum class Frontend { LLVM, Flang, Clang };
35} // namespace
36
37static void emitDirectivesConstexprImpl(const DirectiveLanguage &DirLang,
38 raw_ostream &OS);
39
40static StringRef getFESpelling(Frontend FE) {
41 switch (FE) {
42 case Frontend::LLVM:
43 return "llvm";
44 case Frontend::Flang:
45 return "flang";
46 case Frontend::Clang:
47 return "clang";
48 }
49 llvm_unreachable("unknown FE kind");
50}
51
52// Get the full namespace qualifier for the directive language.
53static std::string getQualifier(const DirectiveLanguage &DirLang,
54 Frontend FE = Frontend::LLVM) {
55 return (Twine(getFESpelling(FE)) + "::" + DirLang.getCppNamespace().str() +
56 "::")
57 .str();
58}
59
60// Get prefixed formatted name, e.g. for "target data", get "OMPD_target_data".
61// This should work for any Record as long as BaseRecord::getFormattedName
62// works.
63static std::string getIdentifierName(const Record *Rec, StringRef Prefix) {
64 return Prefix.str() + BaseRecord(Rec).getFormattedName();
65}
66
67using RecordWithSpelling = std::pair<const Record *, Spelling::Value>;
68
69static std::vector<RecordWithSpelling>
70getSpellings(ArrayRef<const Record *> Records) {
71 std::vector<RecordWithSpelling> List;
72 for (const Record *R : Records) {
73 BaseRecord Rec(R);
74 llvm::transform(Range: Rec.getSpellings(), d_first: std::back_inserter(x&: List),
75 F: [R](Spelling::Value V) { return std::make_pair(x: R, y&: V); });
76 }
77 return List;
78}
79
80static void generateEnumExports(ArrayRef<const Record *> Records,
81 raw_ostream &OS, StringRef Enum,
82 StringRef Prefix) {
83 for (const Record *R : Records) {
84 std::string N = getIdentifierName(Rec: R, Prefix);
85 OS << "constexpr auto " << N << " = " << Enum << "::" << N << ";\n";
86 }
87 OS << "\n";
88}
89
90// Generate enum class. Entries are emitted in the order in which they appear
91// in the `Records` vector.
92static void generateEnumClass(ArrayRef<const Record *> Records, raw_ostream &OS,
93 StringRef Enum, StringRef Prefix,
94 bool ExportEnums) {
95 OS << "enum class " << Enum << " {\n";
96 if (!Records.empty()) {
97 std::string N;
98 for (auto [I, R] : llvm::enumerate(First&: Records)) {
99 N = getIdentifierName(Rec: R, Prefix);
100 OS << " " << N << ",\n";
101 // Make the sentinel names less likely to conflict with actual names...
102 if (I == 0)
103 OS << " First_ = " << N << ",\n";
104 }
105 OS << " Last_ = " << N << ",\n";
106 }
107 OS << "};\n";
108 OS << "\n";
109 OS << "static constexpr std::size_t " << Enum
110 << "_enumSize = " << Records.size() << ";\n\n";
111
112 // Make the enum values available in the defined namespace. This allows us to
113 // write something like Enum_X if we have a `using namespace <CppNamespace>`.
114 // At the same time we do not loose the strong type guarantees of the enum
115 // class, that is we cannot pass an unsigned as Directive without an explicit
116 // cast.
117 if (ExportEnums)
118 generateEnumExports(Records, OS, Enum, Prefix);
119}
120
121// Generate enum class with values corresponding to different bit positions.
122// Entries are emitted in the order in which they appear in the `Records`
123// vector.
124static void generateEnumBitmask(ArrayRef<const Record *> Records,
125 raw_ostream &OS, StringRef Enum,
126 StringRef Prefix, bool ExportEnums) {
127 assert(Records.size() <= 64 && "Too many values for a bitmask");
128 StringRef Type = Records.size() <= 32 ? "uint32_t" : "uint64_t";
129 StringRef TypeSuffix = Records.size() <= 32 ? "U" : "ULL";
130
131 OS << "enum class " << Enum << " : " << Type << " {\n";
132 std::string LastName;
133 for (auto [I, R] : llvm::enumerate(First&: Records)) {
134 LastName = getIdentifierName(Rec: R, Prefix);
135 OS << " " << LastName << " = " << (1ull << I) << TypeSuffix << ",\n";
136 }
137 OS << " LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/" << LastName << ")\n";
138 OS << "};\n";
139 OS << "\n";
140 OS << "static constexpr std::size_t " << Enum
141 << "_enumSize = " << Records.size() << ";\n\n";
142
143 // Make the enum values available in the defined namespace. This allows us to
144 // write something like Enum_X if we have a `using namespace <CppNamespace>`.
145 // At the same time we do not loose the strong type guarantees of the enum
146 // class, that is we cannot pass an unsigned as Directive without an explicit
147 // cast.
148 if (ExportEnums)
149 generateEnumExports(Records, OS, Enum, Prefix);
150}
151
152// Generate enums for values that clauses can take.
153// Also generate function declarations for get<Enum>Name(StringRef Str).
154static void generateClauseEnumVal(ArrayRef<const Record *> Records,
155 raw_ostream &OS,
156 const DirectiveLanguage &DirLang,
157 std::string &EnumHelperFuncs) {
158 for (const Record *R : Records) {
159 Clause C(R);
160 const auto &ClauseVals = C.getClauseVals();
161 if (ClauseVals.size() <= 0)
162 continue;
163
164 StringRef Enum = C.getEnumName();
165 if (Enum.empty()) {
166 PrintError(Msg: "enumClauseValue field not set in Clause" +
167 C.getFormattedName() + ".");
168 return;
169 }
170
171 OS << "enum class " << Enum << " {\n";
172 for (const EnumVal Val : ClauseVals)
173 OS << " " << Val.getRecordName() << "=" << Val.getValue() << ",\n";
174 OS << "};\n";
175
176 if (DirLang.hasMakeEnumAvailableInNamespace()) {
177 OS << "\n";
178 for (const auto &CV : ClauseVals) {
179 OS << "constexpr auto " << CV->getName() << " = " << Enum
180 << "::" << CV->getName() << ";\n";
181 }
182 OS << "\n";
183 EnumHelperFuncs += (Twine("LLVM_ABI ") + Twine(Enum) + Twine(" get") +
184 Twine(Enum) + Twine("(StringRef Str);\n"))
185 .str();
186
187 EnumHelperFuncs +=
188 (Twine("LLVM_ABI StringRef get") + Twine(DirLang.getName()) +
189 Twine(Enum) + Twine("Name(") + Twine(Enum) + Twine(" x);\n"))
190 .str();
191 }
192 }
193}
194
195static bool hasDuplicateClauses(ArrayRef<const Record *> Clauses,
196 const Directive &Directive,
197 StringSet<> &CrtClauses) {
198 bool HasError = false;
199 for (const VersionedClause VerClause : Clauses) {
200 StringRef Name = VerClause.getClause().getRecordName();
201 const auto InsRes = CrtClauses.insert(key: Name);
202 if (!InsRes.second) {
203 PrintError(Msg: "Clause " + Name + " already defined on directive " +
204 Directive.getRecordName());
205 HasError = true;
206 }
207 }
208 return HasError;
209}
210
211// Check for duplicate clauses in lists. Clauses cannot appear twice in the
212// three allowed list. Also, since required implies allowed, clauses cannot
213// appear in both the allowedClauses and requiredClauses lists.
214static bool
215hasDuplicateClausesInDirectives(ArrayRef<const Record *> Directives) {
216 bool HasDuplicate = false;
217 for (const Directive Dir : Directives) {
218 StringSet<> Clauses;
219 // Check for duplicates in the three allowed lists.
220 if (hasDuplicateClauses(Clauses: Dir.getAllowedClauses(), Directive: Dir, CrtClauses&: Clauses) ||
221 hasDuplicateClauses(Clauses: Dir.getAllowedOnceClauses(), Directive: Dir, CrtClauses&: Clauses) ||
222 hasDuplicateClauses(Clauses: Dir.getAllowedExclusiveClauses(), Directive: Dir, CrtClauses&: Clauses)) {
223 HasDuplicate = true;
224 }
225 // Check for duplicate between allowedClauses and required
226 Clauses.clear();
227 if (hasDuplicateClauses(Clauses: Dir.getAllowedClauses(), Directive: Dir, CrtClauses&: Clauses) ||
228 hasDuplicateClauses(Clauses: Dir.getRequiredClauses(), Directive: Dir, CrtClauses&: Clauses)) {
229 HasDuplicate = true;
230 }
231 if (HasDuplicate)
232 PrintFatalError(Msg: "One or more clauses are defined multiple times on"
233 " directive " +
234 Dir.getRecordName());
235 }
236
237 return HasDuplicate;
238}
239
240// Check consitency of records. Return true if an error has been detected.
241// Return false if the records are valid.
242bool DirectiveLanguage::HasValidityErrors() const {
243 if (getDirectiveLanguages().size() != 1) {
244 PrintFatalError(Msg: "A single definition of DirectiveLanguage is needed.");
245 return true;
246 }
247
248 return hasDuplicateClausesInDirectives(Directives: getDirectives());
249}
250
251// Count the maximum number of leaf constituents per construct.
252static size_t getMaxLeafCount(const DirectiveLanguage &DirLang) {
253 size_t MaxCount = 0;
254 for (const Directive D : DirLang.getDirectives())
255 MaxCount = std::max(a: MaxCount, b: D.getLeafConstructs().size());
256 return MaxCount;
257}
258
259// Generate the declaration section for the enumeration in the directive
260// language.
261static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
262 const auto DirLang = DirectiveLanguage(Records);
263 if (DirLang.HasValidityErrors())
264 return;
265
266 StringRef Lang = DirLang.getName();
267 IncludeGuardEmitter IncGuard(OS, (Twine("LLVM_") + Lang + "_INC").str());
268
269 OS << "#include \"llvm/ADT/ArrayRef.h\"\n";
270
271 if (DirLang.hasEnableBitmaskEnumInNamespace())
272 OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n";
273
274 OS << "#include \"llvm/ADT/Sequence.h\"\n";
275 OS << "#include \"llvm/ADT/STLExtras.h\"\n";
276 OS << "#include \"llvm/ADT/StringRef.h\"\n";
277 OS << "#include \"llvm/Frontend/Directive/Spelling.h\"\n";
278 OS << "#include \"llvm/Support/Compiler.h\"\n";
279 OS << "#include <cstddef>\n"; // for size_t
280 OS << "#include <utility>\n"; // for std::pair
281 OS << "\n";
282 NamespaceEmitter LlvmNS(OS, "llvm");
283 {
284 NamespaceEmitter DirLangNS(OS, DirLang.getCppNamespace());
285
286 if (DirLang.hasEnableBitmaskEnumInNamespace())
287 OS << "LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n\n";
288
289 // Emit Directive associations
290 std::vector<const Record *> Associations;
291 copy_if(
292 Range: DirLang.getAssociations(), Out: std::back_inserter(x&: Associations),
293 // Skip the "special" value
294 P: [](const Record *Def) { return Def->getName() != "AS_FromLeaves"; });
295 generateEnumClass(Records: Associations, OS, Enum: "Association",
296 /*Prefix=*/"", /*ExportEnums=*/false);
297
298 generateEnumClass(Records: DirLang.getCategories(), OS, Enum: "Category", /*Prefix=*/"",
299 /*ExportEnums=*/false);
300
301 generateEnumBitmask(Records: DirLang.getSourceLanguages(), OS, Enum: "SourceLanguage",
302 /*Prefix=*/"", /*ExportEnums=*/false);
303
304 // Emit Directive enumeration
305 generateEnumClass(Records: DirLang.getDirectives(), OS, Enum: "Directive",
306 Prefix: DirLang.getDirectivePrefix(),
307 ExportEnums: DirLang.hasMakeEnumAvailableInNamespace());
308
309 // Emit Clause enumeration
310 generateEnumClass(Records: DirLang.getClauses(), OS, Enum: "Clause",
311 Prefix: DirLang.getClausePrefix(),
312 ExportEnums: DirLang.hasMakeEnumAvailableInNamespace());
313
314 // Emit LoopModifier
315 generateEnumClass(Records: DirLang.getLoopModifiers(), OS, Enum: "LoopModifier",
316 Prefix: DirLang.getLoopModifierPrefix(),
317 ExportEnums: DirLang.hasMakeEnumAvailableInNamespace());
318
319 // Emit ClauseVals enumeration
320 std::string EnumHelperFuncs;
321 generateClauseEnumVal(Records: DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
322
323 // Emit constexpr functions.
324 emitDirectivesConstexprImpl(DirLang, OS);
325
326 // Generic function signatures
327 OS << "\n";
328 OS << "// Enumeration helper functions\n";
329
330 OS << "LLVM_ABI std::pair<Directive, directive::VersionRange> get" << Lang
331 << "DirectiveKindAndVersions(StringRef Str);\n";
332
333 OS << "inline Directive get" << Lang << "DirectiveKind(StringRef Str) {\n";
334 OS << " return get" << Lang << "DirectiveKindAndVersions(Str).first;\n";
335 OS << "}\n";
336 OS << "\n";
337
338 OS << "LLVM_ABI StringRef get" << Lang
339 << "DirectiveName(Directive D, unsigned Ver = 0);\n";
340 OS << "\n";
341
342 OS << "LLVM_ABI std::pair<Clause, directive::VersionRange> get" << Lang
343 << "ClauseKindAndVersions(StringRef Str);\n";
344 OS << "\n";
345
346 OS << "inline Clause get" << Lang << "ClauseKind(StringRef Str) {\n";
347 OS << " return get" << Lang << "ClauseKindAndVersions(Str).first;\n";
348 OS << "}\n";
349 OS << "\n";
350
351 OS << "LLVM_ABI StringRef get" << Lang
352 << "ClauseName(Clause C, unsigned Ver = 0);\n";
353 OS << "\n";
354
355 OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "
356 << "Version.\n";
357 OS << "LLVM_ABI bool isAllowedClauseForDirective(Directive D, "
358 << "Clause C, unsigned Version);\n";
359 OS << "\n";
360 OS << "constexpr std::size_t getMaxLeafCount() { return "
361 << getMaxLeafCount(DirLang) << "; }\n";
362 OS << "LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier "
363 "LM);\n";
364 OS << "LLVM_ABI StringRef getLoopModifierName(LoopModifier LM, unsigned "
365 "Ver = 0);\n";
366 OS << EnumHelperFuncs;
367 } // close DirLangNS
368
369 // These specializations need to be in ::llvm.
370 for (StringRef Enum :
371 {"Association", "Category", "Directive", "Clause", "LoopModifier"}) {
372 OS << "\n";
373 OS << "template <> struct enum_iteration_traits<"
374 << DirLang.getCppNamespace() << "::" << Enum << "> {\n";
375 OS << " static constexpr bool is_iterable = true;\n";
376 OS << "};\n";
377 }
378}
379
380// Given a list of spellings (for a given clause/directive), order them
381// in a way that allows the use of binary search to locate a spelling
382// for a specified version.
383static std::vector<Spelling::Value>
384orderSpellings(ArrayRef<Spelling::Value> Spellings) {
385 std::vector<Spelling::Value> List(Spellings.begin(), Spellings.end());
386
387 llvm::stable_sort(Range&: List,
388 C: [](const Spelling::Value &A, const Spelling::Value &B) {
389 return A.Versions < B.Versions;
390 });
391 return List;
392}
393
394// Generate function implementation for get<Enum>Name(StringRef Str)
395static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS,
396 StringRef Enum, const DirectiveLanguage &DirLang,
397 StringRef LangName, StringRef Prefix) {
398 std::string Qual = getQualifier(DirLang);
399 OS << "\n";
400 OS << "llvm::StringRef " << Qual << "get" << LangName << Enum << "Name("
401 << Qual << Enum << " Kind, unsigned Version) {\n";
402 OS << " switch (Kind) {\n";
403 for (const Record *R : Records) {
404 BaseRecord Rec(R);
405 std::string Ident = getIdentifierName(Rec: R, Prefix);
406 OS << " case " << Ident << ":";
407 std::vector<Spelling::Value> Spellings(orderSpellings(Spellings: Rec.getSpellings()));
408 assert(Spellings.size() != 0 && "No spellings for this item");
409 if (Spellings.size() == 1) {
410 OS << "\n";
411 OS << " return \"" << Spellings.front().Name << "\";\n";
412 } else {
413 OS << " {\n";
414 std::string SpellingsName = Ident + "_spellings";
415 OS << " static constexpr llvm::directive::Spelling " << SpellingsName
416 << "[] = {\n";
417 for (auto &S : Spellings) {
418 OS << " {\"" << S.Name << "\", {" << S.Versions.Min << ", "
419 << S.Versions.Max << "}},\n";
420 }
421 OS << " };\n";
422 OS << " return llvm::directive::FindName(" << SpellingsName
423 << ", Version);\n";
424 OS << " }\n";
425 }
426 }
427 OS << " }\n"; // switch
428 OS << " llvm_unreachable(\"Invalid " << LangName << " " << Enum
429 << " kind\");\n";
430 OS << "}\n";
431}
432
433// Generate function implementation for get<Enum>KindAndVersions(StringRef Str)
434static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS,
435 StringRef Enum, const DirectiveLanguage &DirLang,
436 StringRef Prefix, bool ImplicitAsUnknown) {
437
438 const auto *DefaultIt = find_if(
439 Range&: Records, P: [](const Record *R) { return R->getValueAsBit(FieldName: "isDefault"); });
440
441 if (DefaultIt == Records.end()) {
442 PrintError(Msg: "At least one " + Enum + " must be defined as default.");
443 return;
444 }
445
446 BaseRecord DefaultRec(*DefaultIt);
447 std::string Qual = getQualifier(DirLang);
448 std::string DefaultName = getIdentifierName(Rec: *DefaultIt, Prefix);
449
450 // std::pair<<Enum>, VersionRange>
451 // get<DirLang><Enum>KindAndVersions(StringRef Str);
452 OS << "\n";
453 OS << "std::pair<" << Qual << Enum << ", llvm::directive::VersionRange> "
454 << Qual << "get" << DirLang.getName() << Enum
455 << "KindAndVersions(llvm::StringRef Str) {\n";
456 OS << " directive::VersionRange All; // Default-initialized to \"all "
457 "versions\"\n";
458 OS << " return StringSwitch<std::pair<" << Enum << ", "
459 << "directive::VersionRange>>(Str)\n";
460
461 directive::VersionRange All;
462
463 // When a given spelling maps to more than one enum kind, this function
464 // will return one of them, but it's unspecified which one.
465 // This can happen whem a directive/clause uses the same spelling as
466 // another directive/clause, e.g. when it varies depending on the version:
467 // OMPC_foo : {"foo", v1.0}, {"bar", v2.0}
468 // OMPC_bar : {"bar", v1.0}, {"baz", v2.0}
469 // or when the same spelling can be used to mean different things:
470 // OMPC_do_one_thing : {"doit"}
471 // OMPC_do_something_else : {"doit"}
472 for (const Record *R : Records) {
473 BaseRecord Rec(R);
474 std::string Ident = ImplicitAsUnknown && R->getValueAsBit(FieldName: "isImplicit")
475 ? DefaultName
476 : getIdentifierName(Rec: R, Prefix);
477
478 for (auto &[Name, Versions] : Rec.getSpellings()) {
479 OS << " .Case(\"" << Name << "\", {" << Ident << ", ";
480 if (Versions.Min == All.Min && Versions.Max == All.Max)
481 OS << "All})\n";
482 else
483 OS << "{" << Versions.Min << ", " << Versions.Max << "}})\n";
484 }
485 }
486 OS << " .Default({" << DefaultName << ", All});\n";
487 OS << "}\n";
488}
489
490// Generate function implementations for
491// <enumClauseValue> get<enumClauseValue>(StringRef Str) and
492// StringRef get<enumClauseValue>Name(<enumClauseValue>)
493static void generateGetClauseVal(const DirectiveLanguage &DirLang,
494 raw_ostream &OS) {
495 StringRef Lang = DirLang.getName();
496 std::string Qual = getQualifier(DirLang);
497
498 for (const Clause C : DirLang.getClauses()) {
499 const auto &ClauseVals = C.getClauseVals();
500 if (ClauseVals.size() <= 0)
501 continue;
502
503 auto DefaultIt = find_if(Range: ClauseVals, P: [](const Record *CV) {
504 return CV->getValueAsBit(FieldName: "isDefault");
505 });
506
507 if (DefaultIt == ClauseVals.end()) {
508 PrintError(Msg: "At least one val in Clause " + C.getRecordName() +
509 " must be defined as default.");
510 return;
511 }
512 const auto DefaultName = (*DefaultIt)->getName();
513
514 StringRef Enum = C.getEnumName();
515 if (Enum.empty()) {
516 PrintError(Msg: "enumClauseValue field not set in Clause" + C.getRecordName() +
517 ".");
518 return;
519 }
520
521 OS << "\n";
522 OS << Qual << Enum << " " << Qual << "get" << Enum
523 << "(llvm::StringRef Str) {\n";
524 OS << " return StringSwitch<" << Enum << ">(Str)\n";
525 for (const EnumVal Val : ClauseVals) {
526 OS << " .Case(\"" << Val.getFormattedName() << "\","
527 << Val.getRecordName() << ")\n";
528 }
529 OS << " .Default(" << DefaultName << ");\n";
530 OS << "}\n";
531
532 OS << "\n";
533 OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual
534 << Enum << " x) {\n";
535 OS << " switch (x) {\n";
536 for (const EnumVal Val : ClauseVals) {
537 OS << " case " << Val.getRecordName() << ":\n";
538 OS << " return \"" << Val.getFormattedName() << "\";\n";
539 }
540 OS << " }\n"; // switch
541 OS << " llvm_unreachable(\"Invalid " << Lang << " " << Enum
542 << " kind\");\n";
543 OS << "}\n";
544 }
545}
546
547static void generateCaseForVersionedClauses(ArrayRef<const Record *> VerClauses,
548 raw_ostream &OS,
549 const DirectiveLanguage &DirLang,
550 StringSet<> &Cases) {
551 StringRef Prefix = DirLang.getClausePrefix();
552 for (const Record *R : VerClauses) {
553 VersionedClause VerClause(R);
554 std::string Name =
555 getIdentifierName(Rec: VerClause.getClause().getRecord(), Prefix);
556 if (Cases.insert(key: Name).second) {
557 OS << " case " << Name << ":\n";
558 OS << " return " << VerClause.getMinVersion()
559 << " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n";
560 }
561 }
562}
563
564// Generate the isAllowedClauseForDirective function implementation.
565static void generateIsAllowedClause(const DirectiveLanguage &DirLang,
566 raw_ostream &OS) {
567 std::string Qual = getQualifier(DirLang);
568
569 OS << "\n";
570 OS << "bool " << Qual << "isAllowedClauseForDirective(" << Qual
571 << "Directive D, " << Qual << "Clause C, unsigned Version) {\n";
572 OS << " assert(unsigned(D) <= Directive_enumSize);\n";
573 OS << " assert(unsigned(C) <= Clause_enumSize);\n";
574
575 OS << " switch (D) {\n";
576
577 StringRef Prefix = DirLang.getDirectivePrefix();
578 for (const Record *R : DirLang.getDirectives()) {
579 Directive Dir(R);
580 OS << " case " << getIdentifierName(Rec: R, Prefix) << ":\n";
581 if (Dir.getAllowedClauses().empty() &&
582 Dir.getAllowedOnceClauses().empty() &&
583 Dir.getAllowedExclusiveClauses().empty() &&
584 Dir.getRequiredClauses().empty()) {
585 OS << " return false;\n";
586 } else {
587 OS << " switch (C) {\n";
588
589 StringSet<> Cases;
590
591 generateCaseForVersionedClauses(VerClauses: Dir.getAllowedClauses(), OS, DirLang,
592 Cases);
593
594 generateCaseForVersionedClauses(VerClauses: Dir.getAllowedOnceClauses(), OS, DirLang,
595 Cases);
596
597 generateCaseForVersionedClauses(VerClauses: Dir.getAllowedExclusiveClauses(), OS,
598 DirLang, Cases);
599
600 generateCaseForVersionedClauses(VerClauses: Dir.getRequiredClauses(), OS, DirLang,
601 Cases);
602
603 OS << " default:\n";
604 OS << " return false;\n";
605 OS << " }\n"; // End of clauses switch
606 }
607 OS << " break;\n";
608 }
609
610 OS << " }\n"; // End of directives switch
611 OS << " llvm_unreachable(\"Invalid " << DirLang.getName()
612 << " Directive kind\");\n";
613 OS << "}\n"; // End of function isAllowedClauseForDirective
614}
615
616static void emitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS,
617 StringRef TableName) {
618 // The leaf constructs are emitted in a form of a 2D table, where each
619 // row corresponds to a directive (and there is a row for each directive).
620 //
621 // Each row consists of
622 // - the id of the directive itself,
623 // - number of leaf constructs that will follow (0 for leafs),
624 // - ids of the leaf constructs (none if the directive is itself a leaf).
625 // The total number of these entries is at most MaxLeafCount+2. If this
626 // number is less than that, it is padded to occupy exactly MaxLeafCount+2
627 // entries in memory.
628 //
629 // The rows are stored in the table in the lexicographical order. This
630 // is intended to enable binary search when mapping a sequence of leafs
631 // back to the compound directive.
632 // The consequence of that is that in order to find a row corresponding
633 // to the given directive, we'd need to scan the first element of each
634 // row. To avoid this, an auxiliary ordering table is created, such that
635 // row for Dir_A = table[auxiliary[Dir_A]].
636
637 ArrayRef<const Record *> Directives = DirLang.getDirectives();
638 DenseMap<const Record *, int> DirId; // Record * -> llvm::omp::Directive
639
640 for (auto [Idx, Rec] : enumerate(First&: Directives))
641 DirId.try_emplace(Key: Rec, Args&: Idx);
642
643 using LeafList = std::vector<int>;
644 int MaxLeafCount = getMaxLeafCount(DirLang);
645
646 // The initial leaf table, rows order is same as directive order.
647 std::vector<LeafList> LeafTable(Directives.size());
648 for (auto [Idx, Rec] : enumerate(First&: Directives)) {
649 Directive Dir(Rec);
650 std::vector<const Record *> Leaves = Dir.getLeafConstructs();
651
652 auto &List = LeafTable[Idx];
653 List.resize(new_size: MaxLeafCount + 2);
654 List[0] = Idx; // The id of the directive itself.
655 List[1] = Leaves.size(); // The number of leaves to follow.
656
657 for (int I = 0; I != MaxLeafCount; ++I)
658 List[I + 2] =
659 static_cast<size_t>(I) < Leaves.size() ? DirId.at(Val: Leaves[I]) : -1;
660 }
661
662 // Some Fortran directives are delimited, i.e. they have the form of
663 // "directive"---"end directive". If "directive" is a compound construct,
664 // then the set of leaf constituents will be nonempty and the same for
665 // both directives. Given this set of leafs, looking up the corresponding
666 // compound directive should return "directive", and not "end directive".
667 // To avoid this problem, gather all "end directives" at the end of the
668 // leaf table, and only do the search on the initial segment of the table
669 // that excludes the "end directives".
670 // It's safe to find all directives whose names begin with "end ". The
671 // problem only exists for compound directives, like "end do simd".
672 // All existing directives with names starting with "end " are either
673 // "end directives" for an existing "directive", or leaf directives
674 // (such as "end declare target").
675 DenseSet<int> EndDirectives;
676 for (auto [Rec, Id] : DirId) {
677 // FIXME: This will need to recognize different spellings for different
678 // versions.
679 StringRef Name = Directive(Rec).getSpellingForIdentifier();
680 if (Name.starts_with_insensitive(Prefix: "end "))
681 EndDirectives.insert(V: Id);
682 }
683
684 // Avoid sorting the vector<vector> array, instead sort an index array.
685 // It will also be useful later to create the auxiliary indexing array.
686 std::vector<int> Ordering(Directives.size());
687 std::iota(first: Ordering.begin(), last: Ordering.end(), value: 0);
688
689 llvm::sort(C&: Ordering, Comp: [&](int A, int B) {
690 auto &LeavesA = LeafTable[A];
691 auto &LeavesB = LeafTable[B];
692 int DirA = LeavesA[0], DirB = LeavesB[0];
693 // First of all, end directives compare greater than non-end directives.
694 bool IsEndA = EndDirectives.contains(V: DirA);
695 bool IsEndB = EndDirectives.contains(V: DirB);
696 if (IsEndA != IsEndB)
697 return IsEndA < IsEndB;
698 if (LeavesA[1] == 0 && LeavesB[1] == 0)
699 return DirA < DirB;
700 return std::lexicographical_compare(first1: &LeavesA[2], last1: &LeavesA[2] + LeavesA[1],
701 first2: &LeavesB[2], last2: &LeavesB[2] + LeavesB[1]);
702 });
703
704 // Emit the table
705
706 // The directives are emitted into a scoped enum, for which the underlying
707 // type is `int` (by default). The code above uses `int` to store directive
708 // ids, so make sure that we catch it when something changes in the
709 // underlying type.
710 StringRef Prefix = DirLang.getDirectivePrefix();
711 std::string Qual = getQualifier(DirLang);
712 std::string DirectiveType = Qual + "Directive";
713 OS << "\nstatic_assert(sizeof(" << DirectiveType << ") == sizeof(int));\n";
714
715 OS << "[[maybe_unused]] static const " << DirectiveType << ' ' << TableName
716 << "[][" << MaxLeafCount + 2 << "] = {\n";
717 for (size_t I = 0, E = Directives.size(); I != E; ++I) {
718 auto &Leaves = LeafTable[Ordering[I]];
719 OS << " {" << Qual << getIdentifierName(Rec: Directives[Leaves[0]], Prefix);
720 OS << ", static_cast<" << DirectiveType << ">(" << Leaves[1] << "),";
721 for (size_t I = 2, E = Leaves.size(); I != E; ++I) {
722 int Idx = Leaves[I];
723 if (Idx >= 0)
724 OS << ' ' << Qual << getIdentifierName(Rec: Directives[Leaves[I]], Prefix)
725 << ',';
726 else
727 OS << " static_cast<" << DirectiveType << ">(-1),";
728 }
729 OS << "},\n";
730 }
731 OS << "};\n\n";
732
733 // Emit a marker where the first "end directive" is.
734 auto FirstE = find_if(Range&: Ordering, P: [&](int RowIdx) {
735 return EndDirectives.contains(V: LeafTable[RowIdx][0]);
736 });
737 OS << "[[maybe_unused]] static auto " << TableName
738 << "EndDirective = " << TableName << " + "
739 << std::distance(first: Ordering.begin(), last: FirstE) << ";\n\n";
740
741 // Emit the auxiliary index table: it's the inverse of the `Ordering`
742 // table above.
743 OS << "[[maybe_unused]] static const int " << TableName << "Ordering[] = {\n";
744 OS << " ";
745 std::vector<int> Reverse(Ordering.size());
746 for (int I = 0, E = Ordering.size(); I != E; ++I)
747 Reverse[Ordering[I]] = I;
748 for (int Idx : Reverse)
749 OS << ' ' << Idx << ',';
750 OS << "\n};\n";
751}
752
753static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
754 raw_ostream &OS) {
755 enum struct Association {
756 None = 0, // None should be the smallest value.
757 Block, // If the order of the rest of these changes, update the
758 Declaration, // 'Reduce' function below.
759 Delimited,
760 LoopNest,
761 LoopSeq,
762 Separating,
763 FromLeaves,
764 Invalid,
765 };
766
767 ArrayRef<const Record *> Associations = DirLang.getAssociations();
768
769 auto GetAssocValue = [](StringRef Name) -> Association {
770 return StringSwitch<Association>(Name)
771 .Case(S: "AS_Block", Value: Association::Block)
772 .Case(S: "AS_Declaration", Value: Association::Declaration)
773 .Case(S: "AS_Delimited", Value: Association::Delimited)
774 .Case(S: "AS_LoopNest", Value: Association::LoopNest)
775 .Case(S: "AS_LoopSeq", Value: Association::LoopSeq)
776 .Case(S: "AS_None", Value: Association::None)
777 .Case(S: "AS_Separating", Value: Association::Separating)
778 .Case(S: "AS_FromLeaves", Value: Association::FromLeaves)
779 .Default(Value: Association::Invalid);
780 };
781
782 auto GetAssocName = [&](Association A) -> StringRef {
783 if (A != Association::Invalid && A != Association::FromLeaves) {
784 const auto *F = find_if(Range&: Associations, P: [&](const Record *R) {
785 return GetAssocValue(R->getName()) == A;
786 });
787 if (F != Associations.end())
788 return (*F)->getValueAsString(FieldName: "name"); // enum name
789 }
790 llvm_unreachable("Unexpected association value");
791 };
792
793 auto ErrorPrefixFor = [&](Directive D) -> std::string {
794 return (Twine("Directive '") + D.getRecordName() + "' in namespace '" +
795 DirLang.getCppNamespace() + "' ")
796 .str();
797 };
798
799 auto Reduce = [&](Association A, Association B) -> Association {
800 if (A > B)
801 std::swap(a&: A, b&: B);
802
803 // Calculate the result using the following rules:
804 // x + x = x
805 // AS_None + x = x
806 // AS_Block + AS_Loop{Nest|Seq} = AS_Loop{Nest|Seq}
807 if (A == Association::None || A == B)
808 return B;
809 if (A == Association::Block &&
810 (B == Association::LoopNest || B == Association::LoopSeq))
811 return B;
812 return Association::Invalid;
813 };
814
815 DenseMap<const Record *, Association> AsMap;
816
817 auto CompAssocImpl = [&](const Record *R, auto &&Self) -> Association {
818 if (auto F = AsMap.find(Val: R); F != AsMap.end())
819 return F->second;
820
821 Directive D(R);
822 Association AS = GetAssocValue(D.getAssociation()->getName());
823 if (AS == Association::Invalid) {
824 PrintFatalError(Msg: ErrorPrefixFor(D) +
825 "has an unrecognized value for association: '" +
826 D.getAssociation()->getName() + "'");
827 }
828 if (AS != Association::FromLeaves) {
829 AsMap.try_emplace(Key: R, Args&: AS);
830 return AS;
831 }
832 // Compute the association from leaf constructs.
833 std::vector<const Record *> Leaves = D.getLeafConstructs();
834 if (Leaves.empty()) {
835 PrintFatalError(Msg: ErrorPrefixFor(D) +
836 "requests association to be computed from leaves, "
837 "but it has no leaves");
838 }
839
840 Association Result = Self(Leaves[0], Self);
841 for (int I = 1, E = Leaves.size(); I < E; ++I) {
842 Association A = Self(Leaves[I], Self);
843 Association R = Reduce(Result, A);
844 if (R == Association::Invalid) {
845 PrintFatalError(Msg: ErrorPrefixFor(D) +
846 "has leaves with incompatible association values: " +
847 GetAssocName(A) + " and " + GetAssocName(R));
848 }
849 Result = R;
850 }
851
852 assert(Result != Association::Invalid);
853 assert(Result != Association::FromLeaves);
854 AsMap.try_emplace(Key: R, Args&: Result);
855 return Result;
856 };
857
858 for (const Record *R : DirLang.getDirectives())
859 CompAssocImpl(R, CompAssocImpl); // Updates AsMap.
860
861 StringRef Prefix = DirLang.getDirectivePrefix();
862
863 OS << "constexpr Association getDirectiveAssociation(Directive Dir) {\n";
864 OS << " switch (Dir) {\n";
865 for (const Record *R : DirLang.getDirectives()) {
866 if (auto F = AsMap.find(Val: R); F != AsMap.end()) {
867 OS << " case " << getIdentifierName(Rec: R, Prefix) << ":\n";
868 OS << " return Association::" << GetAssocName(F->second) << ";\n";
869 }
870 }
871 OS << " } // switch (Dir)\n";
872 OS << "#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 9\n";
873 OS << " abort();\n";
874 OS << "#else\n";
875 OS << " llvm_unreachable(\"Unexpected directive\");\n";
876 OS << "#endif\n";
877 OS << "}\n";
878}
879
880static void generateGetDirectiveCategory(const DirectiveLanguage &DirLang,
881 raw_ostream &OS) {
882 OS << "constexpr Category getDirectiveCategory(Directive Dir) {\n";
883 OS << " switch (Dir) {\n";
884
885 StringRef Prefix = DirLang.getDirectivePrefix();
886
887 for (const Record *R : DirLang.getDirectives()) {
888 Directive D(R);
889 OS << " case " << getIdentifierName(Rec: R, Prefix) << ":\n";
890 OS << " return Category::" << D.getCategory()->getValueAsString(FieldName: "name")
891 << ";\n";
892 }
893 OS << " } // switch (Dir)\n";
894 OS << "#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 9\n";
895 OS << " abort();\n";
896 OS << "#else\n";
897 OS << " llvm_unreachable(\"Unexpected directive\");\n";
898 OS << "#endif\n";
899 OS << "}\n";
900}
901
902static void generateGetDirectivePureSince(const DirectiveLanguage &DirLang,
903 raw_ostream &OS) {
904 // Must match the sentinel in DirectiveBase.td and in
905 // OmpStructureChecker::CheckDirectiveInPureProcedure.
906 constexpr int NeverPure = 0x7FFFFFFF;
907 OS << "constexpr unsigned getDirectivePureSince(Directive Dir) {\n";
908 OS << " switch (Dir) {\n";
909
910 StringRef Prefix = DirLang.getDirectivePrefix();
911
912 for (const Record *R : DirLang.getDirectives()) {
913 Directive D(R);
914 int PureSince = D.getPureSince();
915 if (PureSince == NeverPure)
916 continue;
917 OS << " case " << getIdentifierName(Rec: R, Prefix) << ":\n";
918 OS << " return " << PureSince << ";\n";
919 }
920 OS << " default:\n";
921 OS << " return 0x7FFFFFFF;\n";
922 OS << " } // switch (Dir)\n";
923 OS << "}\n";
924}
925
926static void generateGetDirectiveLanguages(const DirectiveLanguage &DirLang,
927 raw_ostream &OS) {
928 OS << "constexpr SourceLanguage getDirectiveLanguages(Directive D) {\n";
929 OS << " switch (D) {\n";
930
931 StringRef Prefix = DirLang.getDirectivePrefix();
932
933 for (const Record *R : DirLang.getDirectives()) {
934 Directive D(R);
935 OS << " case " << getIdentifierName(Rec: R, Prefix) << ":\n";
936 OS << " return ";
937 llvm::interleave(
938 c: D.getSourceLanguages(), os&: OS,
939 each_fn: [&](const Record *L) {
940 StringRef N = L->getValueAsString(FieldName: "name");
941 OS << "SourceLanguage::" << BaseRecord::getSnakeName(Name: N);
942 },
943 separator: " | ");
944 OS << ";\n";
945 }
946 OS << " } // switch(D)\n";
947 OS << "#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 9\n";
948 OS << " abort();\n";
949 OS << "#else\n";
950 OS << " llvm_unreachable(\"Unexpected directive\");\n";
951 OS << "#endif\n";
952 OS << "}\n";
953}
954
955// Generate the isAllowedLoopModifier function implementation.
956static void generateIsAllowedLoopModifier(const DirectiveLanguage &DirLang,
957 raw_ostream &OS) {
958 std::string Qual = getQualifier(DirLang);
959
960 OS << "\n";
961 OS << "bool " << Qual << "isAllowedLoopModifier(" << Qual << "Directive D, "
962 << Qual << "LoopModifier LM) {\n";
963 OS << " assert(unsigned(D) <= Directive_enumSize);\n";
964
965 OS << " switch (D) {\n";
966
967 StringRef DPrefix = DirLang.getDirectivePrefix();
968 StringRef LMPrefix = DirLang.getLoopModifierPrefix();
969 for (const Record *R : DirLang.getDirectives()) {
970 Directive Dir(R);
971 OS << " case " << getIdentifierName(Rec: R, Prefix: DPrefix) << ":\n";
972 if (Dir.getAllowedLoopModifiers().empty()) {
973 OS << " return false;\n";
974 } else {
975 OS << " switch (LM) {\n";
976
977 for (const Record *LMR : Dir.getAllowedLoopModifiers()) {
978 std::string Name = getIdentifierName(Rec: LMR, Prefix: LMPrefix);
979 OS << " case LoopModifier::" << Name << ":\n";
980 OS << " return true;\n";
981 }
982
983 OS << " default:\n";
984 OS << " return false;\n";
985 OS << " }\n"; // End of modifier switch
986 }
987 OS << " break;\n";
988 }
989
990 OS << " }\n"; // End of directives switch
991 OS << " llvm_unreachable(\"Invalid " << DirLang.getName()
992 << " Directive kind\");\n";
993 OS << "}\n"; // End of function isAllowedLoopModifier
994}
995
996// Generate a simple enum set with the give clauses.
997static void generateClauseSet(ArrayRef<const Record *> VerClauses,
998 raw_ostream &OS, StringRef ClauseSetPrefix,
999 const Directive &Dir,
1000 const DirectiveLanguage &DirLang, Frontend FE) {
1001
1002 OS << "\n";
1003 OS << "static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix
1004 << DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n";
1005
1006 StringRef Prefix = DirLang.getClausePrefix();
1007
1008 for (const VersionedClause VerClause : VerClauses) {
1009 Clause C = VerClause.getClause();
1010 if (FE == Frontend::Flang) {
1011 OS << " Clause::" << getIdentifierName(Rec: C.getRecord(), Prefix) << ",\n";
1012 } else {
1013 assert(FE == Frontend::Clang);
1014 assert(DirLang.getName() == "OpenACC");
1015 OS << " OpenACCClauseKind::" << C.getClangAccSpelling() << ",\n";
1016 }
1017 }
1018 OS << "};\n";
1019}
1020
1021// Generate an enum set for the 4 kinds of clauses linked to a directive.
1022static void generateDirectiveClauseSets(const DirectiveLanguage &DirLang,
1023 Frontend FE, raw_ostream &OS) {
1024 IfDefEmitter Scope(OS, "GEN_" + getFESpelling(FE).upper() +
1025 "_DIRECTIVE_CLAUSE_SETS");
1026
1027 std::string Namespace =
1028 getFESpelling(FE: FE == Frontend::Flang ? Frontend::LLVM : FE).str();
1029 // The namespace has to be different for clang vs flang, as 2 structs with the
1030 // same name but different layout is UB. So just put the 'clang' on in the
1031 // clang namespace.
1032 // Additionally, open namespaces defined in the directive language.
1033 if (!DirLang.getCppNamespace().empty())
1034 Namespace += "::" + DirLang.getCppNamespace().str();
1035 NamespaceEmitter NS(OS, Namespace);
1036
1037 for (const Directive Dir : DirLang.getDirectives()) {
1038 OS << "// Sets for " << Dir.getSpellingForIdentifier() << "\n";
1039
1040 generateClauseSet(VerClauses: Dir.getAllowedClauses(), OS, ClauseSetPrefix: "allowedClauses_", Dir,
1041 DirLang, FE);
1042 generateClauseSet(VerClauses: Dir.getAllowedOnceClauses(), OS, ClauseSetPrefix: "allowedOnceClauses_",
1043 Dir, DirLang, FE);
1044 generateClauseSet(VerClauses: Dir.getAllowedExclusiveClauses(), OS,
1045 ClauseSetPrefix: "allowedExclusiveClauses_", Dir, DirLang, FE);
1046 generateClauseSet(VerClauses: Dir.getRequiredClauses(), OS, ClauseSetPrefix: "requiredClauses_", Dir,
1047 DirLang, FE);
1048 }
1049}
1050
1051// Generate a map of directive (key) with DirectiveClauses struct as values.
1052// The struct holds the 4 sets of enumeration for the 4 kinds of clauses
1053// allowances (allowed, allowed once, allowed exclusive and required).
1054static void generateDirectiveClauseMap(const DirectiveLanguage &DirLang,
1055 Frontend FE, raw_ostream &OS) {
1056 IfDefEmitter Scope(OS, "GEN_" + getFESpelling(FE).upper() +
1057 "_DIRECTIVE_CLAUSE_MAP");
1058
1059 OS << "{\n";
1060
1061 // The namespace has to be different for clang vs flang, as 2 structs with the
1062 // same name but different layout is UB. So just put the 'clang' on in the
1063 // clang namespace.
1064 std::string Qual =
1065 getQualifier(DirLang, FE: FE == Frontend::Flang ? Frontend::LLVM : FE);
1066 StringRef Prefix = DirLang.getDirectivePrefix();
1067
1068 for (const Record *R : DirLang.getDirectives()) {
1069 Directive Dir(R);
1070 std::string Name = getIdentifierName(Rec: R, Prefix);
1071
1072 OS << " {";
1073 if (FE == Frontend::Flang) {
1074 OS << Qual << "Directive::" << Name << ",\n";
1075 } else {
1076 assert(FE == Frontend::Clang);
1077 assert(DirLang.getName() == "OpenACC");
1078 OS << "clang::OpenACCDirectiveKind::" << Dir.getClangAccSpelling()
1079 << ",\n";
1080 }
1081
1082 OS << " {\n";
1083 OS << " " << Qual << "allowedClauses_" << Name << ",\n";
1084 OS << " " << Qual << "allowedOnceClauses_" << Name << ",\n";
1085 OS << " " << Qual << "allowedExclusiveClauses_" << Name << ",\n";
1086 OS << " " << Qual << "requiredClauses_" << Name << ",\n";
1087 OS << " }\n";
1088 OS << " },\n";
1089 }
1090
1091 OS << "}\n";
1092}
1093
1094// Generate classes entry for Flang clauses in the Flang parse-tree
1095// If the clause as a non-generic class, no entry is generated.
1096// If the clause does not hold a value, an EMPTY_CLASS is used.
1097// If the clause class is generic then a WRAPPER_CLASS is used. When the value
1098// is optional, the value class is wrapped into a std::optional.
1099static void generateFlangClauseParserClass(const DirectiveLanguage &DirLang,
1100 raw_ostream &OS) {
1101
1102 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSE_PARSER_CLASSES");
1103
1104 for (const Clause Clause : DirLang.getClauses()) {
1105 if (!Clause.getFlangClass().empty()) {
1106 OS << "WRAPPER_CLASS(" << Clause.getFormattedParserClassName() << ", ";
1107 if (Clause.isValueOptional() && Clause.isValueList()) {
1108 OS << "std::optional<std::list<" << Clause.getFlangClass() << ">>";
1109 } else if (Clause.isValueOptional()) {
1110 OS << "std::optional<" << Clause.getFlangClass() << ">";
1111 } else if (Clause.isValueList()) {
1112 OS << "std::list<" << Clause.getFlangClass() << ">";
1113 } else {
1114 OS << Clause.getFlangClass();
1115 }
1116 } else {
1117 OS << "EMPTY_CLASS(" << Clause.getFormattedParserClassName();
1118 }
1119 OS << ");\n";
1120 }
1121}
1122
1123// Generate a list of the different clause classes for Flang.
1124static void generateFlangClauseParserClassList(const DirectiveLanguage &DirLang,
1125 raw_ostream &OS) {
1126
1127 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSE_PARSER_CLASSES_LIST");
1128
1129 interleaveComma(c: DirLang.getClauses(), os&: OS, each_fn: [&](const Record *C) {
1130 Clause Clause(C);
1131 OS << Clause.getFormattedParserClassName() << "\n";
1132 });
1133}
1134
1135// Generate dump node list for the clauses holding a generic class name.
1136static void generateFlangClauseDump(const DirectiveLanguage &DirLang,
1137 raw_ostream &OS) {
1138
1139 IfDefEmitter Scope(OS, "GEN_FLANG_DUMP_PARSE_TREE_CLAUSES");
1140
1141 for (const Clause Clause : DirLang.getClauses()) {
1142 OS << "NODE(" << DirLang.getFlangClauseBaseClass() << ", "
1143 << Clause.getFormattedParserClassName() << ")\n";
1144 }
1145}
1146
1147// Generate Unparse functions for clauses classes in the Flang parse-tree
1148// If the clause is a non-generic class, no entry is generated.
1149static void generateFlangClauseUnparse(const DirectiveLanguage &DirLang,
1150 raw_ostream &OS) {
1151
1152 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSE_UNPARSE");
1153
1154 StringRef Base = DirLang.getFlangClauseBaseClass();
1155
1156 for (const Clause Clause : DirLang.getClauses()) {
1157 if (Clause.skipFlangUnparser())
1158 continue;
1159 // The unparser doesn't know the effective version, so just pick some
1160 // spelling.
1161 StringRef SomeSpelling = Clause.getSpellingForIdentifier();
1162 std::string Parser = Clause.getFormattedParserClassName();
1163 std::string Upper = SomeSpelling.upper();
1164
1165 if (!Clause.getFlangClass().empty()) {
1166 if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) {
1167 OS << "void Unparse(const " << Base << "::" << Parser << " &x) {\n";
1168 OS << " Word(\"" << Upper << "\");\n";
1169
1170 OS << " Walk(\"(\", x.v, \")\");\n";
1171 OS << "}\n";
1172 } else if (Clause.isValueOptional()) {
1173 OS << "void Unparse(const " << Base << "::" << Parser << " &x) {\n";
1174 OS << " Word(\"" << Upper << "\");\n";
1175 OS << " Put(\"(\");\n";
1176 OS << " if (x.v.has_value())\n";
1177 if (Clause.isValueList())
1178 OS << " Walk(x.v, \",\");\n";
1179 else
1180 OS << " Walk(x.v);\n";
1181 OS << " else\n";
1182 OS << " Put(\"" << Clause.getDefaultValue() << "\");\n";
1183 OS << " Put(\")\");\n";
1184 OS << "}\n";
1185 } else {
1186 OS << "void Unparse(const " << Base << "::" << Parser << " &x) {\n";
1187 OS << " Word(\"" << Upper << "\");\n";
1188 OS << " Put(\"(\");\n";
1189 if (Clause.isValueList())
1190 OS << " Walk(x.v, \",\");\n";
1191 else
1192 OS << " Walk(x.v);\n";
1193 OS << " Put(\")\");\n";
1194 OS << "}\n";
1195 }
1196 } else {
1197 OS << "void Before(const " << Base << "::" << Parser << " &) { Word(\""
1198 << Upper << "\"); }\n";
1199 }
1200 }
1201}
1202
1203// Generate check in the Enter functions for clauses classes.
1204static void generateFlangClauseCheckPrototypes(const DirectiveLanguage &DirLang,
1205 raw_ostream &OS) {
1206
1207 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSE_CHECK_ENTER");
1208
1209 for (const Clause Clause : DirLang.getClauses()) {
1210 OS << "void Enter(const parser::" << DirLang.getFlangClauseBaseClass()
1211 << "::" << Clause.getFormattedParserClassName() << " &);\n";
1212 }
1213}
1214
1215// Generate the mapping for clauses between the parser class and the
1216// corresponding clause Kind
1217static void generateFlangClauseParserKindMap(const DirectiveLanguage &DirLang,
1218 raw_ostream &OS) {
1219
1220 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSE_PARSER_KIND_MAP");
1221
1222 StringRef Prefix = DirLang.getClausePrefix();
1223 std::string Qual = getQualifier(DirLang);
1224
1225 for (const Record *R : DirLang.getClauses()) {
1226 Clause C(R);
1227 OS << "if constexpr (std::is_same_v<A, parser::"
1228 << DirLang.getFlangClauseBaseClass()
1229 << "::" << C.getFormattedParserClassName();
1230 OS << ">)\n";
1231 OS << " return " << Qual << "Clause::" << getIdentifierName(Rec: R, Prefix)
1232 << ";\n";
1233 }
1234
1235 OS << "llvm_unreachable(\"Invalid " << DirLang.getName()
1236 << " Parser clause\");\n";
1237}
1238
1239// Generate the parser for the clauses.
1240static void generateFlangClausesParser(const DirectiveLanguage &DirLang,
1241 raw_ostream &OS) {
1242 std::vector<const Record *> Clauses = DirLang.getClauses();
1243 // Sort clauses in the reverse alphabetical order with respect to their
1244 // names and aliases, so that longer names are tried before shorter ones.
1245 std::vector<RecordWithSpelling> Names = getSpellings(Records: Clauses);
1246 llvm::sort(C&: Names, Comp: [](const auto &A, const auto &B) {
1247 return A.second.Name > B.second.Name;
1248 });
1249 IfDefEmitter Scope(OS, "GEN_FLANG_CLAUSES_PARSER");
1250 StringRef Base = DirLang.getFlangClauseBaseClass();
1251
1252 unsigned LastIndex = Names.size() - 1;
1253 OS << "TYPE_PARSER(\n";
1254 for (auto [Index, RecSp] : llvm::enumerate(First&: Names)) {
1255 auto [R, S] = RecSp;
1256 Clause C(R);
1257
1258 StringRef FlangClass = C.getFlangClass();
1259 OS << " \"" << S.Name << "\" >> construct<" << Base << ">(construct<"
1260 << Base << "::" << C.getFormattedParserClassName() << ">(";
1261 if (FlangClass.empty()) {
1262 OS << "))";
1263 if (Index != LastIndex)
1264 OS << " ||";
1265 OS << "\n";
1266 continue;
1267 }
1268
1269 if (C.isValueOptional())
1270 OS << "maybe(";
1271 OS << "parenthesized(";
1272 if (C.isValueList())
1273 OS << "nonemptyList(";
1274
1275 if (!C.getPrefix().empty())
1276 OS << "\"" << C.getPrefix() << " :\" >> ";
1277
1278 // The common Flang parser are used directly. Their name is identical to
1279 // the Flang class with first letter as lowercase. If the Flang class is
1280 // not a common class, we assume there is a specific Parser<>{} with the
1281 // Flang class name provided.
1282 SmallString<128> Scratch;
1283 StringRef Parser =
1284 StringSwitch<StringRef>(FlangClass)
1285 .Case(S: "Name", Value: "name")
1286 .Case(S: "ScalarIntConstantExpr", Value: "scalarIntConstantExpr")
1287 .Case(S: "ScalarIntExpr", Value: "scalarIntExpr")
1288 .Case(S: "ScalarExpr", Value: "scalarExpr")
1289 .Case(S: "ScalarLogicalExpr", Value: "scalarLogicalExpr")
1290 .Default(Value: ("Parser<" + FlangClass + ">{}").toStringRef(Out&: Scratch));
1291 OS << Parser;
1292 if (!C.getPrefix().empty() && C.isPrefixOptional())
1293 OS << " || " << Parser;
1294 if (C.isValueList()) // close nonemptyList(.
1295 OS << ")";
1296 OS << ")"; // close parenthesized(.
1297
1298 if (C.isValueOptional()) // close maybe(.
1299 OS << ")";
1300 OS << "))";
1301 if (Index != LastIndex)
1302 OS << " ||";
1303 OS << "\n";
1304 }
1305 OS << ")\n";
1306}
1307
1308// Generate the implementation section for the enumeration in the directive
1309// language
1310static void emitDirectivesClangImpl(const DirectiveLanguage &DirLang,
1311 raw_ostream &OS) {
1312 // Currently we only have work to do for OpenACC, so skip otherwise.
1313 if (DirLang.getName() != "OpenACC")
1314 return;
1315
1316 generateDirectiveClauseSets(DirLang, FE: Frontend::Clang, OS);
1317 generateDirectiveClauseMap(DirLang, FE: Frontend::Clang, OS);
1318}
1319// Generate the implementation section for the enumeration in the directive
1320// language
1321static void emitDirectivesFlangImpl(const DirectiveLanguage &DirLang,
1322 raw_ostream &OS) {
1323 generateDirectiveClauseSets(DirLang, FE: Frontend::Flang, OS);
1324
1325 generateDirectiveClauseMap(DirLang, FE: Frontend::Flang, OS);
1326
1327 generateFlangClauseParserClass(DirLang, OS);
1328
1329 generateFlangClauseParserClassList(DirLang, OS);
1330
1331 generateFlangClauseDump(DirLang, OS);
1332
1333 generateFlangClauseUnparse(DirLang, OS);
1334
1335 generateFlangClauseCheckPrototypes(DirLang, OS);
1336
1337 generateFlangClauseParserKindMap(DirLang, OS);
1338
1339 generateFlangClausesParser(DirLang, OS);
1340}
1341
1342static void generateClauseClassMacro(const DirectiveLanguage &DirLang,
1343 raw_ostream &OS) {
1344 // Generate macros style information for legacy code in clang
1345 IfDefEmitter Scope(OS, "GEN_CLANG_CLAUSE_CLASS");
1346
1347 StringRef Prefix = DirLang.getClausePrefix();
1348
1349 OS << "#ifndef CLAUSE\n";
1350 OS << "#define CLAUSE(Enum, Str, Implicit)\n";
1351 OS << "#endif\n";
1352 OS << "#ifndef CLAUSE_CLASS\n";
1353 OS << "#define CLAUSE_CLASS(Enum, Str, Class)\n";
1354 OS << "#endif\n";
1355 OS << "#ifndef CLAUSE_NO_CLASS\n";
1356 OS << "#define CLAUSE_NO_CLASS(Enum, Str)\n";
1357 OS << "#endif\n";
1358 OS << "\n";
1359 OS << "#define __CLAUSE(Name, Class) \\\n";
1360 OS << " CLAUSE(" << Prefix << "##Name, #Name, /* Implicit */ false) \\\n";
1361 OS << " CLAUSE_CLASS(" << Prefix << "##Name, #Name, Class)\n";
1362 OS << "#define __CLAUSE_NO_CLASS(Name) \\\n";
1363 OS << " CLAUSE(" << Prefix << "##Name, #Name, /* Implicit */ false) \\\n";
1364 OS << " CLAUSE_NO_CLASS(" << Prefix << "##Name, #Name)\n";
1365 OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class) \\\n";
1366 OS << " CLAUSE(" << Prefix << "##Name, Str, /* Implicit */ true) \\\n";
1367 OS << " CLAUSE_CLASS(" << Prefix << "##Name, Str, Class)\n";
1368 OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str) \\\n";
1369 OS << " CLAUSE(" << Prefix << "##Name, Str, /* Implicit */ true) \\\n";
1370 OS << " CLAUSE_NO_CLASS(" << Prefix << "##Name, Str)\n";
1371 OS << "\n";
1372
1373 for (const Clause C : DirLang.getClauses()) {
1374 std::string Name = C.getFormattedName();
1375 if (C.getClangClass().empty()) { // NO_CLASS
1376 if (C.isImplicit()) {
1377 OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << Name << ", \"" << Name
1378 << "\")\n";
1379 } else {
1380 OS << "__CLAUSE_NO_CLASS(" << Name << ")\n";
1381 }
1382 } else { // CLASS
1383 if (C.isImplicit()) {
1384 OS << "__IMPLICIT_CLAUSE_CLASS(" << Name << ", \"" << Name << "\", "
1385 << C.getClangClass() << ")\n";
1386 } else {
1387 OS << "__CLAUSE(" << Name << ", " << C.getClangClass() << ")\n";
1388 }
1389 }
1390 }
1391
1392 OS << "\n";
1393 OS << "#undef __IMPLICIT_CLAUSE_NO_CLASS\n";
1394 OS << "#undef __IMPLICIT_CLAUSE_CLASS\n";
1395 OS << "#undef __CLAUSE_NO_CLASS\n";
1396 OS << "#undef __CLAUSE\n";
1397 OS << "#undef CLAUSE_NO_CLASS\n";
1398 OS << "#undef CLAUSE_CLASS\n";
1399 OS << "#undef CLAUSE\n";
1400}
1401
1402static void emitDirectivesConstexprImpl(const DirectiveLanguage &DirLang,
1403 raw_ostream &OS) {
1404 OS << "// Constexpr functions.\n";
1405 OS << "\n";
1406 generateGetDirectiveAssociation(DirLang, OS);
1407 OS << "\n";
1408 generateGetDirectiveCategory(DirLang, OS);
1409 OS << "\n";
1410 generateGetDirectivePureSince(DirLang, OS);
1411 OS << "\n";
1412 generateGetDirectiveLanguages(DirLang, OS);
1413}
1414
1415// Generate the implemenation for the enumeration in the directive
1416// language. This code can be included in library.
1417void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
1418 raw_ostream &OS) {
1419 IfDefEmitter Scope(OS, "GEN_DIRECTIVES_IMPL");
1420
1421 StringRef DPrefix = DirLang.getDirectivePrefix();
1422 StringRef CPrefix = DirLang.getClausePrefix();
1423
1424 OS << "#include \"llvm/Frontend/Directive/Spelling.h\"\n";
1425 OS << "#include \"llvm/Support/ErrorHandling.h\"\n";
1426 OS << "#include <utility>\n";
1427
1428 // getDirectiveKind(StringRef Str)
1429 generateGetKind(Records: DirLang.getDirectives(), OS, Enum: "Directive", DirLang, Prefix: DPrefix,
1430 /*ImplicitAsUnknown=*/false);
1431
1432 // getDirectiveName(Directive Kind)
1433 generateGetName(Records: DirLang.getDirectives(), OS, Enum: "Directive", DirLang,
1434 LangName: DirLang.getName(), Prefix: DPrefix);
1435
1436 // getClauseKind(StringRef Str)
1437 generateGetKind(Records: DirLang.getClauses(), OS, Enum: "Clause", DirLang, Prefix: CPrefix,
1438 /*ImplicitAsUnknown=*/true);
1439
1440 // getClauseName(Clause Kind)
1441 generateGetName(Records: DirLang.getClauses(), OS, Enum: "Clause", DirLang,
1442 LangName: DirLang.getName(), Prefix: CPrefix);
1443
1444 // <enumClauseValue> get<enumClauseValue>(StringRef Str) ; string -> value
1445 // StringRef get<enumClauseValue>Name(<enumClauseValue>) ; value -> string
1446 generateGetClauseVal(DirLang, OS);
1447
1448 // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
1449 generateIsAllowedClause(DirLang, OS);
1450
1451 // isAllowedLoopModifier(Directive D, LoopModifier LM)
1452 generateIsAllowedLoopModifier(DirLang, OS);
1453
1454 // getLoopModifierName(LoopModifier Kind)
1455 generateGetName(Records: DirLang.getLoopModifiers(), OS, Enum: "LoopModifier", DirLang, LangName: "",
1456 Prefix: DirLang.getLoopModifierPrefix());
1457
1458 // Leaf table for getLeafConstructs, etc.
1459 emitLeafTable(DirLang, OS, TableName: "LeafConstructTable");
1460}
1461
1462// Generate the implemenation section for the enumeration in the directive
1463// language.
1464static void emitDirectivesImpl(const RecordKeeper &Records, raw_ostream &OS) {
1465 const auto DirLang = DirectiveLanguage(Records);
1466 if (DirLang.HasValidityErrors())
1467 return;
1468
1469 emitDirectivesFlangImpl(DirLang, OS);
1470
1471 emitDirectivesClangImpl(DirLang, OS);
1472
1473 generateClauseClassMacro(DirLang, OS);
1474
1475 emitDirectivesBasicImpl(DirLang, OS);
1476}
1477
1478static TableGen::Emitter::Opt
1479 X("gen-directive-decl", emitDirectivesDecl,
1480 "Generate directive related declaration code (header file)");
1481
1482static TableGen::Emitter::Opt
1483 Y("gen-directive-impl", emitDirectivesImpl,
1484 "Generate directive related implementation code");
1485