xref: /aosp_15_r20/external/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/header.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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