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