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