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/TableGen/Error.h"
18 #include "llvm/TableGen/Record.h"
19 #include "llvm/TableGen/TableGenBackend.h"
20 
21 using namespace llvm;
22 
23 namespace llvm {
24 
25 // Get Directive or Clause name formatted by replacing whitespaces with
26 // underscores.
27 std::string getFormattedName(StringRef Name) {
28   std::string N = Name.str();
29   std::replace(N.begin(), N.end(), ' ', '_');
30   return N;
31 }
32 
33 // Generate enum class
34 void GenerateEnumClass(const std::vector<Record *> &Records, raw_ostream &OS,
35                        StringRef Enum, StringRef Prefix, StringRef CppNamespace,
36                        bool MakeEnumAvailableInNamespace) {
37   OS << "\n";
38   OS << "enum class " << Enum << " {\n";
39   for (const auto &R : Records) {
40     const auto Name = R->getValueAsString("name");
41     OS << "  " << Prefix << getFormattedName(Name) << ",\n";
42   }
43   OS << "};\n";
44   OS << "\n";
45   OS << "static constexpr std::size_t " << Enum
46      << "_enumSize = " << Records.size() << ";\n";
47 
48   // Make the enum values available in the defined namespace. This allows us to
49   // write something like Enum_X if we have a `using namespace <CppNamespace>`.
50   // At the same time we do not loose the strong type guarantees of the enum
51   // class, that is we cannot pass an unsigned as Directive without an explicit
52   // cast.
53   if (MakeEnumAvailableInNamespace) {
54     OS << "\n";
55     for (const auto &R : Records) {
56       const auto FormattedName = getFormattedName(R->getValueAsString("name"));
57       OS << "constexpr auto " << Prefix << FormattedName << " = "
58          << "llvm::" << CppNamespace << "::" << Enum << "::" << Prefix
59          << FormattedName << ";\n";
60     }
61   }
62 }
63 
64 // Generate the declaration section for the enumeration in the directive
65 // language
66 void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
67 
68   const auto &DirectiveLanguages =
69       Records.getAllDerivedDefinitions("DirectiveLanguage");
70 
71   if (DirectiveLanguages.size() != 1) {
72     PrintError("A single definition of DirectiveLanguage is needed.");
73     return;
74   }
75 
76   const auto &DirectiveLanguage = DirectiveLanguages[0];
77   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
78   StringRef DirectivePrefix =
79       DirectiveLanguage->getValueAsString("directivePrefix");
80   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
81   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
82   bool MakeEnumAvailableInNamespace =
83       DirectiveLanguage->getValueAsBit("makeEnumAvailableInNamespace");
84   bool EnableBitmaskEnumInNamespace =
85       DirectiveLanguage->getValueAsBit("enableBitmaskEnumInNamespace");
86 
87   OS << "#ifndef LLVM_" << LanguageName << "_INC\n";
88   OS << "#define LLVM_" << LanguageName << "_INC\n";
89 
90   if (EnableBitmaskEnumInNamespace)
91     OS << "\n#include \"llvm/ADT/BitmaskEnum.h\"\n";
92 
93   OS << "\n";
94   OS << "namespace llvm {\n";
95   OS << "class StringRef;\n";
96 
97   // Open namespaces defined in the directive language
98   llvm::SmallVector<StringRef, 2> Namespaces;
99   llvm::SplitString(CppNamespace, Namespaces, "::");
100   for (auto Ns : Namespaces)
101     OS << "namespace " << Ns << " {\n";
102 
103   if (EnableBitmaskEnumInNamespace)
104     OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";
105 
106   // Emit Directive enumeration
107   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
108   GenerateEnumClass(Directives, OS, "Directive", DirectivePrefix, CppNamespace,
109                     MakeEnumAvailableInNamespace);
110 
111   // Emit Clause enumeration
112   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
113   GenerateEnumClass(Clauses, OS, "Clause", ClausePrefix, CppNamespace,
114                     MakeEnumAvailableInNamespace);
115 
116   // Generic function signatures
117   OS << "\n";
118   OS << "// Enumeration helper functions\n";
119   OS << "Directive get" << LanguageName
120      << "DirectiveKind(llvm::StringRef Str);\n";
121   OS << "\n";
122   OS << "llvm::StringRef get" << LanguageName
123      << "DirectiveName(Directive D);\n";
124   OS << "\n";
125   OS << "Clause get" << LanguageName << "ClauseKind(llvm::StringRef Str);\n";
126   OS << "\n";
127   OS << "llvm::StringRef get" << LanguageName << "ClauseName(Clause C);\n";
128   OS << "\n";
129   OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "
130      << "Version.\n";
131   OS << "bool isAllowedClauseForDirective(Directive D, "
132      << "Clause C, unsigned Version);\n";
133   OS << "\n";
134 
135   // Closing namespaces
136   for (auto Ns : llvm::reverse(Namespaces))
137     OS << "} // namespace " << Ns << "\n";
138 
139   OS << "} // namespace llvm\n";
140 
141   OS << "#endif // LLVM_" << LanguageName << "_INC\n";
142 }
143 
144 // Generate function implementation for get<Enum>Name(StringRef Str)
145 void GenerateGetName(const std::vector<Record *> &Records, raw_ostream &OS,
146                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
147                      StringRef Namespace) {
148   OS << "\n";
149   OS << "llvm::StringRef llvm::" << Namespace << "::get" << LanguageName << Enum
150      << "Name(" << Enum << " Kind) {\n";
151   OS << "  switch (Kind) {\n";
152   for (const auto &R : Records) {
153     const auto Name = R->getValueAsString("name");
154     const auto AlternativeName = R->getValueAsString("alternativeName");
155     OS << "    case " << Prefix << getFormattedName(Name) << ":\n";
156     OS << "      return \"";
157     if (AlternativeName.empty())
158       OS << Name;
159     else
160       OS << AlternativeName;
161     OS << "\";\n";
162   }
163   OS << "  }\n"; // switch
164   OS << "  llvm_unreachable(\"Invalid " << LanguageName << " " << Enum
165      << " kind\");\n";
166   OS << "}\n";
167 }
168 
169 // Generate function implementation for get<Enum>Kind(StringRef Str)
170 void GenerateGetKind(const std::vector<Record *> &Records, raw_ostream &OS,
171                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
172                      StringRef Namespace, bool ImplicitAsUnknown) {
173 
174   auto DefaultIt = std::find_if(Records.begin(), Records.end(), [](Record *R) {
175     return R->getValueAsBit("isDefault") == true;
176   });
177 
178   if (DefaultIt == Records.end()) {
179     PrintError("A least one " + Enum + " must be defined as default.");
180     return;
181   }
182 
183   const auto FormattedDefaultName =
184       getFormattedName((*DefaultIt)->getValueAsString("name"));
185 
186   OS << "\n";
187   OS << Enum << " llvm::" << Namespace << "::get" << LanguageName << Enum
188      << "Kind(llvm::StringRef Str) {\n";
189   OS << "  return llvm::StringSwitch<" << Enum << ">(Str)\n";
190 
191   for (const auto &R : Records) {
192     const auto Name = R->getValueAsString("name");
193     if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {
194       OS << "    .Case(\"" << Name << "\"," << Prefix << FormattedDefaultName
195          << ")\n";
196     } else {
197       OS << "    .Case(\"" << Name << "\"," << Prefix << getFormattedName(Name)
198          << ")\n";
199     }
200   }
201   OS << "    .Default(" << Prefix << FormattedDefaultName << ");\n";
202   OS << "}\n";
203 }
204 
205 void GenerateCaseForVersionedClauses(const std::vector<Record *> &Clauses,
206                                      raw_ostream &OS, StringRef DirectiveName,
207                                      StringRef DirectivePrefix,
208                                      StringRef ClausePrefix) {
209   for (const auto &C : Clauses) {
210     const auto MinVersion = C->getValueAsInt("minVersion");
211     const auto MaxVersion = C->getValueAsInt("maxVersion");
212     const auto SpecificClause = C->getValueAsDef("clause");
213     const auto ClauseName = SpecificClause->getValueAsString("name");
214     OS << "        case " << ClausePrefix << getFormattedName(ClauseName)
215        << ":\n";
216     OS << "          return " << MinVersion << " <= Version && " << MaxVersion
217        << " >= Version;\n";
218   }
219 }
220 
221 // Generate the isAllowedClauseForDirective function implementation.
222 void GenerateIsAllowedClause(const std::vector<Record *> &Directives,
223                              raw_ostream &OS, StringRef LanguageName,
224                              StringRef DirectivePrefix, StringRef ClausePrefix,
225                              StringRef CppNamespace) {
226   OS << "\n";
227   OS << "bool llvm::" << CppNamespace << "::isAllowedClauseForDirective("
228      << "Directive D, Clause C, unsigned Version) {\n";
229   OS << "  assert(unsigned(D) <= llvm::" << CppNamespace
230      << "::Directive_enumSize);\n";
231   OS << "  assert(unsigned(C) <= llvm::" << CppNamespace
232      << "::Clause_enumSize);\n";
233 
234   OS << "  switch (D) {\n";
235 
236   for (const auto &D : Directives) {
237 
238     const auto DirectiveName = D->getValueAsString("name");
239 
240     OS << "    case " << DirectivePrefix << getFormattedName(DirectiveName)
241        << ":\n";
242     OS << "      switch (C) {\n";
243 
244     const auto &AllowedClauses = D->getValueAsListOfDefs("allowedClauses");
245     GenerateCaseForVersionedClauses(AllowedClauses, OS, DirectiveName,
246                                     DirectivePrefix, ClausePrefix);
247 
248     const auto &AllowedOnceClauses =
249         D->getValueAsListOfDefs("allowedOnceClauses");
250     GenerateCaseForVersionedClauses(AllowedOnceClauses, OS, DirectiveName,
251                                     DirectivePrefix, ClausePrefix);
252 
253     const auto &RequiredClauses = D->getValueAsListOfDefs("requiredClauses");
254     GenerateCaseForVersionedClauses(RequiredClauses, OS, DirectiveName,
255                                     DirectivePrefix, ClausePrefix);
256 
257     OS << "        default:\n";
258     OS << "          return false;\n";
259     OS << "      }\n"; // End of clauses switch
260     OS << "      break;\n";
261   }
262 
263   OS << "  }\n"; // End of directives switch
264   OS << "  llvm_unreachable(\"Invalid " << LanguageName
265      << " Directive kind\");\n";
266   OS << "}\n"; // End of function isAllowedClauseForDirective
267 }
268 
269 // Generate the implemenation section for the enumeration in the directive
270 // language
271 void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {
272 
273   const auto &DirectiveLanguages =
274       Records.getAllDerivedDefinitions("DirectiveLanguage");
275 
276   if (DirectiveLanguages.size() != 1) {
277     PrintError("A single definition of DirectiveLanguage is needed.");
278     return;
279   }
280 
281   const auto &DirectiveLanguage = DirectiveLanguages[0];
282   StringRef DirectivePrefix =
283       DirectiveLanguage->getValueAsString("directivePrefix");
284   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
285   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
286   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
287 
288   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
289   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
290 
291   // getDirectiveKind(StringRef Str)
292   GenerateGetKind(Directives, OS, "Directive", DirectivePrefix, LanguageName,
293                   CppNamespace, /*ImplicitAsUnknown=*/false);
294 
295   // getDirectiveName(Directive Kind)
296   GenerateGetName(Directives, OS, "Directive", DirectivePrefix, LanguageName,
297                   CppNamespace);
298 
299   // getClauseKind(StringRef Str)
300   GenerateGetKind(Clauses, OS, "Clause", ClausePrefix, LanguageName,
301                   CppNamespace, /*ImplicitAsUnknown=*/true);
302 
303   // getClauseName(Clause Kind)
304   GenerateGetName(Clauses, OS, "Clause", ClausePrefix, LanguageName,
305                   CppNamespace);
306 
307   GenerateIsAllowedClause(Directives, OS, LanguageName, DirectivePrefix,
308                           ClausePrefix, CppNamespace);
309 }
310 
311 } // namespace llvm
312