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 { } printHasResultType(std::ostream &) const101 virtual void printHasResultType(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 printHasResultType(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 printHasResultType(std::ostream & out) const506 virtual void printHasResultType(std::ostream& out) const override 507 { 508 const Json::Value& enums = spvRoot["spv"]["enum"]; 509 510 std::set<unsigned> seenValues; 511 512 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 513 const auto opName = (*opClass)["Name"].asString(); 514 if (opName != "Op") { 515 continue; 516 } 517 518 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 519 out << "#ifndef __cplusplus" << std::endl; 520 out << "#include <stdbool.h>" << std::endl; 521 out << "#endif" << std::endl; 522 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 523 out << " *hasResult = *hasResultType = false;" << std::endl; 524 out << " switch (opcode) {" << std::endl; 525 out << " default: /* unknown opcode */ break;" << std::endl; 526 527 for (auto& inst : spv::InstructionDesc) { 528 529 // Filter out duplicate enum values, which would break the switch statement. 530 // These are probably just extension enums promoted to core. 531 if (seenValues.find(inst.value) != seenValues.end()) { 532 continue; 533 } 534 seenValues.insert(inst.value); 535 536 std::string name = inst.name; 537 out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 538 } 539 540 out << " }" << std::endl; 541 out << "}" << std::endl; 542 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 543 } 544 } 545 }; 546 547 // C printer 548 class TPrinterC final : public TPrinterCBase { 549 private: commentBeg() const550 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const551 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const552 std::string commentBOL() const override { return "** "; } 553 enumBeg(const std::string & s,enumStyle_t style) const554 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 555 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 556 } 557 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const558 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 559 return "} " + pre() + s + styleStr(style) + ";\n\n"; 560 } 561 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const562 std::string enumFmt(const std::string& s, const valpair_t& v, 563 enumStyle_t style, bool isLast) const override { 564 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 565 } 566 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const567 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 568 enumStyle_t style) const override { 569 return enumFmt(s, v, style, true); 570 } 571 pre() const572 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const573 std::string headerGuardSuffix() const override { return "H"; } 574 }; 575 576 // C++ printer 577 class TPrinterCPP : public TPrinterCBase { 578 protected: printMaskOperators(std::ostream & out,const std::string & specifiers) const579 void printMaskOperators(std::ostream& out, const std::string& specifiers) const { 580 const Json::Value& enums = spvRoot["spv"]["enum"]; 581 582 out << "// Overload bitwise operators for mask bit combining\n\n"; 583 584 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 585 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 586 const auto opName = (*opClass)["Name"].asString(); 587 588 if (isMask) { 589 const auto typeName = opName + styleStr(enumMask); 590 591 // Overload operator| 592 out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << 593 typeName << "(unsigned(a) | unsigned(b)); }\n"; 594 // Overload operator& 595 out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << 596 typeName << "(unsigned(a) & unsigned(b)); }\n"; 597 // Overload operator^ 598 out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << 599 typeName << "(unsigned(a) ^ unsigned(b)); }\n"; 600 // Overload operator~ 601 out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " << 602 typeName << "(~unsigned(a)); }\n"; 603 } 604 } 605 } 606 private: printPrologue(std::ostream & out) const607 void printPrologue(std::ostream& out) const override { 608 TPrinterCBase::printPrologue(out); 609 out << "namespace spv {\n\n"; 610 } 611 printEpilogue(std::ostream & out) const612 void printEpilogue(std::ostream& out) const override { 613 printMaskOperators(out, "inline"); 614 out << "\n} // end namespace spv\n\n"; 615 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 616 } 617 commentBOL() const618 std::string commentBOL() const override { return "// "; } 619 620 enumBeg(const std::string & s,enumStyle_t style) const621 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 622 return std::string("enum ") + s + styleStr(style) + " {\n"; 623 } 624 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const625 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 626 return "};\n\n"; 627 } 628 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const629 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 630 enumStyle_t style, bool isLast) const override { 631 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 632 } 633 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const634 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 635 enumStyle_t style) const override { 636 return enumFmt(s, v, style, true); 637 } 638 639 // The C++ and C++11 headers define types with the same name. So they 640 // should use the same header guard. headerGuardSuffix() const641 std::string headerGuardSuffix() const override { return "HPP"; } 642 643 std::string operators; 644 }; 645 646 // C++11 printer (uses enum classes) 647 class TPrinterCPP11 final : public TPrinterCPP { 648 private: printEpilogue(std::ostream & out) const649 void printEpilogue(std::ostream& out) const override { 650 printMaskOperators(out, "constexpr"); 651 out << "\n} // end namespace spv\n\n"; 652 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 653 } enumBeg(const std::string & s,enumStyle_t style) const654 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 655 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 656 } 657 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const658 std::string enumFmt(const std::string& s, const valpair_t& v, 659 enumStyle_t style, bool isLast) const override { 660 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 661 } 662 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const663 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 664 enumStyle_t style) const override { 665 return enumFmt(s, v, style, true); 666 } 667 668 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & name) const669 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; } 670 headerGuardSuffix() const671 std::string headerGuardSuffix() const override { return "HPP"; } 672 }; 673 674 // LUA printer 675 class TPrinterLua final : public TPrinter { 676 private: printPrologue(std::ostream & out) const677 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 678 printEpilogue(std::ostream & out) const679 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 680 commentBOL() const681 std::string commentBOL() const override { return "-- "; } 682 enumBeg(const std::string & s,enumStyle_t style) const683 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 684 return indent() + s + styleStr(style) + " = {\n"; 685 } 686 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const687 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 688 return indent() + "},\n\n"; 689 } 690 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const691 std::string enumFmt(const std::string& s, const valpair_t& v, 692 enumStyle_t style, bool isLast) const override { 693 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 694 } 695 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const696 virtual std::string fmtConstInt(unsigned val, const std::string& name, 697 const char* fmt, bool isLast) const override 698 { 699 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 700 } 701 }; 702 703 // Python printer 704 class TPrinterPython final : public TPrinter { 705 private: printPrologue(std::ostream & out) const706 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 707 printEpilogue(std::ostream & out) const708 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 709 commentBOL() const710 std::string commentBOL() const override { return "# "; } 711 enumBeg(const std::string & s,enumStyle_t style) const712 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 713 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 714 } 715 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const716 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 717 return indent() + "},\n\n"; 718 } 719 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const720 std::string enumFmt(const std::string& s, const valpair_t& v, 721 enumStyle_t style, bool isLast) const override { 722 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 723 } 724 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const725 std::string fmtConstInt(unsigned val, const std::string& name, 726 const char* fmt, bool isLast) const override 727 { 728 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 729 } 730 }; 731 732 // C# printer 733 class TPrinterCSharp final : public TPrinter { 734 private: commentBOL() const735 std::string commentBOL() const override { return "// "; } 736 printPrologue(std::ostream & out) const737 void printPrologue(std::ostream& out) const override { 738 out << "namespace Spv\n{\n\n"; 739 out << indent() << "public static class Specification\n"; 740 out << indent() << "{\n"; 741 } 742 printEpilogue(std::ostream & out) const743 void printEpilogue(std::ostream& out) const override { 744 out << indent() << "}\n"; 745 out << "}\n"; 746 } 747 enumBeg(const std::string & s,enumStyle_t style) const748 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 749 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 750 } 751 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const752 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 753 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 754 } 755 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const756 std::string enumFmt(const std::string& s, const valpair_t& v, 757 enumStyle_t style, bool isLast) const override { 758 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 759 } 760 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const761 std::string fmtConstInt(unsigned val, const std::string& name, 762 const char* fmt, bool isLast) const override { 763 return indent(2) + std::string("public const uint ") + name + 764 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 765 } 766 }; 767 768 // D printer 769 class TPrinterD final : public TPrinter { 770 private: commentBeg() const771 std::string commentBeg() const override { return "/+\n"; } commentBOL() const772 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const773 std::string commentEnd(bool isLast) const override { return " +/\n"; } 774 printPrologue(std::ostream & out) const775 void printPrologue(std::ostream& out) const override { 776 out << "module spv;\n\n"; 777 } 778 printEpilogue(std::ostream & out) const779 void printEpilogue(std::ostream& out) const override { 780 } 781 enumBeg(const std::string & s,enumStyle_t style) const782 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 783 return "enum " + s + styleStr(style) + " : uint\n{\n"; 784 } 785 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const786 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 787 return std::string("}\n\n"); 788 } 789 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const790 std::string enumFmt(const std::string& s, const valpair_t& v, 791 enumStyle_t style, bool isLast) const override { 792 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 793 } 794 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const795 std::string fmtConstInt(unsigned val, const std::string& name, 796 const char* fmt, bool isLast) const override { 797 return std::string("enum uint ") + name + 798 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 799 } 800 }; 801 802 // Beef printer 803 class TPrinterBeef final : public TPrinter { 804 private: commentBOL() const805 std::string commentBOL() const override { return "// "; } 806 printPrologue(std::ostream & out) const807 void printPrologue(std::ostream& out) const override { 808 out << "namespace Spv\n{\n"; 809 out << indent() << "using System;\n\n"; 810 out << indent() << "public static class Specification\n"; 811 out << indent() << "{\n"; 812 } 813 printEpilogue(std::ostream & out) const814 void printEpilogue(std::ostream& out) const override { 815 out << indent() << "}\n"; 816 out << "}\n"; 817 } 818 enumBeg(const std::string & s,enumStyle_t style) const819 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 820 return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 821 } 822 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const823 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 824 return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); 825 } 826 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const827 std::string enumFmt(const std::string& s, const valpair_t& v, 828 enumStyle_t style, bool isLast) const override { 829 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 830 } 831 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const832 std::string fmtConstInt(unsigned val, const std::string& name, 833 const char* fmt, bool isLast) const override { 834 return indent(2) + std::string("public const uint32 ") + name + 835 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 836 } 837 }; 838 839 } // namespace 840 841 namespace spv { PrintAllHeaders()842 void PrintAllHeaders() 843 { 844 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 845 std::vector<std::pair<TLanguage, std::string>> langInfo; 846 847 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 848 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 849 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 850 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 851 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 852 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 853 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 854 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 855 langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); 856 857 for (const auto& lang : langInfo) { 858 std::ofstream out(lang.second, std::ios::out); 859 860 if ((out.rdstate() & std::ifstream::failbit)) { 861 std::cerr << "Unable to open file: " << lang.second << std::endl; 862 } else { 863 PrintHeader(lang.first, out); 864 } 865 } 866 } 867 868 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)869 void PrintHeader(TLanguage lang, std::ostream& out) 870 { 871 typedef std::unique_ptr<TPrinter> TPrinterPtr; 872 TPrinterPtr p; 873 874 switch (lang) { 875 case ELangC: p = TPrinterPtr(new TPrinterC); break; 876 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 877 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 878 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 879 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 880 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 881 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 882 case ELangD: p = TPrinterPtr(new TPrinterD); break; 883 case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; 884 case ELangAll: PrintAllHeaders(); break; 885 default: 886 std::cerr << "Unknown language." << std::endl; 887 return; 888 } 889 890 // Print the data in the requested format 891 if (p) 892 out << *p << std::endl; 893 894 // object is auto-deleted 895 } 896 897 } // namespace spv 898