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