1 // Copyright (c) 2014-2024 The Khronos Group Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and/or associated documentation files (the "Materials"), 5 // to deal in the Materials without restriction, including without limitation 6 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 // and/or sell copies of the Materials, and to permit persons to whom the 8 // Materials are furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Materials. 12 // 13 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 14 // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 15 // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 16 // 17 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 23 // IN THE MATERIALS. 24 25 // 26 // Print headers for SPIR-V in several languages. 27 // 28 // To change the header information, change the C++-built database in doc.*. 29 // 30 // Then, use "spriv -h <language>" - e.g, spriv.{h,hpp,lua,py,etc}: 31 // replace the auto-generated header, or "spirv -H" to generate all 32 // supported language headers to predefined names in the current directory. 33 // 34 35 #include <string> 36 #include <sstream> 37 #include <fstream> 38 #include <cstring> 39 #include <cstdio> 40 #include <algorithm> 41 #include <memory> 42 #include <cctype> 43 #include <vector> 44 #include <utility> 45 #include <set> 46 47 #include "jsoncpp/dist/json/json.h" 48 49 #include "header.h" 50 #include "jsonToSpirv.h" 51 52 // snprintf and _snprintf are not quite the same, but close enough 53 // for our use. 54 #ifdef _MSC_VER 55 #pragma warning(disable:4996) 56 #define snprintf _snprintf 57 #endif 58 59 // This file converts SPIR-V definitions to an internal JSON 60 // representation, and then generates language specific 61 // data from that single internal form. 62 63 // Initially, the internal form is created from C++ data, 64 // though this can be changed to a JSON master in time. 65 66 namespace { 67 class TPrinter { 68 protected: 69 TPrinter(); 70 71 static const int DocMagicNumber = 0x07230203; 72 static const int DocVersion = 0x00010600; 73 static const int DocRevision = 1; 74 #define DocRevisionString "1" 75 static const std::string DocCopyright; 76 static const std::string DocComment1; 77 static const std::string DocComment2; 78 79 enum enumStyle_t { 80 enumNoMask, 81 enumCount, 82 enumShift, 83 enumMask, 84 enumHex, 85 }; 86 styleStr(enumStyle_t s)87 static std::string styleStr(enumStyle_t s) { 88 return s == enumShift ? "Shift" : 89 s == enumMask ? "Mask" : ""; 90 } 91 92 friend std::ostream& operator<<(std::ostream&, const TPrinter&); 93 94 virtual void printAll(std::ostream&) const; 95 virtual void printComments(std::ostream&) const; printPrologue(std::ostream &) const96 virtual void printPrologue(std::ostream&) const { } 97 virtual void printDefs(std::ostream&) const; printEpilogue(std::ostream &) const98 virtual void printEpilogue(std::ostream&) const { } 99 virtual void printMeta(std::ostream&) const; printTypes(std::ostream &) const100 virtual void printTypes(std::ostream&) const { } printUtility(std::ostream &) const101 virtual void printUtility(std::ostream&) const { }; 102 103 virtual std::string escapeComment(const std::string& s) const; 104 105 // Default printComments() uses these comment strings commentBeg() const106 virtual std::string commentBeg() const { return ""; } commentEnd(bool isLast) const107 virtual std::string commentEnd(bool isLast) const { return ""; } commentBOL() const108 virtual std::string commentBOL() const { return ""; } commentEOL(bool isLast) const109 virtual std::string commentEOL(bool isLast) const { return ""; } 110 111 typedef std::pair<unsigned, std::string> valpair_t; 112 113 // for printing enum values enumBeg(const std::string &,enumStyle_t) const114 virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; } enumEnd(const std::string &,enumStyle_t,bool isLast=false) const115 virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const { 116 return ""; 117 } enumFmt(const std::string &,const valpair_t &,enumStyle_t,bool isLast=false) const118 virtual std::string enumFmt(const std::string&, const valpair_t&, 119 enumStyle_t, bool isLast = false) const { 120 return ""; 121 } maxEnumFmt(const std::string &,const valpair_t &,enumStyle_t) const122 virtual std::string maxEnumFmt(const std::string&, const valpair_t&, 123 enumStyle_t) const { 124 return ""; 125 } 126 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast=false) const127 virtual std::string fmtConstInt(unsigned val, const std::string& name, 128 const char* fmt, bool isLast = false) const { 129 return ""; 130 } 131 132 std::vector<valpair_t> getSortedVals(const Json::Value&) const; 133 indent(int count=1) const134 virtual std::string indent(int count = 1) const { 135 return std::string(count * 4, ' '); // default indent level = 4 136 } 137 fmtNum(const char * fmt,unsigned val)138 static std::string fmtNum(const char* fmt, unsigned val) { 139 char buff[16]; // ample for 8 hex digits + 0x 140 snprintf(buff, sizeof(buff), fmt, val); 141 buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination 142 return buff; 143 } 144 145 static std::string fmtStyleVal(unsigned v, enumStyle_t style); 146 147 // If the enum value name would start with a sigit, prepend the enum name. 148 // E.g, "3D" -> "Dim3D". prependIfDigit(const std::string & ename,const std::string & vname)149 static std::string prependIfDigit(const std::string& ename, const std::string& vname) { 150 return (std::isdigit(vname[0]) ? ename : std::string("")) + vname; 151 } 152 153 void addComment(Json::Value& node, const std::string& str); 154 155 Json::Value spvRoot; // JSON SPIR-V data 156 }; 157 158 // Format value as mask or value fmtStyleVal(unsigned v,enumStyle_t style)159 std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style) 160 { 161 switch (style) { 162 case enumMask: 163 return fmtNum("0x%08x", 1<<v); 164 case enumHex: 165 return fmtNum("0x%08x", v); 166 default: 167 return std::to_string(v); 168 } 169 } 170 171 const std::string TPrinter::DocCopyright = 172 R"(Copyright (c) 2014-2024 The Khronos Group Inc. 173 174 Permission is hereby granted, free of charge, to any person obtaining a copy 175 of this software and/or associated documentation files (the "Materials"), 176 to deal in the Materials without restriction, including without limitation 177 the rights to use, copy, modify, merge, publish, distribute, sublicense, 178 and/or sell copies of the Materials, and to permit persons to whom the 179 Materials are furnished to do so, subject to the following conditions: 180 181 The above copyright notice and this permission notice shall be included in 182 all copies or substantial portions of the Materials. 183 184 MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 185 STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 186 HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 187 188 THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 189 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 190 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 191 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 192 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 193 FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 194 IN THE MATERIALS. 195 )"; 196 197 const std::string TPrinter::DocComment1 = 198 "This header is automatically generated by the same tool that creates\n" 199 "the Binary Section of the SPIR-V specification.\n"; 200 201 const std::string TPrinter::DocComment2 = 202 "Enumeration tokens for SPIR-V, in various styles:\n" 203 " C, C++, C++11, JSON, Lua, Python, C#, D, Beef\n" 204 "\n" 205 "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n" 206 "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n" 207 "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n" 208 "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n" 209 "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n" 210 "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 211 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 212 "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL\n" 213 "- Beef will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 214 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 215 "\n" 216 "Some tokens act like mask values, which can be OR'd together,\n" 217 "while others are mutually exclusive. The mask-like ones have\n" 218 "\"Mask\" in their name, and a parallel enum that has the shift\n" 219 "amount (1 << x) for each corresponding enumerant.\n"; 220 221 // Construct TPrinter()222 TPrinter::TPrinter() 223 { 224 Json::Value& meta = spvRoot["spv"]["meta"]; 225 Json::Value& enums = spvRoot["spv"]["enum"]; 226 227 meta["MagicNumber"] = DocMagicNumber; 228 meta["Version"] = DocVersion; 229 meta["Revision"] = DocRevision; 230 meta["OpCodeMask"] = 0xffff; 231 meta["WordCountShift"] = 16; 232 233 int commentId = 0; 234 addComment(meta["Comment"][commentId++], DocCopyright); 235 addComment(meta["Comment"][commentId++], DocComment1); 236 addComment(meta["Comment"][commentId++], DocComment2); 237 238 for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) { 239 auto& enumSet = spv::OperandClassParams[e]; 240 const bool mask = enumSet.bitmask; 241 const std::string enumName = enumSet.codeName; 242 243 for (auto& enumRow : enumSet) { 244 std::string name = enumRow.name; 245 enums[e - spv::OperandSource]["Values"][name] = enumRow.value; 246 } 247 248 enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value"; 249 enums[e - spv::OperandSource]["Name"] = enumName; 250 } 251 252 // Instructions are in their own different table 253 { 254 auto& entry = enums[spv::OperandOpcode - spv::OperandSource]; 255 for (auto& enumRow : spv::InstructionDesc) { 256 std::string name = enumRow.name; 257 entry["Values"][name] = enumRow.value; 258 } 259 entry["Type"] = "Value"; 260 entry["Name"] = "Op"; 261 } 262 } 263 264 // Create comment addComment(Json::Value & node,const std::string & str)265 void TPrinter::addComment(Json::Value& node, const std::string& str) 266 { 267 std::istringstream cstream(str); 268 std::string cline; 269 270 int line = 0; 271 while (std::getline(cstream, cline)) // fmt each line 272 node[line++] = cline; 273 } 274 275 276 // Return a list of values sorted by enum value. The std::vector 277 // returned by value is okay in c++11 due to move semantics. 278 std::vector<TPrinter::valpair_t> getSortedVals(const Json::Value & p) const279 TPrinter::getSortedVals(const Json::Value& p) const 280 { 281 std::vector<valpair_t> values; 282 283 for (auto e = p.begin(); e != p.end(); ++e) 284 values.push_back(valpair_t(e->asUInt(), e.name())); 285 286 // Use a stable sort because we might have aliases, e.g. 287 // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. 288 std::stable_sort(values.begin(), values.end()); 289 290 return values; 291 } 292 293 // Escape comment characters if needed escapeComment(const std::string & s) const294 std::string TPrinter::escapeComment(const std::string& s) const { return s; } 295 296 // Format comments in language specific way printComments(std::ostream & out) const297 void TPrinter::printComments(std::ostream& out) const 298 { 299 const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); 300 int commentNum = 0; 301 302 for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { 303 out << commentBeg(); 304 305 for (int line = 0; line < int(comment.size()); ++line) 306 out << commentBOL() << escapeComment(comment[line].asString()) << 307 commentEOL((line+1) == comment.size()) << std::endl; 308 309 out << commentEnd(++commentNum == commentCount) << std::endl; 310 } 311 } 312 313 // Format header metadata printMeta(std::ostream & out) const314 void TPrinter::printMeta(std::ostream& out) const 315 { 316 const Json::Value& meta = spvRoot["spv"]["meta"]; 317 318 const auto print = [&](const char* name, const char* fmt, bool isLast) { 319 out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); 320 }; 321 322 print("MagicNumber", "0x%08lx", false); 323 print("Version", "0x%08lx", false); 324 print("Revision", "%d", false); 325 print("OpCodeMask", "0x%04x", false); 326 print("WordCountShift", "%d", true); 327 } 328 329 // Format value definitions in language specific way printDefs(std::ostream & out) const330 void TPrinter::printDefs(std::ostream& out) const 331 { 332 const Json::Value& enums = spvRoot["spv"]["enum"]; 333 334 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 335 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 336 const auto opName = (*opClass)["Name"].asString(); 337 const auto opPrefix = opName == "Op" ? "" : opName; 338 339 for (enumStyle_t style = (isMask ? enumShift : enumCount); 340 style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { 341 342 out << enumBeg(opName, style); 343 344 if (style == enumMask) 345 out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); 346 347 const auto sorted = getSortedVals((*opClass)["Values"]); 348 349 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); 350 351 bool printMax = (style != enumMask && maxEnum.size() > 0); 352 353 for (const auto& v : sorted) 354 out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); 355 356 if (printMax) 357 out << maxEnum; 358 359 auto nextOpClass = opClass; 360 out << enumEnd(opName, style, ++nextOpClass == enums.end()); 361 } 362 } 363 } 364 printAll(std::ostream & out) const365 void TPrinter::printAll(std::ostream& out) const 366 { 367 printComments(out); 368 printPrologue(out); 369 printTypes(out); 370 printMeta(out); 371 printDefs(out); 372 printUtility(out); 373 printEpilogue(out); 374 } 375 376 // Stream entire header to output operator <<(std::ostream & out,const TPrinter & p)377 std::ostream& operator<<(std::ostream& out, const TPrinter &p) 378 { 379 p.printAll(out); 380 return out; 381 } 382 383 // JSON printer. Rather than use the default printer, we supply our own so 384 // we can control the printing order within various containers. 385 class TPrinterJSON final : public TPrinter { 386 private: printPrologue(std::ostream & out) const387 void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } printEpilogue(std::ostream & out) const388 void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } 389 escapeComment(const std::string & s) const390 std::string escapeComment(const std::string& s) const override { 391 std::string newStr; 392 for (auto c : s) { 393 if (c == '"') { 394 newStr += '\\'; 395 newStr += c; 396 } else { 397 newStr += c; 398 } 399 } 400 return newStr; 401 } 402 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const403 std::string fmtConstInt(unsigned val, const std::string& name, 404 const char* fmt, bool isLast) const override { 405 return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); 406 } 407 printMeta(std::ostream & out) const408 void printMeta(std::ostream& out) const override 409 { 410 out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; 411 printComments(out); 412 TPrinter::printMeta(out); 413 out << indent(2) + "},\n"; 414 } 415 commentBeg() const416 std::string commentBeg() const override { return indent(4) + "[\n"; } commentEnd(bool isLast) const417 std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } commentBOL() const418 std::string commentBOL() const override { return indent(5) + '"'; } commentEOL(bool isLast) const419 std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } 420 printComments(std::ostream & out) const421 void printComments(std::ostream& out) const override 422 { 423 out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; 424 TPrinter::printComments(out); 425 out << indent(3) + "],\n"; 426 } 427 printDefs(std::ostream & out) const428 void printDefs(std::ostream& out) const override 429 { 430 out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; 431 TPrinter::printDefs(out); 432 out << indent(2) + "]\n"; 433 } 434 printAll(std::ostream & out) const435 void printAll(std::ostream& out) const override 436 { 437 printPrologue(out); 438 printMeta(out); 439 printDefs(out); 440 printEpilogue(out); 441 } 442 enumBeg(const std::string & s,enumStyle_t style) const443 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 444 if (style == enumMask) 445 return ""; 446 return indent(3) + "{\n" + 447 indent(4) + "\"Name\": \"" + s + "\",\n" + 448 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + 449 indent(4) + "\"Values\":\n" + 450 indent(4) + "{\n"; 451 } 452 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const453 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 454 if (style == enumMask) 455 return ""; 456 return indent(4) + "}\n" + 457 indent(3) + "}" + (isLast ? "" : ",") + "\n"; 458 } 459 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const460 std::string enumFmt(const std::string& s, const valpair_t& v, 461 enumStyle_t style, bool isLast) const override { 462 if (style == enumMask || style == enumNoMask) 463 return ""; 464 return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + 465 (isLast ? "\n" : ",\n"); 466 } 467 }; 468 469 // base for C and C++ 470 class TPrinterCBase : public TPrinter { 471 protected: printPrologue(std::ostream & out) const472 virtual void printPrologue(std::ostream& out) const override { 473 out << "#ifndef spirv_" << headerGuardSuffix() << std::endl 474 << "#define spirv_" << headerGuardSuffix() << std::endl 475 << std::endl; 476 } 477 printMeta(std::ostream & out) const478 void printMeta(std::ostream& out) const override { 479 out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; 480 out << "#define SPV_REVISION " << DocRevision << "\n"; 481 out << "\n"; 482 483 return TPrinter::printMeta(out); 484 } 485 printEpilogue(std::ostream & out) const486 virtual void printEpilogue(std::ostream& out) const override { 487 out << "#endif" << std::endl; 488 } 489 printTypes(std::ostream & out) const490 virtual void printTypes(std::ostream& out) const override { 491 out << "typedef unsigned int " << pre() << "Id;\n\n"; 492 } 493 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const494 virtual std::string fmtConstInt(unsigned val, const std::string& name, 495 const char* fmt, bool isLast) const override 496 { 497 return std::string("static const unsigned int ") + pre() + name + 498 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 499 } 500 pre() const501 virtual std::string pre() const { return ""; } // C name prefix 502 virtual std::string headerGuardSuffix() const = 0; 503 fmtEnumUse(const std::string & opPrefix,const std::string & name) const504 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; } 505 printUtility(std::ostream & out) const506 virtual void printUtility(std::ostream& out) const override 507 { 508 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 509 out << "#ifndef __cplusplus" << std::endl; 510 out << "#include <stdbool.h>" << std::endl; 511 out << "#endif" << std::endl; 512 513 printHasResultType(out); 514 printStringFunctions(out); 515 516 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 517 } 518 printHasResultType(std::ostream & out) const519 void printHasResultType(std::ostream& out) const { 520 const Json::Value& enums = spvRoot["spv"]["enum"]; 521 522 std::set<unsigned> seenValues; 523 524 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 525 const auto opName = (*opClass)["Name"].asString(); 526 if (opName != "Op") { 527 continue; 528 } 529 530 531 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 532 out << " *hasResult = *hasResultType = false;" << std::endl; 533 out << " switch (opcode) {" << std::endl; 534 out << " default: /* unknown opcode */ break;" << std::endl; 535 536 for (auto& inst : spv::InstructionDesc) { 537 538 // Filter out duplicate enum values, which would break the switch statement. 539 // These are probably just extension enums promoted to core. 540 if (seenValues.find(inst.value) != seenValues.end()) { 541 continue; 542 } 543 seenValues.insert(inst.value); 544 545 std::string name = inst.name; 546 out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 547 } 548 549 out << " }" << std::endl; 550 out << "}" << std::endl; 551 } 552 } 553 printStringFunctions(std::ostream & out) const554 void printStringFunctions(std::ostream& out) const { 555 const Json::Value& enums = spvRoot["spv"]["enum"]; 556 557 for (auto it = enums.begin(); it != enums.end(); ++it) { 558 const auto type = (*it)["Type"].asString(); 559 // Skip bitmasks 560 if (type == "Bit") { 561 continue; 562 } 563 const auto name = (*it)["Name"].asString(); 564 const auto sorted = getSortedVals((*it)["Values"]); 565 566 std::set<unsigned> seenValues; 567 std::string fullName = pre() + name; 568 569 out << "inline const char* " << fullName << "ToString(" << fullName << " value) {" << std::endl; 570 out << " switch (value) {" << std::endl; 571 for (const auto& v : sorted) { 572 // Filter out duplicate enum values, which would break the switch statement. 573 // These are probably just extension enums promoted to core. 574 if (seenValues.count(v.first)) { 575 continue; 576 } 577 seenValues.insert(v.first); 578 579 std::string label{name + v.second}; 580 if (name == "Op") { 581 label = v.second; 582 } 583 out << " " << "case " << pre() << label << ": return " << "\"" << v.second << "\";" << std::endl; 584 } 585 out << " default: return \"Unknown\";" << std::endl; 586 out << " }" << std::endl; 587 out << "}" << std::endl << std::endl; 588 } 589 } 590 }; 591 592 // C printer 593 class TPrinterC final : public TPrinterCBase { 594 private: commentBeg() const595 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const596 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const597 std::string commentBOL() const override { return "** "; } 598 enumBeg(const std::string & s,enumStyle_t style) const599 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 600 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 601 } 602 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const603 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 604 return "} " + pre() + s + styleStr(style) + ";\n\n"; 605 } 606 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const607 std::string enumFmt(const std::string& s, const valpair_t& v, 608 enumStyle_t style, bool isLast) const override { 609 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 610 } 611 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const612 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 613 enumStyle_t style) const override { 614 return enumFmt(s, v, style, true); 615 } 616 pre() const617 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const618 std::string headerGuardSuffix() const override { return "H"; } 619 }; 620 621 // C++ printer 622 class TPrinterCPP : public TPrinterCBase { 623 protected: printMaskOperators(std::ostream & out,const std::string & specifiers) const624 void printMaskOperators(std::ostream& out, const std::string& specifiers) const { 625 const Json::Value& enums = spvRoot["spv"]["enum"]; 626 627 out << "// Overload bitwise operators for mask bit combining\n\n"; 628 629 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 630 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 631 const auto opName = (*opClass)["Name"].asString(); 632 633 if (isMask) { 634 const auto typeName = opName + styleStr(enumMask); 635 636 // Overload operator| 637 out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << 638 typeName << "(unsigned(a) | unsigned(b)); }\n"; 639 // Overload operator& 640 out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << 641 typeName << "(unsigned(a) & unsigned(b)); }\n"; 642 // Overload operator^ 643 out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << 644 typeName << "(unsigned(a) ^ unsigned(b)); }\n"; 645 // Overload operator~ 646 out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " << 647 typeName << "(~unsigned(a)); }\n"; 648 } 649 } 650 } 651 private: printPrologue(std::ostream & out) const652 void printPrologue(std::ostream& out) const override { 653 TPrinterCBase::printPrologue(out); 654 out << "namespace spv {\n\n"; 655 } 656 printEpilogue(std::ostream & out) const657 void printEpilogue(std::ostream& out) const override { 658 printMaskOperators(out, "inline"); 659 out << "\n} // end namespace spv\n\n"; 660 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 661 } 662 commentBOL() const663 std::string commentBOL() const override { return "// "; } 664 665 enumBeg(const std::string & s,enumStyle_t style) const666 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 667 return std::string("enum ") + s + styleStr(style) + " {\n"; 668 } 669 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const670 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 671 return "};\n\n"; 672 } 673 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const674 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 675 enumStyle_t style, bool isLast) const override { 676 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 677 } 678 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const679 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 680 enumStyle_t style) const override { 681 return enumFmt(s, v, style, true); 682 } 683 684 // The C++ and C++11 headers define types with the same name. So they 685 // should use the same header guard. headerGuardSuffix() const686 std::string headerGuardSuffix() const override { return "HPP"; } 687 688 std::string operators; 689 }; 690 691 // C++11 printer (uses enum classes) 692 class TPrinterCPP11 final : public TPrinterCPP { 693 private: printEpilogue(std::ostream & out) const694 void printEpilogue(std::ostream& out) const override { 695 printMaskOperators(out, "constexpr"); 696 out << "\n} // end namespace spv\n\n"; 697 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 698 } enumBeg(const std::string & s,enumStyle_t style) const699 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 700 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 701 } 702 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const703 std::string enumFmt(const std::string& s, const valpair_t& v, 704 enumStyle_t style, bool isLast) const override { 705 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 706 } 707 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const708 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 709 enumStyle_t style) const override { 710 return enumFmt(s, v, style, true); 711 } 712 713 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & name) const714 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; } 715 headerGuardSuffix() const716 std::string headerGuardSuffix() const override { return "HPP"; } 717 }; 718 719 // LUA printer 720 class TPrinterLua final : public TPrinter { 721 private: printPrologue(std::ostream & out) const722 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 723 printEpilogue(std::ostream & out) const724 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 725 commentBOL() const726 std::string commentBOL() const override { return "-- "; } 727 enumBeg(const std::string & s,enumStyle_t style) const728 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 729 return indent() + s + styleStr(style) + " = {\n"; 730 } 731 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const732 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 733 return indent() + "},\n\n"; 734 } 735 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const736 std::string enumFmt(const std::string& s, const valpair_t& v, 737 enumStyle_t style, bool isLast) const override { 738 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 739 } 740 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const741 virtual std::string fmtConstInt(unsigned val, const std::string& name, 742 const char* fmt, bool isLast) const override 743 { 744 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 745 } 746 }; 747 748 // Python printer 749 class TPrinterPython final : public TPrinter { 750 private: printPrologue(std::ostream & out) const751 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 752 printEpilogue(std::ostream & out) const753 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 754 commentBOL() const755 std::string commentBOL() const override { return "# "; } 756 enumBeg(const std::string & s,enumStyle_t style) const757 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 758 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 759 } 760 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const761 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 762 return indent() + "},\n\n"; 763 } 764 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const765 std::string enumFmt(const std::string& s, const valpair_t& v, 766 enumStyle_t style, bool isLast) const override { 767 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 768 } 769 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const770 std::string fmtConstInt(unsigned val, const std::string& name, 771 const char* fmt, bool isLast) const override 772 { 773 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 774 } 775 }; 776 777 // C# printer 778 class TPrinterCSharp final : public TPrinter { 779 private: commentBOL() const780 std::string commentBOL() const override { return "// "; } 781 printPrologue(std::ostream & out) const782 void printPrologue(std::ostream& out) const override { 783 out << "namespace Spv\n{\n\n"; 784 out << indent() << "public static class Specification\n"; 785 out << indent() << "{\n"; 786 } 787 printEpilogue(std::ostream & out) const788 void printEpilogue(std::ostream& out) const override { 789 out << indent() << "}\n"; 790 out << "}\n"; 791 } 792 enumBeg(const std::string & s,enumStyle_t style) const793 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 794 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 795 } 796 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const797 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 798 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 799 } 800 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const801 std::string enumFmt(const std::string& s, const valpair_t& v, 802 enumStyle_t style, bool isLast) const override { 803 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 804 } 805 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const806 std::string fmtConstInt(unsigned val, const std::string& name, 807 const char* fmt, bool isLast) const override { 808 return indent(2) + std::string("public const uint ") + name + 809 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 810 } 811 }; 812 813 // D printer 814 class TPrinterD final : public TPrinter { 815 private: commentBeg() const816 std::string commentBeg() const override { return "/+\n"; } commentBOL() const817 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const818 std::string commentEnd(bool isLast) const override { return " +/\n"; } 819 printPrologue(std::ostream & out) const820 void printPrologue(std::ostream& out) const override { 821 out << "module spv;\n\n"; 822 } 823 printEpilogue(std::ostream & out) const824 void printEpilogue(std::ostream& out) const override { 825 } 826 enumBeg(const std::string & s,enumStyle_t style) const827 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 828 return "enum " + s + styleStr(style) + " : uint\n{\n"; 829 } 830 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const831 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 832 return std::string("}\n\n"); 833 } 834 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const835 std::string enumFmt(const std::string& s, const valpair_t& v, 836 enumStyle_t style, bool isLast) const override { 837 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 838 } 839 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const840 std::string fmtConstInt(unsigned val, const std::string& name, 841 const char* fmt, bool isLast) const override { 842 return std::string("enum uint ") + name + 843 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 844 } 845 }; 846 847 // Beef printer 848 class TPrinterBeef final : public TPrinter { 849 private: commentBOL() const850 std::string commentBOL() const override { return "// "; } 851 printPrologue(std::ostream & out) const852 void printPrologue(std::ostream& out) const override { 853 out << "namespace Spv\n{\n"; 854 out << indent() << "using System;\n\n"; 855 out << indent() << "public static class Specification\n"; 856 out << indent() << "{\n"; 857 } 858 printEpilogue(std::ostream & out) const859 void printEpilogue(std::ostream& out) const override { 860 out << indent() << "}\n"; 861 out << "}\n"; 862 } 863 enumBeg(const std::string & s,enumStyle_t style) const864 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 865 return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 866 } 867 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const868 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 869 return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); 870 } 871 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const872 std::string enumFmt(const std::string& s, const valpair_t& v, 873 enumStyle_t style, bool isLast) const override { 874 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 875 } 876 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const877 std::string fmtConstInt(unsigned val, const std::string& name, 878 const char* fmt, bool isLast) const override { 879 return indent(2) + std::string("public const uint32 ") + name + 880 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 881 } 882 }; 883 884 } // namespace 885 886 namespace spv { PrintAllHeaders()887 void PrintAllHeaders() 888 { 889 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 890 std::vector<std::pair<TLanguage, std::string>> langInfo; 891 892 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 893 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 894 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 895 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 896 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 897 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 898 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 899 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 900 langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); 901 902 for (const auto& lang : langInfo) { 903 std::ofstream out(lang.second, std::ios::out); 904 905 if ((out.rdstate() & std::ifstream::failbit)) { 906 std::cerr << "Unable to open file: " << lang.second << std::endl; 907 } else { 908 PrintHeader(lang.first, out); 909 } 910 } 911 } 912 913 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)914 void PrintHeader(TLanguage lang, std::ostream& out) 915 { 916 typedef std::unique_ptr<TPrinter> TPrinterPtr; 917 TPrinterPtr p; 918 919 switch (lang) { 920 case ELangC: p = TPrinterPtr(new TPrinterC); break; 921 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 922 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 923 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 924 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 925 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 926 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 927 case ELangD: p = TPrinterPtr(new TPrinterD); break; 928 case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; 929 case ELangAll: PrintAllHeaders(); break; 930 default: 931 std::cerr << "Unknown language." << std::endl; 932 return; 933 } 934 935 // Print the data in the requested format 936 if (p) 937 out << *p << std::endl; 938 939 // object is auto-deleted 940 } 941 942 } // namespace spv 943