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 & s,const valpair_t & v,enumStyle_t style) const122 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 123 enumStyle_t style) const { 124 return enumFmt(s, v, style, true); 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 // Add aliases 247 for (auto& alias : enumRow.aliases) { 248 enums[e - spv::OperandSource]["Values"][alias] = enumRow.value; 249 } 250 } 251 252 enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value"; 253 enums[e - spv::OperandSource]["Name"] = enumName; 254 } 255 256 // Instructions are in their own different table 257 { 258 auto& entry = enums[spv::OperandOpcode - spv::OperandSource]; 259 for (auto& enumRow : spv::InstructionDesc) { 260 std::string name = enumRow.name; 261 entry["Values"][name] = enumRow.value; 262 // Add aliases 263 for (auto& alias : enumRow.aliases) { 264 entry["Values"][alias] = enumRow.value; 265 } 266 } 267 entry["Type"] = "Value"; 268 entry["Name"] = "Op"; 269 } 270 } 271 272 // Create comment addComment(Json::Value & node,const std::string & str)273 void TPrinter::addComment(Json::Value& node, const std::string& str) 274 { 275 std::istringstream cstream(str); 276 std::string cline; 277 278 int line = 0; 279 while (std::getline(cstream, cline)) // fmt each line 280 node[line++] = cline; 281 } 282 283 284 // Return a list of values sorted by enum value. The std::vector 285 // returned by value is okay in c++11 due to move semantics. 286 std::vector<TPrinter::valpair_t> getSortedVals(const Json::Value & p) const287 TPrinter::getSortedVals(const Json::Value& p) const 288 { 289 std::vector<valpair_t> values; 290 291 for (auto e = p.begin(); e != p.end(); ++e) 292 values.push_back(valpair_t(e->asUInt(), e.name())); 293 294 // Use a stable sort because we might have aliases, e.g. 295 // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. 296 std::stable_sort(values.begin(), values.end()); 297 298 return values; 299 } 300 301 // Escape comment characters if needed escapeComment(const std::string & s) const302 std::string TPrinter::escapeComment(const std::string& s) const { return s; } 303 304 // Format comments in language specific way printComments(std::ostream & out) const305 void TPrinter::printComments(std::ostream& out) const 306 { 307 const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); 308 int commentNum = 0; 309 310 for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { 311 out << commentBeg(); 312 313 for (int line = 0; line < int(comment.size()); ++line) 314 out << commentBOL() << escapeComment(comment[line].asString()) << 315 commentEOL((line+1) == comment.size()) << std::endl; 316 317 out << commentEnd(++commentNum == commentCount) << std::endl; 318 } 319 } 320 321 // Format header metadata printMeta(std::ostream & out) const322 void TPrinter::printMeta(std::ostream& out) const 323 { 324 const Json::Value& meta = spvRoot["spv"]["meta"]; 325 326 const auto print = [&](const char* name, const char* fmt, bool isLast) { 327 out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); 328 }; 329 330 print("MagicNumber", "0x%08lx", false); 331 print("Version", "0x%08lx", false); 332 print("Revision", "%d", false); 333 print("OpCodeMask", "0x%04x", false); 334 print("WordCountShift", "%d", true); 335 } 336 337 // Format value definitions in language specific way printDefs(std::ostream & out) const338 void TPrinter::printDefs(std::ostream& out) const 339 { 340 const Json::Value& enums = spvRoot["spv"]["enum"]; 341 342 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 343 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 344 const auto opName = (*opClass)["Name"].asString(); 345 const auto opPrefix = opName == "Op" ? "" : opName; 346 347 for (enumStyle_t style = (isMask ? enumShift : enumCount); 348 style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { 349 350 out << enumBeg(opName, style); 351 352 if (style == enumMask) 353 out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); 354 355 const auto sorted = getSortedVals((*opClass)["Values"]); 356 357 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); 358 359 bool printMax = (style != enumMask && maxEnum.size() > 0); 360 361 for (const auto& v : sorted) 362 out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); 363 364 if (printMax) 365 out << maxEnum; 366 367 auto nextOpClass = opClass; 368 out << enumEnd(opName, style, ++nextOpClass == enums.end()); 369 } 370 } 371 } 372 printAll(std::ostream & out) const373 void TPrinter::printAll(std::ostream& out) const 374 { 375 printComments(out); 376 printPrologue(out); 377 printTypes(out); 378 printMeta(out); 379 printDefs(out); 380 printUtility(out); 381 printEpilogue(out); 382 } 383 384 // Stream entire header to output operator <<(std::ostream & out,const TPrinter & p)385 std::ostream& operator<<(std::ostream& out, const TPrinter &p) 386 { 387 p.printAll(out); 388 return out; 389 } 390 391 // JSON printer. Rather than use the default printer, we supply our own so 392 // we can control the printing order within various containers. 393 class TPrinterJSON final : public TPrinter { 394 private: printPrologue(std::ostream & out) const395 void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } printEpilogue(std::ostream & out) const396 void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } 397 escapeComment(const std::string & s) const398 std::string escapeComment(const std::string& s) const override { 399 std::string newStr; 400 for (auto c : s) { 401 if (c == '"') { 402 newStr += '\\'; 403 newStr += c; 404 } else { 405 newStr += c; 406 } 407 } 408 return newStr; 409 } 410 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const411 std::string fmtConstInt(unsigned val, const std::string& name, 412 const char* fmt, bool isLast) const override { 413 return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); 414 } 415 printMeta(std::ostream & out) const416 void printMeta(std::ostream& out) const override 417 { 418 out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; 419 printComments(out); 420 TPrinter::printMeta(out); 421 out << indent(2) + "},\n"; 422 } 423 commentBeg() const424 std::string commentBeg() const override { return indent(4) + "[\n"; } commentEnd(bool isLast) const425 std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } commentBOL() const426 std::string commentBOL() const override { return indent(5) + '"'; } commentEOL(bool isLast) const427 std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } 428 printComments(std::ostream & out) const429 void printComments(std::ostream& out) const override 430 { 431 out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; 432 TPrinter::printComments(out); 433 out << indent(3) + "],\n"; 434 } 435 printDefs(std::ostream & out) const436 void printDefs(std::ostream& out) const override 437 { 438 out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; 439 TPrinter::printDefs(out); 440 out << indent(2) + "]\n"; 441 } 442 printAll(std::ostream & out) const443 void printAll(std::ostream& out) const override 444 { 445 printPrologue(out); 446 printMeta(out); 447 printDefs(out); 448 printEpilogue(out); 449 } 450 enumBeg(const std::string & s,enumStyle_t style) const451 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 452 if (style == enumMask) 453 return ""; 454 return indent(3) + "{\n" + 455 indent(4) + "\"Name\": \"" + s + "\",\n" + 456 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + 457 indent(4) + "\"Values\":\n" + 458 indent(4) + "{\n"; 459 } 460 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const461 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 462 if (style == enumMask) 463 return ""; 464 return indent(4) + "}\n" + 465 indent(3) + "}" + (isLast ? "" : ",") + "\n"; 466 } 467 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const468 std::string enumFmt(const std::string& s, const valpair_t& v, 469 enumStyle_t style, bool isLast) const override { 470 if (style == enumMask || style == enumNoMask) 471 return ""; 472 return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + 473 (isLast ? "\n" : ",\n"); 474 } maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const475 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 476 enumStyle_t style) const override { 477 return ""; 478 } 479 }; 480 481 // base for C and C++ 482 class TPrinterCBase : public TPrinter { 483 protected: printPrologue(std::ostream & out) const484 virtual void printPrologue(std::ostream& out) const override { 485 out << "#ifndef spirv_" << headerGuardSuffix() << std::endl 486 << "#define spirv_" << headerGuardSuffix() << std::endl 487 << std::endl; 488 } 489 printMeta(std::ostream & out) const490 void printMeta(std::ostream& out) const override { 491 out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; 492 out << "#define SPV_REVISION " << DocRevision << "\n"; 493 out << "\n"; 494 495 return TPrinter::printMeta(out); 496 } 497 printEpilogue(std::ostream & out) const498 virtual void printEpilogue(std::ostream& out) const override { 499 out << "#endif" << std::endl; 500 } 501 printTypes(std::ostream & out) const502 virtual void printTypes(std::ostream& out) const override { 503 out << "typedef unsigned int " << pre() << "Id;\n\n"; 504 } 505 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const506 virtual std::string fmtConstInt(unsigned val, const std::string& name, 507 const char* fmt, bool isLast) const override 508 { 509 return std::string("static const unsigned int ") + pre() + name + 510 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 511 } 512 pre() const513 virtual std::string pre() const { return ""; } // C name prefix 514 virtual std::string headerGuardSuffix() const = 0; 515 fmtEnumUse(const std::string & opPrefix,const std::string & opEnum,const std::string & name) const516 virtual std::string fmtEnumUse(const std::string &opPrefix, const std::string &opEnum, const std::string &name) const { return pre() + opPrefix + name; } 517 printUtility(std::ostream & out) const518 void printUtility(std::ostream& out) const override 519 { 520 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 521 out << "#ifndef __cplusplus" << std::endl; 522 out << "#include <stdbool.h>" << std::endl; 523 out << "#endif" << std::endl; 524 525 printHasResultType(out); 526 printStringFunctions(out); 527 528 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 529 } 530 printHasResultType(std::ostream & out) const531 void printHasResultType(std::ostream& out) const { 532 const Json::Value& enums = spvRoot["spv"]["enum"]; 533 534 std::set<unsigned> seenValues; 535 536 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 537 const auto opName = (*opClass)["Name"].asString(); 538 if (opName != "Op") { 539 continue; 540 } 541 542 543 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 544 out << " *hasResult = *hasResultType = false;" << std::endl; 545 out << " switch (opcode) {" << std::endl; 546 out << " default: /* unknown opcode */ break;" << std::endl; 547 548 for (auto& inst : spv::InstructionDesc) { 549 550 // Filter out duplicate enum values, which would break the switch statement. 551 // These are probably just extension enums promoted to core. 552 if (seenValues.find(inst.value) != seenValues.end()) { 553 continue; 554 } 555 seenValues.insert(inst.value); 556 557 std::string name = inst.name; 558 out << " case " << fmtEnumUse("", "Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 559 } 560 561 out << " }" << std::endl; 562 out << "}" << std::endl; 563 } 564 } 565 printStringFunctions(std::ostream & out) const566 void printStringFunctions(std::ostream& out) const { 567 const Json::Value& enums = spvRoot["spv"]["enum"]; 568 569 for (auto it = enums.begin(); it != enums.end(); ++it) { 570 const auto type = (*it)["Type"].asString(); 571 // Skip bitmasks 572 if (type == "Bit") { 573 continue; 574 } 575 const auto name = (*it)["Name"].asString(); 576 const auto sorted = getSortedVals((*it)["Values"]); 577 578 std::set<unsigned> seenValues; 579 std::string fullName = pre() + name; 580 581 out << "inline const char* " << fullName << "ToString(" << fullName << " value) {" << std::endl; 582 out << " switch (value) {" << std::endl; 583 for (const auto& v : sorted) { 584 // Filter out duplicate enum values, which would break the switch statement. 585 // These are probably just extension enums promoted to core. 586 if (seenValues.count(v.first)) { 587 continue; 588 } 589 seenValues.insert(v.first); 590 591 out << " " << "case "; 592 if (name == "Op") { 593 out << fmtEnumUse("", name, v.second); 594 } 595 else 596 out << fmtEnumUse(name, name, v.second); 597 out << ": return " << "\"" << v.second << "\";" << std::endl; 598 } 599 out << " default: return \"Unknown\";" << std::endl; 600 out << " }" << std::endl; 601 out << "}" << std::endl << std::endl; 602 } 603 } 604 }; 605 606 // C printer 607 class TPrinterC final : public TPrinterCBase { 608 private: commentBeg() const609 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const610 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const611 std::string commentBOL() const override { return "** "; } 612 enumBeg(const std::string & s,enumStyle_t style) const613 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 614 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 615 } 616 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const617 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 618 return "} " + pre() + s + styleStr(style) + ";\n\n"; 619 } 620 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const621 std::string enumFmt(const std::string& s, const valpair_t& v, 622 enumStyle_t style, bool isLast) const override { 623 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 624 } 625 pre() const626 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const627 std::string headerGuardSuffix() const override { return "H"; } 628 }; 629 630 // C++ printer 631 class TPrinterCPP : public TPrinterCBase { 632 protected: printMaskOperators(std::ostream & out,const std::string & specifiers) const633 void printMaskOperators(std::ostream& out, const std::string& specifiers) const { 634 const Json::Value& enums = spvRoot["spv"]["enum"]; 635 636 out << "// Overload bitwise operators for mask bit combining\n\n"; 637 638 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 639 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 640 const auto opName = (*opClass)["Name"].asString(); 641 642 if (isMask) { 643 const auto typeName = opName + styleStr(enumMask); 644 645 // Overload operator| 646 out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << 647 typeName << "(unsigned(a) | unsigned(b)); }\n"; 648 // Overload operator& 649 out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << 650 typeName << "(unsigned(a) & unsigned(b)); }\n"; 651 // Overload operator^ 652 out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << 653 typeName << "(unsigned(a) ^ unsigned(b)); }\n"; 654 // Overload operator~ 655 out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " << 656 typeName << "(~unsigned(a)); }\n"; 657 } 658 } 659 } 660 private: printPrologue(std::ostream & out) const661 void printPrologue(std::ostream& out) const override { 662 TPrinterCBase::printPrologue(out); 663 out << "namespace spv {\n\n"; 664 } 665 printEpilogue(std::ostream & out) const666 void printEpilogue(std::ostream& out) const override { 667 printMaskOperators(out, "inline"); 668 out << "\n} // end namespace spv\n\n"; 669 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 670 } 671 commentBOL() const672 std::string commentBOL() const override { return "// "; } 673 674 enumBeg(const std::string & s,enumStyle_t style) const675 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 676 return std::string("enum ") + s + styleStr(style) + " {\n"; 677 } 678 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const679 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 680 return "};\n\n"; 681 } 682 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const683 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 684 enumStyle_t style, bool isLast) const override { 685 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 686 } 687 688 // The C++ and C++11 headers define types with the same name. So they 689 // should use the same header guard. headerGuardSuffix() const690 std::string headerGuardSuffix() const override { return "HPP"; } 691 692 std::string operators; 693 }; 694 695 // C++11 printer (uses enum classes) 696 class TPrinterCPP11 final : public TPrinterCPP { 697 private: printEpilogue(std::ostream & out) const698 void printEpilogue(std::ostream& out) const override { 699 printMaskOperators(out, "constexpr"); 700 out << "\n} // end namespace spv\n\n"; 701 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 702 } enumBeg(const std::string & s,enumStyle_t style) const703 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 704 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 705 } 706 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const707 std::string enumFmt(const std::string& s, const valpair_t& v, 708 enumStyle_t style, bool isLast) const override { 709 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 710 } 711 712 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & opEnum,const std::string & name) const713 std::string fmtEnumUse(const std::string& opPrefix, const std::string& opEnum, const std::string& name) const override { return opEnum + "::" + prependIfDigit(opEnum, name); } 714 headerGuardSuffix() const715 std::string headerGuardSuffix() const override { return "HPP"; } 716 }; 717 718 // LUA printer 719 class TPrinterLua final : public TPrinter { 720 private: printPrologue(std::ostream & out) const721 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 722 printEpilogue(std::ostream & out) const723 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 724 commentBOL() const725 std::string commentBOL() const override { return "-- "; } 726 enumBeg(const std::string & s,enumStyle_t style) const727 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 728 return indent() + s + styleStr(style) + " = {\n"; 729 } 730 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const731 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 732 return indent() + "},\n\n"; 733 } 734 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const735 std::string enumFmt(const std::string& s, const valpair_t& v, 736 enumStyle_t style, bool isLast) const override { 737 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 738 } 739 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const740 virtual std::string fmtConstInt(unsigned val, const std::string& name, 741 const char* fmt, bool isLast) const override 742 { 743 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 744 } 745 }; 746 747 // Python printer 748 class TPrinterPython final : public TPrinter { 749 private: printPrologue(std::ostream & out) const750 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 751 printEpilogue(std::ostream & out) const752 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 753 commentBOL() const754 std::string commentBOL() const override { return "# "; } 755 enumBeg(const std::string & s,enumStyle_t style) const756 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 757 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 758 } 759 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const760 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 761 return indent() + "},\n\n"; 762 } 763 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const764 std::string enumFmt(const std::string& s, const valpair_t& v, 765 enumStyle_t style, bool isLast) const override { 766 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 767 } maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const768 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 769 enumStyle_t style) const override { 770 return ""; 771 } fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const772 std::string fmtConstInt(unsigned val, const std::string& name, 773 const char* fmt, bool isLast) const override 774 { 775 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 776 } 777 }; 778 779 // C# printer 780 class TPrinterCSharp final : public TPrinter { 781 private: commentBOL() const782 std::string commentBOL() const override { return "// "; } 783 printPrologue(std::ostream & out) const784 void printPrologue(std::ostream& out) const override { 785 out << "namespace Spv\n{\n\n"; 786 out << indent() << "public static class Specification\n"; 787 out << indent() << "{\n"; 788 } 789 printEpilogue(std::ostream & out) const790 void printEpilogue(std::ostream& out) const override { 791 out << indent() << "}\n"; 792 out << "}\n"; 793 } 794 enumBeg(const std::string & s,enumStyle_t style) const795 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 796 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 797 } 798 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const799 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 800 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 801 } 802 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const803 std::string enumFmt(const std::string& s, const valpair_t& v, 804 enumStyle_t style, bool isLast) const override { 805 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 806 } 807 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const808 std::string fmtConstInt(unsigned val, const std::string& name, 809 const char* fmt, bool isLast) const override { 810 return indent(2) + std::string("public const uint ") + name + 811 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 812 } 813 }; 814 815 // D printer 816 class TPrinterD final : public TPrinter { 817 private: commentBeg() const818 std::string commentBeg() const override { return "/+\n"; } commentBOL() const819 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const820 std::string commentEnd(bool isLast) const override { return " +/\n"; } 821 printPrologue(std::ostream & out) const822 void printPrologue(std::ostream& out) const override { 823 out << "module spv;\n\n"; 824 } 825 printEpilogue(std::ostream & out) const826 void printEpilogue(std::ostream& out) const override { 827 } 828 enumBeg(const std::string & s,enumStyle_t style) const829 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 830 return "enum " + s + styleStr(style) + " : uint\n{\n"; 831 } 832 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const833 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 834 return std::string("}\n\n"); 835 } 836 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const837 std::string enumFmt(const std::string& s, const valpair_t& v, 838 enumStyle_t style, bool isLast) const override { 839 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 840 } 841 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const842 std::string fmtConstInt(unsigned val, const std::string& name, 843 const char* fmt, bool isLast) const override { 844 return std::string("enum uint ") + name + 845 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 846 } 847 }; 848 849 // Beef printer 850 class TPrinterBeef final : public TPrinter { 851 private: commentBOL() const852 std::string commentBOL() const override { return "// "; } 853 printPrologue(std::ostream & out) const854 void printPrologue(std::ostream& out) const override { 855 out << "namespace Spv\n{\n"; 856 out << indent() << "using System;\n\n"; 857 out << indent() << "public static class Specification\n"; 858 out << indent() << "{\n"; 859 } 860 printEpilogue(std::ostream & out) const861 void printEpilogue(std::ostream& out) const override { 862 out << indent() << "}\n"; 863 out << "}\n"; 864 } 865 enumBeg(const std::string & s,enumStyle_t style) const866 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 867 return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 868 } 869 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const870 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 871 return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); 872 } 873 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const874 std::string enumFmt(const std::string& s, const valpair_t& v, 875 enumStyle_t style, bool isLast) const override { 876 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 877 } 878 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const879 std::string fmtConstInt(unsigned val, const std::string& name, 880 const char* fmt, bool isLast) const override { 881 return indent(2) + std::string("public const uint32 ") + name + 882 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 883 } 884 }; 885 886 } // namespace 887 888 namespace spv { PrintAllHeaders()889 void PrintAllHeaders() 890 { 891 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 892 std::vector<std::pair<TLanguage, std::string>> langInfo; 893 894 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 895 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 896 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 897 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 898 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 899 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 900 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 901 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 902 langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); 903 904 for (const auto& lang : langInfo) { 905 std::ofstream out(lang.second, std::ios::out); 906 907 if ((out.rdstate() & std::ifstream::failbit)) { 908 std::cerr << "Unable to open file: " << lang.second << std::endl; 909 } else { 910 PrintHeader(lang.first, out); 911 } 912 } 913 } 914 915 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)916 void PrintHeader(TLanguage lang, std::ostream& out) 917 { 918 typedef std::unique_ptr<TPrinter> TPrinterPtr; 919 TPrinterPtr p; 920 921 switch (lang) { 922 case ELangC: p = TPrinterPtr(new TPrinterC); break; 923 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 924 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 925 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 926 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 927 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 928 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 929 case ELangD: p = TPrinterPtr(new TPrinterD); break; 930 case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; 931 case ELangAll: PrintAllHeaders(); break; 932 default: 933 std::cerr << "Unknown language." << std::endl; 934 return; 935 } 936 937 // Print the data in the requested format 938 if (p) 939 out << *p << std::endl; 940 941 // object is auto-deleted 942 } 943 944 } // namespace spv 945