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 // Generate enum class
26 void GenerateEnumClass(const std::vector<Record *> &Records, raw_ostream &OS,
27                        StringRef Enum, StringRef Prefix, StringRef CppNamespace,
28                        bool MakeEnumAvailableInNamespace) {
29   OS << "\n";
30   OS << "enum class " << Enum << " {\n";
31   for (const auto &R : Records) {
32     const auto Name = R->getValueAsString("name");
33     std::string N = Name.str();
34     std::replace(N.begin(), N.end(), ' ', '_');
35     OS << "  " << Prefix << N << ",\n";
36   }
37   OS << "};\n";
38   OS << "\n";
39   OS << "static constexpr std::size_t " << Enum
40      << "_enumSize = " << Records.size() << ";\n";
41 
42   // Make the enum values available in the defined namespace. This allows us to
43   // write something like Enum_X if we have a `using namespace <CppNamespace>`.
44   // At the same time we do not loose the strong type guarantees of the enum
45   // class, that is we cannot pass an unsigned as Directive without an explicit
46   // cast.
47   if (MakeEnumAvailableInNamespace) {
48     OS << "\n";
49     for (const auto &R : Records) {
50       const auto Name = R->getValueAsString("name");
51       std::string N = Name.str();
52       std::replace(N.begin(), N.end(), ' ', '_');
53       OS << "constexpr auto " << Prefix << N << " = "
54          << "llvm::" << CppNamespace << "::" << Enum << "::" << Prefix << N
55          << ";\n";
56     }
57   }
58 }
59 
60 // Generate the declaration section for the enumeration in the directive
61 // language
62 void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {
63 
64   const auto &DirectiveLanguages =
65       Records.getAllDerivedDefinitions("DirectiveLanguage");
66 
67   if (DirectiveLanguages.size() != 1) {
68     PrintError("A single definition of DirectiveLanguage is needed.");
69     return;
70   }
71 
72   const auto &DirectiveLanguage = DirectiveLanguages[0];
73   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
74   StringRef DirectivePrefix =
75       DirectiveLanguage->getValueAsString("directivePrefix");
76   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
77   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
78   bool MakeEnumAvailableInNamespace =
79       DirectiveLanguage->getValueAsBit("makeEnumAvailableInNamespace");
80   bool EnableBitmaskEnumInNamespace =
81       DirectiveLanguage->getValueAsBit("enableBitmaskEnumInNamespace");
82 
83   OS << "#ifndef LLVM_" << LanguageName << "_INC\n";
84   OS << "#define LLVM_" << LanguageName << "_INC\n";
85 
86   if (EnableBitmaskEnumInNamespace)
87     OS << "\n#include \"llvm/ADT/BitmaskEnum.h\"\n";
88 
89   OS << "\n";
90   OS << "namespace llvm {\n";
91   OS << "class StringRef;\n";
92 
93   // Open namespaces defined in the directive language
94   llvm::SmallVector<StringRef, 2> Namespaces;
95   llvm::SplitString(CppNamespace, Namespaces, "::");
96   for (auto Ns : Namespaces)
97     OS << "namespace " << Ns << " {\n";
98 
99   if (EnableBitmaskEnumInNamespace)
100     OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";
101 
102   // Emit Directive enumeration
103   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
104   GenerateEnumClass(Directives, OS, "Directive", DirectivePrefix, CppNamespace,
105                     MakeEnumAvailableInNamespace);
106 
107   // Emit Clause enumeration
108   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
109   GenerateEnumClass(Clauses, OS, "Clause", ClausePrefix, CppNamespace,
110                     MakeEnumAvailableInNamespace);
111 
112   // Generic function signatures
113   OS << "\n";
114   OS << "// Enumeration helper functions\n";
115   OS << "Directive get" << LanguageName
116      << "DirectiveKind(llvm::StringRef Str);\n";
117   OS << "\n";
118   OS << "llvm::StringRef get" << LanguageName
119      << "DirectiveName(Directive D);\n";
120   OS << "\n";
121   OS << "Clause get" << LanguageName << "ClauseKind(llvm::StringRef Str);\n";
122   OS << "\n";
123   OS << "llvm::StringRef get" << LanguageName << "ClauseName(Clause C);\n";
124   OS << "\n";
125 
126   // Closing namespaces
127   for (auto Ns : llvm::reverse(Namespaces))
128     OS << "} // namespace " << Ns << "\n";
129 
130   OS << "} // namespace llvm\n";
131 
132   OS << "#endif // LLVM_" << LanguageName << "_INC\n";
133 }
134 
135 // Generate function implementation for get<Enum>Name(StringRef Str)
136 void GenerateGetName(const std::vector<Record *> &Records, raw_ostream &OS,
137                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
138                      StringRef Namespace) {
139   OS << "\n";
140   OS << "llvm::StringRef llvm::" << Namespace << "::get" << LanguageName << Enum
141      << "Name(" << Enum << " Kind) {\n";
142   OS << "  switch (Kind) {\n";
143   for (const auto &R : Records) {
144     const auto Name = R->getValueAsString("name");
145     const auto AlternativeName = R->getValueAsString("alternativeName");
146     std::string N = Name.str();
147     std::replace(N.begin(), N.end(), ' ', '_');
148     OS << "    case " << Prefix << N << ":\n";
149     OS << "      return \"";
150     if (AlternativeName.empty())
151       OS << Name;
152     else
153       OS << AlternativeName;
154     OS << "\";\n";
155   }
156   OS << "  }\n"; // switch
157   OS << "  llvm_unreachable(\"Invalid " << LanguageName << " " << Enum
158      << " kind\");\n";
159   OS << "}\n";
160 }
161 
162 // Generate function implementation for get<Enum>Kind(StringRef Str)
163 void GenerateGetKind(const std::vector<Record *> &Records, raw_ostream &OS,
164                      StringRef Enum, StringRef Prefix, StringRef LanguageName,
165                      StringRef Namespace, bool ImplicitAsUnknown) {
166 
167   auto DefaultIt = std::find_if(Records.begin(), Records.end(), [](Record *R) {
168     return R->getValueAsBit("isDefault") == true;
169   });
170 
171   if (DefaultIt == Records.end()) {
172     PrintError("A least one " + Enum + " must be defined as default.");
173     return;
174   }
175 
176   const auto DefaultName = (*DefaultIt)->getValueAsString("name");
177   std::string DefaultEnum = DefaultName.str();
178   std::replace(DefaultEnum.begin(), DefaultEnum.end(), ' ', '_');
179 
180   OS << "\n";
181   OS << Enum << " llvm::" << Namespace << "::get" << LanguageName << Enum
182      << "Kind(llvm::StringRef Str) {\n";
183   OS << "  return llvm::StringSwitch<" << Enum << ">(Str)\n";
184 
185   for (const auto &R : Records) {
186     const auto Name = R->getValueAsString("name");
187     std::string N = Name.str();
188     std::replace(N.begin(), N.end(), ' ', '_');
189     if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {
190       OS << "    .Case(\"" << Name << "\"," << Prefix << DefaultEnum << ")\n";
191     } else {
192       OS << "    .Case(\"" << Name << "\"," << Prefix << N << ")\n";
193     }
194   }
195   OS << "    .Default(" << Prefix << DefaultEnum << ");\n";
196   OS << "}\n";
197 }
198 
199 // Generate the implemenation section for the enumeration in the directive
200 // language
201 void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {
202 
203   const auto &DirectiveLanguages =
204       Records.getAllDerivedDefinitions("DirectiveLanguage");
205 
206   if (DirectiveLanguages.size() != 1) {
207     PrintError("A single definition of DirectiveLanguage is needed.");
208     return;
209   }
210 
211   const auto &DirectiveLanguage = DirectiveLanguages[0];
212   StringRef DirectivePrefix =
213       DirectiveLanguage->getValueAsString("directivePrefix");
214   StringRef LanguageName = DirectiveLanguage->getValueAsString("name");
215   StringRef ClausePrefix = DirectiveLanguage->getValueAsString("clausePrefix");
216   StringRef CppNamespace = DirectiveLanguage->getValueAsString("cppNamespace");
217 
218   const auto &Directives = Records.getAllDerivedDefinitions("Directive");
219   const auto &Clauses = Records.getAllDerivedDefinitions("Clause");
220 
221   // getDirectiveKind(StringRef Str)
222   GenerateGetKind(Directives, OS, "Directive", DirectivePrefix, LanguageName,
223                   CppNamespace, /*ImplicitAsUnknown=*/false);
224 
225   // getDirectiveName(Directive Kind)
226   GenerateGetName(Directives, OS, "Directive", DirectivePrefix, LanguageName,
227                   CppNamespace);
228 
229   // getClauseKind(StringRef Str)
230   GenerateGetKind(Clauses, OS, "Clause", ClausePrefix, LanguageName,
231                   CppNamespace, /*ImplicitAsUnknown=*/true);
232 
233   // getClauseName(Clause Kind)
234   GenerateGetName(Clauses, OS, "Clause", ClausePrefix, LanguageName,
235                   CppNamespace);
236 }
237 
238 } // namespace llvm
239