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