1 // Copyright (c) 2014-2020 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 "Copyright (c) 2014-2020 The Khronos Group Inc.\n" 173 "\n" 174 "Permission is hereby granted, free of charge, to any person obtaining a copy\n" 175 "of this software and/or associated documentation files (the \"Materials\"),\n" 176 "to deal in the Materials without restriction, including without limitation\n" 177 "the rights to use, copy, modify, merge, publish, distribute, sublicense,\n" 178 "and/or sell copies of the Materials, and to permit persons to whom the\n" 179 "Materials are furnished to do so, subject to the following conditions:\n" 180 "\n" 181 "The above copyright notice and this permission notice shall be included in\n" 182 "all copies or substantial portions of the Materials.\n" 183 "\n" 184 "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n" 185 "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n" 186 "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n" 187 "\n" 188 "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n" 189 "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" 190 "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n" 191 "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" 192 "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n" 193 "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n" 194 "IN THE MATERIALS.\n"; 195 196 const std::string TPrinter::DocComment1 = 197 "This header is automatically generated by the same tool that creates\n" 198 "the Binary Section of the SPIR-V specification.\n"; 199 200 const std::string TPrinter::DocComment2 = 201 "Enumeration tokens for SPIR-V, in various styles:\n" 202 " C, C++, C++11, JSON, Lua, Python, C#, D, Beef\n" 203 "\n" 204 "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n" 205 "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n" 206 "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n" 207 "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n" 208 "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n" 209 "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 210 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 211 "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL\n" 212 "- Beef will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 213 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 214 "\n" 215 "Some tokens act like mask values, which can be OR'd together,\n" 216 "while others are mutually exclusive. The mask-like ones have\n" 217 "\"Mask\" in their name, and a parallel enum that has the shift\n" 218 "amount (1 << x) for each corresponding enumerant.\n"; 219 220 // Construct TPrinter()221 TPrinter::TPrinter() 222 { 223 Json::Value& meta = spvRoot["spv"]["meta"]; 224 Json::Value& enums = spvRoot["spv"]["enum"]; 225 226 meta["MagicNumber"] = DocMagicNumber; 227 meta["Version"] = DocVersion; 228 meta["Revision"] = DocRevision; 229 meta["OpCodeMask"] = 0xffff; 230 meta["WordCountShift"] = 16; 231 232 int commentId = 0; 233 addComment(meta["Comment"][commentId++], DocCopyright); 234 addComment(meta["Comment"][commentId++], DocComment1); 235 addComment(meta["Comment"][commentId++], DocComment2); 236 237 for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) { 238 auto& enumSet = spv::OperandClassParams[e]; 239 const bool mask = enumSet.bitmask; 240 const std::string enumName = enumSet.codeName; 241 242 for (auto& enumRow : enumSet) { 243 std::string name = enumRow.name; 244 enums[e - spv::OperandSource]["Values"][name] = enumRow.value; 245 } 246 247 enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value"; 248 enums[e - spv::OperandSource]["Name"] = enumName; 249 } 250 251 // Instructions are in their own different table 252 { 253 auto& entry = enums[spv::OperandOpcode - spv::OperandSource]; 254 for (auto& enumRow : spv::InstructionDesc) { 255 std::string name = enumRow.name; 256 entry["Values"][name] = enumRow.value; 257 } 258 entry["Type"] = "Value"; 259 entry["Name"] = "Op"; 260 } 261 } 262 263 // Create comment addComment(Json::Value & node,const std::string & str)264 void TPrinter::addComment(Json::Value& node, const std::string& str) 265 { 266 std::istringstream cstream(str); 267 std::string cline; 268 269 int line = 0; 270 while (std::getline(cstream, cline)) // fmt each line 271 node[line++] = cline; 272 } 273 274 275 // Return a list of values sorted by enum value. The std::vector 276 // returned by value is okay in c++11 due to move semantics. 277 std::vector<TPrinter::valpair_t> getSortedVals(const Json::Value & p) const278 TPrinter::getSortedVals(const Json::Value& p) const 279 { 280 std::vector<valpair_t> values; 281 282 for (auto e = p.begin(); e != p.end(); ++e) 283 values.push_back(valpair_t(e->asUInt(), e.name())); 284 285 // Use a stable sort because we might have aliases, e.g. 286 // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. 287 std::stable_sort(values.begin(), values.end()); 288 289 return values; 290 } 291 292 // Escape comment characters if needed escapeComment(const std::string & s) const293 std::string TPrinter::escapeComment(const std::string& s) const { return s; } 294 295 // Format comments in language specific way printComments(std::ostream & out) const296 void TPrinter::printComments(std::ostream& out) const 297 { 298 const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); 299 int commentNum = 0; 300 301 for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { 302 out << commentBeg(); 303 304 for (int line = 0; line < int(comment.size()); ++line) 305 out << commentBOL() << escapeComment(comment[line].asString()) << 306 commentEOL((line+1) == comment.size()) << std::endl; 307 308 out << commentEnd(++commentNum == commentCount) << std::endl; 309 } 310 } 311 312 // Format header metadata printMeta(std::ostream & out) const313 void TPrinter::printMeta(std::ostream& out) const 314 { 315 const Json::Value& meta = spvRoot["spv"]["meta"]; 316 317 const auto print = [&](const char* name, const char* fmt, bool isLast) { 318 out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); 319 }; 320 321 print("MagicNumber", "0x%08lx", false); 322 print("Version", "0x%08lx", false); 323 print("Revision", "%d", false); 324 print("OpCodeMask", "0x%04x", false); 325 print("WordCountShift", "%d", true); 326 } 327 328 // Format value definitions in language specific way printDefs(std::ostream & out) const329 void TPrinter::printDefs(std::ostream& out) const 330 { 331 const Json::Value& enums = spvRoot["spv"]["enum"]; 332 333 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 334 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 335 const auto opName = (*opClass)["Name"].asString(); 336 const auto opPrefix = opName == "Op" ? "" : opName; 337 338 for (enumStyle_t style = (isMask ? enumShift : enumCount); 339 style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { 340 341 out << enumBeg(opName, style); 342 343 if (style == enumMask) 344 out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); 345 346 const auto sorted = getSortedVals((*opClass)["Values"]); 347 348 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); 349 350 bool printMax = (style != enumMask && maxEnum.size() > 0); 351 352 for (const auto& v : sorted) 353 out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); 354 355 if (printMax) 356 out << maxEnum; 357 358 auto nextOpClass = opClass; 359 out << enumEnd(opName, style, ++nextOpClass == enums.end()); 360 } 361 } 362 } 363 printAll(std::ostream & out) const364 void TPrinter::printAll(std::ostream& out) const 365 { 366 printComments(out); 367 printPrologue(out); 368 printTypes(out); 369 printMeta(out); 370 printDefs(out); 371 printHasResultType(out); 372 printEpilogue(out); 373 } 374 375 // Stream entire header to output operator <<(std::ostream & out,const TPrinter & p)376 std::ostream& operator<<(std::ostream& out, const TPrinter &p) 377 { 378 p.printAll(out); 379 return out; 380 } 381 382 // JSON printer. Rather than use the default printer, we supply our own so 383 // we can control the printing order within various containers. 384 class TPrinterJSON final : public TPrinter { 385 private: printPrologue(std::ostream & out) const386 void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } printEpilogue(std::ostream & out) const387 void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } 388 escapeComment(const std::string & s) const389 std::string escapeComment(const std::string& s) const override { 390 std::string newStr; 391 for (auto c : s) { 392 if (c == '"') { 393 newStr += '\\'; 394 newStr += c; 395 } else { 396 newStr += c; 397 } 398 } 399 return newStr; 400 } 401 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const402 std::string fmtConstInt(unsigned val, const std::string& name, 403 const char* fmt, bool isLast) const override { 404 return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); 405 } 406 printMeta(std::ostream & out) const407 void printMeta(std::ostream& out) const override 408 { 409 out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; 410 printComments(out); 411 TPrinter::printMeta(out); 412 out << indent(2) + "},\n"; 413 } 414 commentBeg() const415 std::string commentBeg() const override { return indent(4) + "[\n"; } commentEnd(bool isLast) const416 std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } commentBOL() const417 std::string commentBOL() const override { return indent(5) + '"'; } commentEOL(bool isLast) const418 std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } 419 printComments(std::ostream & out) const420 void printComments(std::ostream& out) const override 421 { 422 out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; 423 TPrinter::printComments(out); 424 out << indent(3) + "],\n"; 425 } 426 printDefs(std::ostream & out) const427 void printDefs(std::ostream& out) const override 428 { 429 out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; 430 TPrinter::printDefs(out); 431 out << indent(2) + "]\n"; 432 } 433 printAll(std::ostream & out) const434 void printAll(std::ostream& out) const override 435 { 436 printPrologue(out); 437 printMeta(out); 438 printDefs(out); 439 printEpilogue(out); 440 } 441 enumBeg(const std::string & s,enumStyle_t style) const442 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 443 if (style == enumMask) 444 return ""; 445 return indent(3) + "{\n" + 446 indent(4) + "\"Name\": \"" + s + "\",\n" + 447 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + 448 indent(4) + "\"Values\":\n" + 449 indent(4) + "{\n"; 450 } 451 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const452 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 453 if (style == enumMask) 454 return ""; 455 return indent(4) + "}\n" + 456 indent(3) + "}" + (isLast ? "" : ",") + "\n"; 457 } 458 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const459 std::string enumFmt(const std::string& s, const valpair_t& v, 460 enumStyle_t style, bool isLast) const override { 461 if (style == enumMask || style == enumNoMask) 462 return ""; 463 return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + 464 (isLast ? "\n" : ",\n"); 465 } 466 }; 467 468 // base for C and C++ 469 class TPrinterCBase : public TPrinter { 470 protected: printPrologue(std::ostream & out) const471 virtual void printPrologue(std::ostream& out) const override { 472 out << "#ifndef spirv_" << headerGuardSuffix() << std::endl 473 << "#define spirv_" << headerGuardSuffix() << std::endl 474 << std::endl; 475 } 476 printMeta(std::ostream & out) const477 void printMeta(std::ostream& out) const override { 478 out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; 479 out << "#define SPV_REVISION " << DocRevision << "\n"; 480 out << "\n"; 481 482 return TPrinter::printMeta(out); 483 } 484 printEpilogue(std::ostream & out) const485 virtual void printEpilogue(std::ostream& out) const override { 486 out << "#endif" << std::endl; 487 } 488 printTypes(std::ostream & out) const489 virtual void printTypes(std::ostream& out) const override { 490 out << "typedef unsigned int " << pre() << "Id;\n\n"; 491 } 492 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const493 virtual std::string fmtConstInt(unsigned val, const std::string& name, 494 const char* fmt, bool isLast) const override 495 { 496 return std::string("static const unsigned int ") + pre() + name + 497 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 498 } 499 pre() const500 virtual std::string pre() const { return ""; } // C name prefix 501 virtual std::string headerGuardSuffix() const = 0; 502 fmtEnumUse(const std::string & opPrefix,const std::string & name) const503 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; } 504 printHasResultType(std::ostream & out) const505 virtual void printHasResultType(std::ostream& out) const override 506 { 507 const Json::Value& enums = spvRoot["spv"]["enum"]; 508 509 std::set<unsigned> seenValues; 510 511 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 512 const auto opName = (*opClass)["Name"].asString(); 513 if (opName != "Op") { 514 continue; 515 } 516 517 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 518 out << "#ifndef __cplusplus" << std::endl; 519 out << "#include <stdbool.h>" << std::endl; 520 out << "#endif" << std::endl; 521 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 522 out << " *hasResult = *hasResultType = false;" << std::endl; 523 out << " switch (opcode) {" << std::endl; 524 out << " default: /* unknown opcode */ break;" << std::endl; 525 526 for (auto& inst : spv::InstructionDesc) { 527 528 // Filter out duplicate enum values, which would break the switch statement. 529 // These are probably just extension enums promoted to core. 530 if (seenValues.find(inst.value) != seenValues.end()) { 531 continue; 532 } 533 seenValues.insert(inst.value); 534 535 std::string name = inst.name; 536 out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 537 } 538 539 out << " }" << std::endl; 540 out << "}" << std::endl; 541 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 542 } 543 } 544 }; 545 546 // C printer 547 class TPrinterC final : public TPrinterCBase { 548 private: commentBeg() const549 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const550 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const551 std::string commentBOL() const override { return "** "; } 552 enumBeg(const std::string & s,enumStyle_t style) const553 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 554 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 555 } 556 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const557 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 558 return "} " + pre() + s + styleStr(style) + ";\n\n"; 559 } 560 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const561 std::string enumFmt(const std::string& s, const valpair_t& v, 562 enumStyle_t style, bool isLast) const override { 563 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 564 } 565 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const566 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 567 enumStyle_t style) const override { 568 return enumFmt(s, v, style, true); 569 } 570 pre() const571 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const572 std::string headerGuardSuffix() const override { return "H"; } 573 }; 574 575 // C++ printer 576 class TPrinterCPP : public TPrinterCBase { 577 protected: printMaskOperators(std::ostream & out,const std::string & specifiers) const578 void printMaskOperators(std::ostream& out, const std::string& specifiers) const { 579 const Json::Value& enums = spvRoot["spv"]["enum"]; 580 581 out << "// Overload bitwise operators for mask bit combining\n\n"; 582 583 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 584 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 585 const auto opName = (*opClass)["Name"].asString(); 586 587 if (isMask) { 588 const auto typeName = opName + styleStr(enumMask); 589 590 // Overload operator| 591 out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << 592 typeName << "(unsigned(a) | unsigned(b)); }\n"; 593 // Overload operator& 594 out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << 595 typeName << "(unsigned(a) & unsigned(b)); }\n"; 596 // Overload operator^ 597 out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << 598 typeName << "(unsigned(a) ^ unsigned(b)); }\n"; 599 // Overload operator~ 600 out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " << 601 typeName << "(~unsigned(a)); }\n"; 602 } 603 } 604 } 605 private: printPrologue(std::ostream & out) const606 void printPrologue(std::ostream& out) const override { 607 TPrinterCBase::printPrologue(out); 608 out << "namespace spv {\n\n"; 609 } 610 printEpilogue(std::ostream & out) const611 void printEpilogue(std::ostream& out) const override { 612 printMaskOperators(out, "inline"); 613 out << "\n} // end namespace spv\n\n"; 614 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 615 } 616 commentBOL() const617 std::string commentBOL() const override { return "// "; } 618 619 enumBeg(const std::string & s,enumStyle_t style) const620 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 621 return std::string("enum ") + s + styleStr(style) + " {\n"; 622 } 623 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const624 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 625 return "};\n\n"; 626 } 627 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const628 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 629 enumStyle_t style, bool isLast) const override { 630 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 631 } 632 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const633 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 634 enumStyle_t style) const override { 635 return enumFmt(s, v, style, true); 636 } 637 638 // The C++ and C++11 headers define types with the same name. So they 639 // should use the same header guard. headerGuardSuffix() const640 std::string headerGuardSuffix() const override { return "HPP"; } 641 642 std::string operators; 643 }; 644 645 // C++11 printer (uses enum classes) 646 class TPrinterCPP11 final : public TPrinterCPP { 647 private: printEpilogue(std::ostream & out) const648 void printEpilogue(std::ostream& out) const override { 649 printMaskOperators(out, "constexpr"); 650 out << "\n} // end namespace spv\n\n"; 651 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 652 } enumBeg(const std::string & s,enumStyle_t style) const653 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 654 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 655 } 656 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const657 std::string enumFmt(const std::string& s, const valpair_t& v, 658 enumStyle_t style, bool isLast) const override { 659 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 660 } 661 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const662 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 663 enumStyle_t style) const override { 664 return enumFmt(s, v, style, true); 665 } 666 667 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & name) const668 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; } 669 headerGuardSuffix() const670 std::string headerGuardSuffix() const override { return "HPP"; } 671 }; 672 673 // LUA printer 674 class TPrinterLua final : public TPrinter { 675 private: printPrologue(std::ostream & out) const676 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 677 printEpilogue(std::ostream & out) const678 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 679 commentBOL() const680 std::string commentBOL() const override { return "-- "; } 681 enumBeg(const std::string & s,enumStyle_t style) const682 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 683 return indent() + s + styleStr(style) + " = {\n"; 684 } 685 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const686 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 687 return indent() + "},\n\n"; 688 } 689 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const690 std::string enumFmt(const std::string& s, const valpair_t& v, 691 enumStyle_t style, bool isLast) const override { 692 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 693 } 694 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const695 virtual std::string fmtConstInt(unsigned val, const std::string& name, 696 const char* fmt, bool isLast) const override 697 { 698 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 699 } 700 }; 701 702 // Python printer 703 class TPrinterPython final : public TPrinter { 704 private: printPrologue(std::ostream & out) const705 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 706 printEpilogue(std::ostream & out) const707 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 708 commentBOL() const709 std::string commentBOL() const override { return "# "; } 710 enumBeg(const std::string & s,enumStyle_t style) const711 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 712 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 713 } 714 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const715 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 716 return indent() + "},\n\n"; 717 } 718 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const719 std::string enumFmt(const std::string& s, const valpair_t& v, 720 enumStyle_t style, bool isLast) const override { 721 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 722 } 723 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const724 std::string fmtConstInt(unsigned val, const std::string& name, 725 const char* fmt, bool isLast) const override 726 { 727 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 728 } 729 }; 730 731 // C# printer 732 class TPrinterCSharp final : public TPrinter { 733 private: commentBOL() const734 std::string commentBOL() const override { return "// "; } 735 printPrologue(std::ostream & out) const736 void printPrologue(std::ostream& out) const override { 737 out << "namespace Spv\n{\n\n"; 738 out << indent() << "public static class Specification\n"; 739 out << indent() << "{\n"; 740 } 741 printEpilogue(std::ostream & out) const742 void printEpilogue(std::ostream& out) const override { 743 out << indent() << "}\n"; 744 out << "}\n"; 745 } 746 enumBeg(const std::string & s,enumStyle_t style) const747 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 748 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 749 } 750 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const751 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 752 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 753 } 754 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const755 std::string enumFmt(const std::string& s, const valpair_t& v, 756 enumStyle_t style, bool isLast) const override { 757 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 758 } 759 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const760 std::string fmtConstInt(unsigned val, const std::string& name, 761 const char* fmt, bool isLast) const override { 762 return indent(2) + std::string("public const uint ") + name + 763 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 764 } 765 }; 766 767 // D printer 768 class TPrinterD final : public TPrinter { 769 private: commentBeg() const770 std::string commentBeg() const override { return "/+\n"; } commentBOL() const771 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const772 std::string commentEnd(bool isLast) const override { return " +/\n"; } 773 printPrologue(std::ostream & out) const774 void printPrologue(std::ostream& out) const override { 775 out << "module spv;\n\n"; 776 } 777 printEpilogue(std::ostream & out) const778 void printEpilogue(std::ostream& out) const override { 779 } 780 enumBeg(const std::string & s,enumStyle_t style) const781 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 782 return "enum " + s + styleStr(style) + " : uint\n{\n"; 783 } 784 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const785 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 786 return std::string("}\n\n"); 787 } 788 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const789 std::string enumFmt(const std::string& s, const valpair_t& v, 790 enumStyle_t style, bool isLast) const override { 791 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 792 } 793 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const794 std::string fmtConstInt(unsigned val, const std::string& name, 795 const char* fmt, bool isLast) const override { 796 return std::string("enum uint ") + name + 797 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 798 } 799 }; 800 801 // Beef printer 802 class TPrinterBeef final : public TPrinter { 803 private: commentBOL() const804 std::string commentBOL() const override { return "// "; } 805 printPrologue(std::ostream & out) const806 void printPrologue(std::ostream& out) const override { 807 out << "namespace Spv\n{\n"; 808 out << indent() << "using System;\n\n"; 809 out << indent() << "public static class Specification\n"; 810 out << indent() << "{\n"; 811 } 812 printEpilogue(std::ostream & out) const813 void printEpilogue(std::ostream& out) const override { 814 out << indent() << "}\n"; 815 out << "}\n"; 816 } 817 enumBeg(const std::string & s,enumStyle_t style) const818 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 819 return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 820 } 821 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const822 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 823 return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); 824 } 825 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const826 std::string enumFmt(const std::string& s, const valpair_t& v, 827 enumStyle_t style, bool isLast) const override { 828 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 829 } 830 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const831 std::string fmtConstInt(unsigned val, const std::string& name, 832 const char* fmt, bool isLast) const override { 833 return indent(2) + std::string("public const uint32 ") + name + 834 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 835 } 836 }; 837 838 } // namespace 839 840 namespace spv { PrintAllHeaders()841 void PrintAllHeaders() 842 { 843 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 844 std::vector<std::pair<TLanguage, std::string>> langInfo; 845 846 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 847 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 848 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 849 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 850 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 851 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 852 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 853 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 854 langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); 855 856 for (const auto& lang : langInfo) { 857 std::ofstream out(lang.second, std::ios::out); 858 859 if ((out.rdstate() & std::ifstream::failbit)) { 860 std::cerr << "Unable to open file: " << lang.second << std::endl; 861 } else { 862 PrintHeader(lang.first, out); 863 } 864 } 865 } 866 867 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)868 void PrintHeader(TLanguage lang, std::ostream& out) 869 { 870 typedef std::unique_ptr<TPrinter> TPrinterPtr; 871 TPrinterPtr p; 872 873 switch (lang) { 874 case ELangC: p = TPrinterPtr(new TPrinterC); break; 875 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 876 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 877 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 878 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 879 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 880 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 881 case ELangD: p = TPrinterPtr(new TPrinterD); break; 882 case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; 883 case ELangAll: PrintAllHeaders(); break; 884 default: 885 std::cerr << "Unknown language." << std::endl; 886 return; 887 } 888 889 // Print the data in the requested format 890 if (p) 891 out << *p << std::endl; 892 893 // object is auto-deleted 894 } 895 896 } // namespace spv 897