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 GenerateTestForAllowedClauses(const std::vector<Record *> &Clauses,
206                                    raw_ostream &OS, StringRef DirectiveName,
207                                    StringRef DirectivePrefix,
208                                    StringRef ClausePrefix) {
209 
210   const auto FormattedDirectiveName = getFormattedName(DirectiveName);
211   for (const auto &C : Clauses) {
212     const auto MinVersion = C->getValueAsInt("minVersion");
213     const auto MaxVersion = C->getValueAsInt("maxVersion");
214     const auto SpecificClause = C->getValueAsDef("clause");
215     const auto ClauseName = SpecificClause->getValueAsString("name");
216 
217     OS << "  if (D == " << DirectivePrefix << FormattedDirectiveName
218        << " && C == " << ClausePrefix << getFormattedName(ClauseName) << " && "
219        << MinVersion << " <= Version && " << MaxVersion << " >= Version)\n";
220     OS << "    return true;\n";
221   }
222 }
223 
224 // Generate the isAllowedClauseForDirective function implementation.
225 void GenerateIsAllowedClause(const std::vector<Record *> &Directives,
226                              raw_ostream &OS, StringRef DirectivePrefix,
227                              StringRef ClausePrefix, StringRef CppNamespace) {
228   OS << "\n";
229   OS << "bool llvm::" << CppNamespace << "::isAllowedClauseForDirective("
230      << "Directive D, Clause C, unsigned Version) {\n";
231   OS << "  assert(unsigned(D) <= llvm::" << CppNamespace
232      << "::Directive_enumSize);\n";
233   OS << "  assert(unsigned(C) <= llvm::" << CppNamespace
234      << "::Clause_enumSize);\n";
235 
236   for (const auto &D : Directives) {
237     const auto DirectiveName = D->getValueAsString("name");
238 
239     const auto &AllowedClauses = D->getValueAsListOfDefs("allowedClauses");
240     GenerateTestForAllowedClauses(AllowedClauses, OS, DirectiveName,
241                                   DirectivePrefix, ClausePrefix);
242 
243     const auto &AllowedOnceClauses =
244         D->getValueAsListOfDefs("allowedOnceClauses");
245     GenerateTestForAllowedClauses(AllowedOnceClauses, OS, DirectiveName,
246                                   DirectivePrefix, ClausePrefix);
247 
248     const auto &RequiredClauses = D->getValueAsListOfDefs("requiredClauses");
249     GenerateTestForAllowedClauses(RequiredClauses, OS, DirectiveName,
250                                   DirectivePrefix, ClausePrefix);
251   }
252   OS << "  return false;\n";
253   OS << "}\n";
254 }
255 
256 // Generate the implemenation section for the enumeration in the directive
257 // language
258 void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {
259 
260   const auto &DirectiveLanguages =
261       Records.getAllDerivedDefinitions("DirectiveLanguage");
262 
263   if (DirectiveLanguages.size() != 1) {
264     PrintError("A single definition of DirectiveLanguage is needed.");
265     return;
266   }
267 
268   const auto &DirectiveLanguage = DirectiveLanguages[0];
269   StringRef DirectivePrefix =
270       DirectiveLanguage->getValueAsString("directivePrefix");
271   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
272   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
273   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
274 
275   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
276   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
277 
278   // getDirectiveKind(StringRef Str)
279   GenerateGetKind(Directives, OS, "Directive", DirectivePrefix, LanguageName,
280                   CppNamespace, /*ImplicitAsUnknown=*/false);
281 
282   // getDirectiveName(Directive Kind)
283   GenerateGetName(Directives, OS, "Directive", DirectivePrefix, LanguageName,
284                   CppNamespace);
285 
286   // getClauseKind(StringRef Str)
287   GenerateGetKind(Clauses, OS, "Clause", ClausePrefix, LanguageName,
288                   CppNamespace, /*ImplicitAsUnknown=*/true);
289 
290   // getClauseName(Clause Kind)
291   GenerateGetName(Clauses, OS, "Clause", ClausePrefix, LanguageName,
292                   CppNamespace);
293 
294   GenerateIsAllowedClause(Directives, OS, DirectivePrefix, ClausePrefix,
295                           CppNamespace);
296 }
297 
298 } // namespace llvm
299