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