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