xref: /freebsd/contrib/llvm-project/llvm/utils/TableGen/DirectiveEmitter.cpp (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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/ADT/STLExtras.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/TableGen/Error.h"
19 #include "llvm/TableGen/Record.h"
20 #include "llvm/TableGen/TableGenBackend.h"
21 
22 using namespace llvm;
23 
24 namespace {
25 // Simple RAII helper for defining ifdef-undef-endif scopes.
26 class IfDefScope {
27 public:
28   IfDefScope(StringRef Name, raw_ostream &OS) : Name(Name), OS(OS) {
29     OS << "#ifdef " << Name << "\n"
30        << "#undef " << Name << "\n";
31   }
32 
33   ~IfDefScope() { OS << "\n#endif // " << Name << "\n\n"; }
34 
35 private:
36   StringRef Name;
37   raw_ostream &OS;
38 };
39 } // end anonymous namespace
40 
41 namespace llvm {
42 
43 // Get Directive or Clause name formatted by replacing whitespaces with
44 // underscores.
45 std::string getFormattedName(StringRef Name) {
46   std::string N = Name.str();
47   std::replace(N.begin(), N.end(), ' ', '_');
48   return N;
49 }
50 
51 // Generate enum class
52 void GenerateEnumClass(const std::vector<Record *> &Records, raw_ostream &OS,
53                        StringRef Enum, StringRef Prefix, StringRef CppNamespace,
54                        bool MakeEnumAvailableInNamespace) {
55   OS << "\n";
56   OS << "enum class " << Enum << " {\n";
57   for (const auto &R : Records) {
58     const auto Name = R->getValueAsString("name");
59     OS << "  " << Prefix << getFormattedName(Name) << ",\n";
60   }
61   OS << "};\n";
62   OS << "\n";
63   OS << "static constexpr std::size_t " << Enum
64      << "_enumSize = " << Records.size() << ";\n";
65 
66   // Make the enum values available in the defined namespace. This allows us to
67   // write something like Enum_X if we have a `using namespace <CppNamespace>`.
68   // At the same time we do not loose the strong type guarantees of the enum
69   // class, that is we cannot pass an unsigned as Directive without an explicit
70   // cast.
71   if (MakeEnumAvailableInNamespace) {
72     OS << "\n";
73     for (const auto &R : Records) {
74       const auto FormattedName = getFormattedName(R->getValueAsString("name"));
75       OS << "constexpr auto " << Prefix << FormattedName << " = "
76          << "llvm::" << CppNamespace << "::" << Enum << "::" << Prefix
77          << FormattedName << ";\n";
78     }
79   }
80 }
81 
82 // Generate the declaration section for the enumeration in the directive
83 // language
84 void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
85 
86   const auto &DirectiveLanguages =
87       Records.getAllDerivedDefinitions("DirectiveLanguage");
88 
89   if (DirectiveLanguages.size() != 1) {
90     PrintError("A single definition of DirectiveLanguage is needed.");
91     return;
92   }
93 
94   const auto &DirectiveLanguage = DirectiveLanguages[0];
95   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
96   StringRef DirectivePrefix =
97       DirectiveLanguage->getValueAsString("directivePrefix");
98   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
99   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
100   bool MakeEnumAvailableInNamespace =
101       DirectiveLanguage->getValueAsBit("makeEnumAvailableInNamespace");
102   bool EnableBitmaskEnumInNamespace =
103       DirectiveLanguage->getValueAsBit("enableBitmaskEnumInNamespace");
104 
105   OS << "#ifndef LLVM_" << LanguageName << "_INC\n";
106   OS << "#define LLVM_" << LanguageName << "_INC\n";
107 
108   if (EnableBitmaskEnumInNamespace)
109     OS << "\n#include \"llvm/ADT/BitmaskEnum.h\"\n";
110 
111   OS << "\n";
112   OS << "namespace llvm {\n";
113   OS << "class StringRef;\n";
114 
115   // Open namespaces defined in the directive language
116   llvm::SmallVector<StringRef, 2> Namespaces;
117   llvm::SplitString(CppNamespace, Namespaces, "::");
118   for (auto Ns : Namespaces)
119     OS << "namespace " << Ns << " {\n";
120 
121   if (EnableBitmaskEnumInNamespace)
122     OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";
123 
124   // Emit Directive enumeration
125   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
126   GenerateEnumClass(Directives, OS, "Directive", DirectivePrefix, CppNamespace,
127                     MakeEnumAvailableInNamespace);
128 
129   // Emit Clause enumeration
130   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
131   GenerateEnumClass(Clauses, OS, "Clause", ClausePrefix, CppNamespace,
132                     MakeEnumAvailableInNamespace);
133 
134   // Generic function signatures
135   OS << "\n";
136   OS << "// Enumeration helper functions\n";
137   OS << "Directive get" << LanguageName
138      << "DirectiveKind(llvm::StringRef Str);\n";
139   OS << "\n";
140   OS << "llvm::StringRef get" << LanguageName
141      << "DirectiveName(Directive D);\n";
142   OS << "\n";
143   OS << "Clause get" << LanguageName << "ClauseKind(llvm::StringRef Str);\n";
144   OS << "\n";
145   OS << "llvm::StringRef get" << LanguageName << "ClauseName(Clause C);\n";
146   OS << "\n";
147   OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "
148      << "Version.\n";
149   OS << "bool isAllowedClauseForDirective(Directive D, "
150      << "Clause C, unsigned Version);\n";
151   OS << "\n";
152 
153   // Closing namespaces
154   for (auto Ns : llvm::reverse(Namespaces))
155     OS << "} // namespace " << Ns << "\n";
156 
157   OS << "} // namespace llvm\n";
158 
159   OS << "#endif // LLVM_" << LanguageName << "_INC\n";
160 }
161 
162 // Generate function implementation for get<Enum>Name(StringRef Str)
163 void GenerateGetName(const std::vector<Record *> &Records, raw_ostream &OS,
164                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
165                      StringRef Namespace) {
166   OS << "\n";
167   OS << "llvm::StringRef llvm::" << Namespace << "::get" << LanguageName << Enum
168      << "Name(" << Enum << " Kind) {\n";
169   OS << "  switch (Kind) {\n";
170   for (const auto &R : Records) {
171     const auto Name = R->getValueAsString("name");
172     const auto AlternativeName = R->getValueAsString("alternativeName");
173     OS << "    case " << Prefix << getFormattedName(Name) << ":\n";
174     OS << "      return \"";
175     if (AlternativeName.empty())
176       OS << Name;
177     else
178       OS << AlternativeName;
179     OS << "\";\n";
180   }
181   OS << "  }\n"; // switch
182   OS << "  llvm_unreachable(\"Invalid " << LanguageName << " " << Enum
183      << " kind\");\n";
184   OS << "}\n";
185 }
186 
187 // Generate function implementation for get<Enum>Kind(StringRef Str)
188 void GenerateGetKind(const std::vector<Record *> &Records, raw_ostream &OS,
189                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
190                      StringRef Namespace, bool ImplicitAsUnknown) {
191 
192   auto DefaultIt = std::find_if(Records.begin(), Records.end(), [](Record *R) {
193     return R->getValueAsBit("isDefault") == true;
194   });
195 
196   if (DefaultIt == Records.end()) {
197     PrintError("A least one " + Enum + " must be defined as default.");
198     return;
199   }
200 
201   const auto FormattedDefaultName =
202       getFormattedName((*DefaultIt)->getValueAsString("name"));
203 
204   OS << "\n";
205   OS << Enum << " llvm::" << Namespace << "::get" << LanguageName << Enum
206      << "Kind(llvm::StringRef Str) {\n";
207   OS << "  return llvm::StringSwitch<" << Enum << ">(Str)\n";
208 
209   for (const auto &R : Records) {
210     const auto Name = R->getValueAsString("name");
211     if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {
212       OS << "    .Case(\"" << Name << "\"," << Prefix << FormattedDefaultName
213          << ")\n";
214     } else {
215       OS << "    .Case(\"" << Name << "\"," << Prefix << getFormattedName(Name)
216          << ")\n";
217     }
218   }
219   OS << "    .Default(" << Prefix << FormattedDefaultName << ");\n";
220   OS << "}\n";
221 }
222 
223 void GenerateCaseForVersionedClauses(const std::vector<Record *> &Clauses,
224                                      raw_ostream &OS, StringRef DirectiveName,
225                                      StringRef DirectivePrefix,
226                                      StringRef ClausePrefix,
227                                      llvm::StringSet<> &Cases) {
228   for (const auto &C : Clauses) {
229     const auto MinVersion = C->getValueAsInt("minVersion");
230     const auto MaxVersion = C->getValueAsInt("maxVersion");
231     const auto SpecificClause = C->getValueAsDef("clause");
232     const auto ClauseName =
233         getFormattedName(SpecificClause->getValueAsString("name"));
234 
235     if (Cases.find(ClauseName) == Cases.end()) {
236       Cases.insert(ClauseName);
237       OS << "        case " << ClausePrefix << ClauseName << ":\n";
238       OS << "          return " << MinVersion << " <= Version && " << MaxVersion
239          << " >= Version;\n";
240     }
241   }
242 }
243 
244 // Generate the isAllowedClauseForDirective function implementation.
245 void GenerateIsAllowedClause(const std::vector<Record *> &Directives,
246                              raw_ostream &OS, StringRef LanguageName,
247                              StringRef DirectivePrefix, StringRef ClausePrefix,
248                              StringRef CppNamespace) {
249   OS << "\n";
250   OS << "bool llvm::" << CppNamespace << "::isAllowedClauseForDirective("
251      << "Directive D, Clause C, unsigned Version) {\n";
252   OS << "  assert(unsigned(D) <= llvm::" << CppNamespace
253      << "::Directive_enumSize);\n";
254   OS << "  assert(unsigned(C) <= llvm::" << CppNamespace
255      << "::Clause_enumSize);\n";
256 
257   OS << "  switch (D) {\n";
258 
259   for (const auto &D : Directives) {
260 
261     const auto DirectiveName = D->getValueAsString("name");
262     const auto &AllowedClauses = D->getValueAsListOfDefs("allowedClauses");
263     const auto &AllowedOnceClauses =
264         D->getValueAsListOfDefs("allowedOnceClauses");
265     const auto &AllowedExclusiveClauses =
266         D->getValueAsListOfDefs("allowedExclusiveClauses");
267     const auto &RequiredClauses = D->getValueAsListOfDefs("requiredClauses");
268 
269     OS << "    case " << DirectivePrefix << getFormattedName(DirectiveName)
270        << ":\n";
271     if (AllowedClauses.size() == 0 && AllowedOnceClauses.size() == 0 &&
272         AllowedExclusiveClauses.size() == 0 && RequiredClauses.size() == 0) {
273       OS << "      return false;\n";
274     } else {
275       OS << "      switch (C) {\n";
276 
277       llvm::StringSet<> Cases;
278 
279       GenerateCaseForVersionedClauses(AllowedClauses, OS, DirectiveName,
280                                       DirectivePrefix, ClausePrefix, Cases);
281 
282       GenerateCaseForVersionedClauses(AllowedOnceClauses, OS, DirectiveName,
283                                       DirectivePrefix, ClausePrefix, Cases);
284 
285       GenerateCaseForVersionedClauses(AllowedExclusiveClauses, OS,
286                                       DirectiveName, DirectivePrefix,
287                                       ClausePrefix, Cases);
288 
289       GenerateCaseForVersionedClauses(RequiredClauses, OS, DirectiveName,
290                                       DirectivePrefix, ClausePrefix, Cases);
291 
292       OS << "        default:\n";
293       OS << "          return false;\n";
294       OS << "      }\n"; // End of clauses switch
295     }
296     OS << "      break;\n";
297   }
298 
299   OS << "  }\n"; // End of directives switch
300   OS << "  llvm_unreachable(\"Invalid " << LanguageName
301      << " Directive kind\");\n";
302   OS << "}\n"; // End of function isAllowedClauseForDirective
303 }
304 
305 // Generate a simple enum set with the give clauses.
306 void GenerateClauseSet(const std::vector<Record *> &Clauses, raw_ostream &OS,
307                        StringRef ClauseEnumSetClass, StringRef ClauseSetPrefix,
308                        StringRef DirectiveName, StringRef DirectivePrefix,
309                        StringRef ClausePrefix, StringRef CppNamespace) {
310 
311   OS << "\n";
312   OS << "  static " << ClauseEnumSetClass << " " << ClauseSetPrefix
313      << DirectivePrefix << getFormattedName(DirectiveName) << " {\n";
314 
315   for (const auto &C : Clauses) {
316     const auto SpecificClause = C->getValueAsDef("clause");
317     const auto ClauseName = SpecificClause->getValueAsString("name");
318     OS << "    llvm::" << CppNamespace << "::Clause::" << ClausePrefix
319        << getFormattedName(ClauseName) << ",\n";
320   }
321   OS << "  };\n";
322 }
323 
324 // Generate an enum set for the 4 kinds of clauses linked to a directive.
325 void GenerateDirectiveClauseSets(const std::vector<Record *> &Directives,
326                                  raw_ostream &OS, StringRef LanguageName,
327                                  StringRef ClauseEnumSetClass,
328                                  StringRef DirectivePrefix,
329                                  StringRef ClausePrefix,
330                                  StringRef CppNamespace) {
331 
332   IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_SETS", OS);
333 
334   OS << "\n";
335   OS << "namespace llvm {\n";
336 
337   // Open namespaces defined in the directive language.
338   llvm::SmallVector<StringRef, 2> Namespaces;
339   llvm::SplitString(CppNamespace, Namespaces, "::");
340   for (auto Ns : Namespaces)
341     OS << "namespace " << Ns << " {\n";
342 
343   for (const auto &D : Directives) {
344     const auto DirectiveName = D->getValueAsString("name");
345 
346     const auto &AllowedClauses = D->getValueAsListOfDefs("allowedClauses");
347     const auto &AllowedOnceClauses =
348         D->getValueAsListOfDefs("allowedOnceClauses");
349     const auto &AllowedExclusiveClauses =
350         D->getValueAsListOfDefs("allowedExclusiveClauses");
351     const auto &RequiredClauses = D->getValueAsListOfDefs("requiredClauses");
352 
353     OS << "\n";
354     OS << "  // Sets for " << DirectiveName << "\n";
355 
356     GenerateClauseSet(AllowedClauses, OS, ClauseEnumSetClass, "allowedClauses_",
357                       DirectiveName, DirectivePrefix, ClausePrefix,
358                       CppNamespace);
359     GenerateClauseSet(AllowedOnceClauses, OS, ClauseEnumSetClass,
360                       "allowedOnceClauses_", DirectiveName, DirectivePrefix,
361                       ClausePrefix, CppNamespace);
362     GenerateClauseSet(AllowedExclusiveClauses, OS, ClauseEnumSetClass,
363                       "allowedExclusiveClauses_", DirectiveName,
364                       DirectivePrefix, ClausePrefix, CppNamespace);
365     GenerateClauseSet(RequiredClauses, OS, ClauseEnumSetClass,
366                       "requiredClauses_", DirectiveName, DirectivePrefix,
367                       ClausePrefix, CppNamespace);
368   }
369 
370   // Closing namespaces
371   for (auto Ns : llvm::reverse(Namespaces))
372     OS << "} // namespace " << Ns << "\n";
373 
374   OS << "} // namespace llvm\n";
375 }
376 
377 // Generate a map of directive (key) with DirectiveClauses struct as values.
378 // The struct holds the 4 sets of enumeration for the 4 kinds of clauses
379 // allowances (allowed, allowed once, allowed exclusive and required).
380 void GenerateDirectiveClauseMap(const std::vector<Record *> &Directives,
381                                 raw_ostream &OS, StringRef LanguageName,
382                                 StringRef ClauseEnumSetClass,
383                                 StringRef DirectivePrefix,
384                                 StringRef ClausePrefix,
385                                 StringRef CppNamespace) {
386 
387   IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS);
388 
389   OS << "\n";
390   OS << "struct " << LanguageName << "DirectiveClauses {\n";
391   OS << "  const " << ClauseEnumSetClass << " allowed;\n";
392   OS << "  const " << ClauseEnumSetClass << " allowedOnce;\n";
393   OS << "  const " << ClauseEnumSetClass << " allowedExclusive;\n";
394   OS << "  const " << ClauseEnumSetClass << " requiredOneOf;\n";
395   OS << "};\n";
396 
397   OS << "\n";
398 
399   OS << "std::unordered_map<llvm::" << CppNamespace << "::Directive, "
400      << LanguageName << "DirectiveClauses>\n";
401   OS << "    directiveClausesTable = {\n";
402 
403   for (const auto &D : Directives) {
404     const auto FormattedDirectiveName =
405         getFormattedName(D->getValueAsString("name"));
406     OS << "  {llvm::" << CppNamespace << "::Directive::" << DirectivePrefix
407        << FormattedDirectiveName << ",\n";
408     OS << "    {\n";
409     OS << "      llvm::" << CppNamespace << "::allowedClauses_"
410        << DirectivePrefix << FormattedDirectiveName << ",\n";
411     OS << "      llvm::" << CppNamespace << "::allowedOnceClauses_"
412        << DirectivePrefix << FormattedDirectiveName << ",\n";
413     OS << "      llvm::" << CppNamespace << "::allowedExclusiveClauses_"
414        << DirectivePrefix << FormattedDirectiveName << ",\n";
415     OS << "      llvm::" << CppNamespace << "::requiredClauses_"
416        << DirectivePrefix << FormattedDirectiveName << ",\n";
417     OS << "    }\n";
418     OS << "  },\n";
419   }
420 
421   OS << "};\n";
422 }
423 
424 // Generate the implemenation section for the enumeration in the directive
425 // language
426 void EmitDirectivesFlangImpl(const std::vector<Record *> &Directives,
427                              raw_ostream &OS, StringRef LanguageName,
428                              StringRef ClauseEnumSetClass,
429                              StringRef DirectivePrefix, StringRef ClausePrefix,
430                              StringRef CppNamespace) {
431 
432   GenerateDirectiveClauseSets(Directives, OS, LanguageName, ClauseEnumSetClass,
433                               DirectivePrefix, ClausePrefix, CppNamespace);
434 
435   GenerateDirectiveClauseMap(Directives, OS, LanguageName, ClauseEnumSetClass,
436                              DirectivePrefix, ClausePrefix, CppNamespace);
437 }
438 
439 // Generate the implemenation section for the enumeration in the directive
440 // language.
441 void EmitDirectivesGen(RecordKeeper &Records, raw_ostream &OS) {
442 
443   const auto &DirectiveLanguages =
444       Records.getAllDerivedDefinitions("DirectiveLanguage");
445 
446   if (DirectiveLanguages.size() != 1) {
447     PrintError("A single definition of DirectiveLanguage is needed.");
448     return;
449   }
450 
451   const auto &DirectiveLanguage = DirectiveLanguages[0];
452   StringRef DirectivePrefix =
453       DirectiveLanguage->getValueAsString("directivePrefix");
454   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
455   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
456   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
457   StringRef ClauseEnumSetClass =
458       DirectiveLanguage->getValueAsString("clauseEnumSetClass");
459 
460   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
461 
462   EmitDirectivesFlangImpl(Directives, OS, LanguageName, ClauseEnumSetClass,
463                           DirectivePrefix, ClausePrefix, CppNamespace);
464 }
465 
466 // Generate the implemenation for the enumeration in the directive
467 // language. This code can be included in library.
468 void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {
469 
470   const auto &DirectiveLanguages =
471       Records.getAllDerivedDefinitions("DirectiveLanguage");
472 
473   if (DirectiveLanguages.size() != 1) {
474     PrintError("A single definition of DirectiveLanguage is needed.");
475     return;
476   }
477 
478   const auto &DirectiveLanguage = DirectiveLanguages[0];
479   StringRef DirectivePrefix =
480       DirectiveLanguage->getValueAsString("directivePrefix");
481   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
482   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
483   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
484   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
485   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
486 
487   StringRef IncludeHeader =
488       DirectiveLanguage->getValueAsString("includeHeader");
489 
490   if (!IncludeHeader.empty())
491     OS << "#include \"" << IncludeHeader << "\"\n\n";
492 
493   OS << "#include \"llvm/ADT/StringRef.h\"\n";
494   OS << "#include \"llvm/ADT/StringSwitch.h\"\n";
495   OS << "#include \"llvm/Support/ErrorHandling.h\"\n";
496   OS << "\n";
497   OS << "using namespace llvm;\n";
498   llvm::SmallVector<StringRef, 2> Namespaces;
499   llvm::SplitString(CppNamespace, Namespaces, "::");
500   for (auto Ns : Namespaces)
501     OS << "using namespace " << Ns << ";\n";
502 
503   // getDirectiveKind(StringRef Str)
504   GenerateGetKind(Directives, OS, "Directive", DirectivePrefix, LanguageName,
505                   CppNamespace, /*ImplicitAsUnknown=*/false);
506 
507   // getDirectiveName(Directive Kind)
508   GenerateGetName(Directives, OS, "Directive", DirectivePrefix, LanguageName,
509                   CppNamespace);
510 
511   // getClauseKind(StringRef Str)
512   GenerateGetKind(Clauses, OS, "Clause", ClausePrefix, LanguageName,
513                   CppNamespace, /*ImplicitAsUnknown=*/true);
514 
515   // getClauseName(Clause Kind)
516   GenerateGetName(Clauses, OS, "Clause", ClausePrefix, LanguageName,
517                   CppNamespace);
518 
519   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
520   GenerateIsAllowedClause(Directives, OS, LanguageName, DirectivePrefix,
521                           ClausePrefix, CppNamespace);
522 }
523 
524 } // namespace llvm
525