• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2014-2015 LunarG, Inc.
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 //    Redistributions of source code must retain the above copyright
11 //    notice, this list of conditions and the following disclaimer.
12 //
13 //    Redistributions in binary form must reproduce the above
14 //    copyright notice, this list of conditions and the following
15 //    disclaimer in the documentation and/or other materials provided
16 //    with the distribution.
17 //
18 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19 //    contributors may be used to endorse or promote products derived
20 //    from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 // POSSIBILITY OF SUCH DAMAGE.
34 
35 //
36 // Disassembler for SPIR-V.
37 //
38 
39 #include <cstdlib>
40 #include <cstring>
41 #include <cassert>
42 #include <iomanip>
43 #include <stack>
44 #include <sstream>
45 #include <cstring>
46 #include <utility>
47 
48 #include "disassemble.h"
49 #include "doc.h"
50 
51 namespace spv {
52     extern "C" {
53         // Include C-based headers that don't have a namespace
54         #include "GLSL.std.450.h"
55         #include "GLSL.ext.AMD.h"
56         #include "GLSL.ext.NV.h"
57         #include "GLSL.ext.ARM.h"
58         #include "NonSemanticShaderDebugInfo100.h"
59         #include "GLSL.ext.QCOM.h"
60     }
61 }
62 const char* GlslStd450DebugNames[spv::GLSLstd450Count];
63 
64 namespace spv {
65 
66 static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
67 static const char* GLSLextNVGetDebugNames(const char*, unsigned);
68 static const char* NonSemanticShaderDebugInfo100GetDebugNames(unsigned);
69 
Kill(std::ostream & out,const char * message)70 static void Kill(std::ostream& out, const char* message)
71 {
72     out << std::endl << "Disassembly failed: " << message << std::endl;
73     exit(1);
74 }
75 
76 // used to identify the extended instruction library imported when printing
77 enum ExtInstSet {
78     GLSL450Inst,
79     GLSLextAMDInst,
80     GLSLextNVInst,
81     OpenCLExtInst,
82     NonSemanticDebugPrintfExtInst,
83     NonSemanticDebugBreakExtInst,
84     NonSemanticShaderDebugInfo100
85 };
86 
87 // Container class for a single instance of a SPIR-V stream, with methods for disassembly.
88 class SpirvStream {
89 public:
SpirvStream(std::ostream & out,const std::vector<unsigned int> & stream)90     SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
~SpirvStream()91     virtual ~SpirvStream() { }
92 
93     void validate();
94     void processInstructions();
95 
96 protected:
97     SpirvStream(const SpirvStream&);
98     SpirvStream& operator=(const SpirvStream&);
getOpCode(int id) const99     Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
100 
101     // Output methods
102     void outputIndent();
103     void formatId(Id id, std::stringstream&);
104     void outputResultId(Id id);
105     void outputTypeId(Id id);
106     void outputId(Id id);
107     void outputMask(OperandClass operandClass, unsigned mask);
108     void disassembleImmediates(int numOperands);
109     void disassembleIds(int numOperands);
110     std::pair<int, std::string> decodeString();
111     int disassembleString();
112     void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
113 
114     // Data
115     std::ostream& out;                       // where to write the disassembly
116     const std::vector<unsigned int>& stream; // the actual word stream
117     int size;                                // the size of the word stream
118     int word;                                // the next word of the stream to read
119 
120     // map each <id> to the instruction that created it
121     Id bound;
122     std::vector<unsigned int> idInstruction;  // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter)
123 
124     std::vector<std::string> idDescriptor;    // the best text string known for explaining the <id>
125 
126     // schema
127     unsigned int schema;
128 
129     // stack of structured-merge points
130     std::stack<Id> nestedControl;
131     Id nextNestedControl;         // need a slight delay for when we are nested
132 };
133 
validate()134 void SpirvStream::validate()
135 {
136     size = (int)stream.size();
137     if (size < 4)
138         Kill(out, "stream is too short");
139 
140     // Magic number
141     if (stream[word++] != MagicNumber) {
142         out << "Bad magic number";
143         return;
144     }
145 
146     // Version
147     out << "// Module Version " << std::hex << stream[word++] << std::endl;
148 
149     // Generator's magic number
150     out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
151 
152     // Result <id> bound
153     bound = stream[word++];
154     idInstruction.resize(bound);
155     idDescriptor.resize(bound);
156     out << "// Id's are bound by " << bound << std::endl;
157     out << std::endl;
158 
159     // Reserved schema, must be 0 for now
160     schema = stream[word++];
161     if (schema != 0)
162         Kill(out, "bad schema, must be 0");
163 }
164 
165 // Loop over all the instructions, in order, processing each.
166 // Boiler plate for each is handled here directly, the rest is dispatched.
processInstructions()167 void SpirvStream::processInstructions()
168 {
169     // Instructions
170     while (word < size) {
171         int instructionStart = word;
172 
173         // Instruction wordCount and opcode
174         unsigned int firstWord = stream[word];
175         unsigned wordCount = firstWord >> WordCountShift;
176         Op opCode = (Op)(firstWord & OpCodeMask);
177         int nextInst = word + wordCount;
178         ++word;
179 
180         // Presence of full instruction
181         if (nextInst > size)
182             Kill(out, "stream instruction terminated too early");
183 
184         // Base for computing number of operands; will be updated as more is learned
185         unsigned numOperands = wordCount - 1;
186 
187         // Type <id>
188         Id typeId = 0;
189         if (InstructionDesc[opCode].hasType()) {
190             typeId = stream[word++];
191             --numOperands;
192         }
193 
194         // Result <id>
195         Id resultId = 0;
196         if (InstructionDesc[opCode].hasResult()) {
197             resultId = stream[word++];
198             --numOperands;
199 
200             // save instruction for future reference
201             idInstruction[resultId] = instructionStart;
202         }
203 
204         outputResultId(resultId);
205         outputTypeId(typeId);
206         outputIndent();
207 
208         // Hand off the Op and all its operands
209         disassembleInstruction(resultId, typeId, opCode, numOperands);
210         if (word != nextInst) {
211             out << " ERROR, incorrect number of operands consumed.  At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
212             word = nextInst;
213         }
214         out << std::endl;
215     }
216 }
217 
outputIndent()218 void SpirvStream::outputIndent()
219 {
220     for (int i = 0; i < (int)nestedControl.size(); ++i)
221         out << "  ";
222 }
223 
formatId(Id id,std::stringstream & idStream)224 void SpirvStream::formatId(Id id, std::stringstream& idStream)
225 {
226     if (id != 0) {
227         // On instructions with no IDs, this is called with "0", which does not
228         // have to be within ID bounds on null shaders.
229         if (id >= bound)
230             Kill(out, "Bad <id>");
231 
232         idStream << id;
233         if (idDescriptor[id].size() > 0)
234             idStream << "(" << idDescriptor[id] << ")";
235     }
236 }
237 
outputResultId(Id id)238 void SpirvStream::outputResultId(Id id)
239 {
240     const int width = 16;
241     std::stringstream idStream;
242     formatId(id, idStream);
243     out << std::setw(width) << std::right << idStream.str();
244     if (id != 0)
245         out << ":";
246     else
247         out << " ";
248 
249     if (nestedControl.size() && id == nestedControl.top())
250         nestedControl.pop();
251 }
252 
outputTypeId(Id id)253 void SpirvStream::outputTypeId(Id id)
254 {
255     const int width = 12;
256     std::stringstream idStream;
257     formatId(id, idStream);
258     out << std::setw(width) << std::right << idStream.str() << " ";
259 }
260 
outputId(Id id)261 void SpirvStream::outputId(Id id)
262 {
263     if (id >= bound)
264         Kill(out, "Bad <id>");
265 
266     out << id;
267     if (idDescriptor[id].size() > 0)
268         out << "(" << idDescriptor[id] << ")";
269 }
270 
outputMask(OperandClass operandClass,unsigned mask)271 void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
272 {
273     if (mask == 0)
274         out << "None";
275     else {
276         for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
277             if (mask & (1 << m))
278                 out << OperandClassParams[operandClass].getName(m) << " ";
279         }
280     }
281 }
282 
disassembleImmediates(int numOperands)283 void SpirvStream::disassembleImmediates(int numOperands)
284 {
285     for (int i = 0; i < numOperands; ++i) {
286         out << stream[word++];
287         if (i < numOperands - 1)
288             out << " ";
289     }
290 }
291 
disassembleIds(int numOperands)292 void SpirvStream::disassembleIds(int numOperands)
293 {
294     for (int i = 0; i < numOperands; ++i) {
295         outputId(stream[word++]);
296         if (i < numOperands - 1)
297             out << " ";
298     }
299 }
300 
301 // decode string from words at current position (non-consuming)
decodeString()302 std::pair<int, std::string> SpirvStream::decodeString()
303 {
304     std::string res;
305     int wordPos = word;
306     char c;
307     bool done = false;
308 
309     do {
310         unsigned int content = stream[wordPos];
311         for (int charCount = 0; charCount < 4; ++charCount) {
312             c = content & 0xff;
313             content >>= 8;
314             if (c == '\0') {
315                 done = true;
316                 break;
317             }
318             res += c;
319         }
320         ++wordPos;
321     } while(! done);
322 
323     return std::make_pair(wordPos - word, res);
324 }
325 
326 // return the number of operands consumed by the string
disassembleString()327 int SpirvStream::disassembleString()
328 {
329     out << " \"";
330 
331     std::pair<int, std::string> decoderes = decodeString();
332 
333     out << decoderes.second;
334     out << "\"";
335 
336     word += decoderes.first;
337 
338     return decoderes.first;
339 }
340 
disassembleInstruction(Id resultId,Id,Op opCode,int numOperands)341 void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
342 {
343     // Process the opcode
344 
345     out << (OpcodeString(opCode) + 2);  // leave out the "Op"
346 
347     if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
348         nextNestedControl = stream[word];
349     else if (opCode == OpBranchConditional || opCode == OpSwitch) {
350         if (nextNestedControl) {
351             nestedControl.push(nextNestedControl);
352             nextNestedControl = 0;
353         }
354     } else if (opCode == OpExtInstImport) {
355         idDescriptor[resultId] = decodeString().second;
356     }
357     else {
358         if (resultId != 0 && idDescriptor[resultId].size() == 0) {
359             switch (opCode) {
360             case OpTypeInt:
361                 switch (stream[word]) {
362                 case 8:  idDescriptor[resultId] = "int8_t"; break;
363                 case 16: idDescriptor[resultId] = "int16_t"; break;
364                 default: assert(0); [[fallthrough]];
365                 case 32: idDescriptor[resultId] = "int"; break;
366                 case 64: idDescriptor[resultId] = "int64_t"; break;
367                 }
368                 break;
369             case OpTypeFloat:
370                 switch (stream[word]) {
371                 case 16: idDescriptor[resultId] = "float16_t"; break;
372                 default: assert(0); [[fallthrough]];
373                 case 32: idDescriptor[resultId] = "float"; break;
374                 case 64: idDescriptor[resultId] = "float64_t"; break;
375                 }
376                 break;
377             case OpTypeBool:
378                 idDescriptor[resultId] = "bool";
379                 break;
380             case OpTypeStruct:
381                 idDescriptor[resultId] = "struct";
382                 break;
383             case OpTypePointer:
384                 idDescriptor[resultId] = "ptr";
385                 break;
386             case OpTypeVector:
387                 if (idDescriptor[stream[word]].size() > 0) {
388                     idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
389                     if (strstr(idDescriptor[stream[word]].c_str(), "8")) {
390                         idDescriptor[resultId].append("8");
391                     }
392                     if (strstr(idDescriptor[stream[word]].c_str(), "16")) {
393                         idDescriptor[resultId].append("16");
394                     }
395                     if (strstr(idDescriptor[stream[word]].c_str(), "64")) {
396                         idDescriptor[resultId].append("64");
397                     }
398                 }
399                 idDescriptor[resultId].append("vec");
400                 switch (stream[word + 1]) {
401                 case 2:   idDescriptor[resultId].append("2");   break;
402                 case 3:   idDescriptor[resultId].append("3");   break;
403                 case 4:   idDescriptor[resultId].append("4");   break;
404                 case 8:   idDescriptor[resultId].append("8");   break;
405                 case 16:  idDescriptor[resultId].append("16");  break;
406                 case 32:  idDescriptor[resultId].append("32");  break;
407                 default: break;
408                 }
409                 break;
410             default:
411                 break;
412             }
413         }
414     }
415 
416     // Process the operands.  Note, a new context-dependent set could be
417     // swapped in mid-traversal.
418 
419     // Handle images specially, so can put out helpful strings.
420     if (opCode == OpTypeImage) {
421         out << " ";
422         disassembleIds(1);
423         out << " " << DimensionString((Dim)stream[word++]);
424         out << (stream[word++] != 0 ? " depth" : "");
425         out << (stream[word++] != 0 ? " array" : "");
426         out << (stream[word++] != 0 ? " multi-sampled" : "");
427         switch (stream[word++]) {
428         case 0: out << " runtime";    break;
429         case 1: out << " sampled";    break;
430         case 2: out << " nonsampled"; break;
431         }
432         out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
433 
434         if (numOperands == 8) {
435             out << " " << AccessQualifierString(stream[word++]);
436         }
437         return;
438     }
439 
440     // Handle all the parameterized operands
441     for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
442         out << " ";
443         OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
444         switch (operandClass) {
445         case OperandId:
446         case OperandScope:
447         case OperandMemorySemantics:
448             disassembleIds(1);
449             --numOperands;
450             // Get names for printing "(XXX)" for readability, *after* this id
451             if (opCode == OpName)
452                 idDescriptor[stream[word - 1]] = decodeString().second;
453             break;
454         case OperandVariableIds:
455             disassembleIds(numOperands);
456             return;
457         case OperandImageOperands:
458             outputMask(OperandImageOperands, stream[word++]);
459             --numOperands;
460             disassembleIds(numOperands);
461             return;
462         case OperandOptionalLiteral:
463         case OperandVariableLiterals:
464             if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
465                 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
466                 out << BuiltInString(stream[word++]);
467                 --numOperands;
468                 ++op;
469             }
470             disassembleImmediates(numOperands);
471             return;
472         case OperandVariableIdLiteral:
473             while (numOperands > 0) {
474                 out << std::endl;
475                 outputResultId(0);
476                 outputTypeId(0);
477                 outputIndent();
478                 out << "     Type ";
479                 disassembleIds(1);
480                 out << ", member ";
481                 disassembleImmediates(1);
482                 numOperands -= 2;
483             }
484             return;
485         case OperandVariableLiteralId:
486             while (numOperands > 0) {
487                 out << std::endl;
488                 outputResultId(0);
489                 outputTypeId(0);
490                 outputIndent();
491                 out << "     case ";
492                 disassembleImmediates(1);
493                 out << ": ";
494                 disassembleIds(1);
495                 numOperands -= 2;
496             }
497             return;
498         case OperandLiteralNumber:
499             disassembleImmediates(1);
500             --numOperands;
501             if (opCode == OpExtInst) {
502                 ExtInstSet extInstSet = GLSL450Inst;
503                 const char* name = idDescriptor[stream[word - 2]].c_str();
504                 if (strcmp("OpenCL.std", name) == 0) {
505                     extInstSet = OpenCLExtInst;
506                 } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) {
507                     extInstSet = OpenCLExtInst;
508                 } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) {
509                     extInstSet = NonSemanticDebugPrintfExtInst;
510                 } else if (strcmp("NonSemantic.DebugBreak", name) == 0) {
511                     extInstSet = NonSemanticDebugBreakExtInst;
512                 } else if (strcmp("NonSemantic.Shader.DebugInfo.100", name) == 0) {
513                     extInstSet = NonSemanticShaderDebugInfo100;
514                 } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
515                            strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
516                            strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
517                            strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
518                     extInstSet = GLSLextAMDInst;
519                 } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
520                           strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
521                           strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
522                           strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 ||
523                           strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
524                           strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) {
525                     extInstSet = GLSLextNVInst;
526                 }
527                 unsigned entrypoint = stream[word - 1];
528                 if (extInstSet == GLSL450Inst) {
529                     if (entrypoint < GLSLstd450Count) {
530                         out << "(" << GlslStd450DebugNames[entrypoint] << ")";
531                     }
532                 } else if (extInstSet == GLSLextAMDInst) {
533                     out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
534                 }
535                 else if (extInstSet == GLSLextNVInst) {
536                     out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
537                 } else if (extInstSet == NonSemanticDebugPrintfExtInst) {
538                     out << "(DebugPrintf)";
539                 } else if (extInstSet == NonSemanticDebugBreakExtInst) {
540                     out << "(DebugBreak)";
541                 } else if (extInstSet == NonSemanticShaderDebugInfo100) {
542                     out << "(" << NonSemanticShaderDebugInfo100GetDebugNames(entrypoint) << ")";
543                 }
544             }
545             break;
546         case OperandOptionalLiteralString:
547         case OperandLiteralString:
548             numOperands -= disassembleString();
549             break;
550         case OperandVariableLiteralStrings:
551             while (numOperands > 0)
552                 numOperands -= disassembleString();
553             return;
554         case OperandMemoryAccess:
555             outputMask(OperandMemoryAccess, stream[word++]);
556             --numOperands;
557             // Aligned is the only memory access operand that uses an immediate
558             // value, and it is also the first operand that uses a value at all.
559             if (stream[word-1] & MemoryAccessAlignedMask) {
560                 disassembleImmediates(1);
561                 numOperands--;
562                 if (numOperands)
563                     out << " ";
564             }
565             disassembleIds(numOperands);
566             return;
567         default:
568             assert(operandClass >= OperandSource && operandClass < OperandOpcode);
569 
570             if (OperandClassParams[operandClass].bitmask)
571                 outputMask(operandClass, stream[word++]);
572             else
573                 out << OperandClassParams[operandClass].getName(stream[word++]);
574             --numOperands;
575 
576             break;
577         }
578     }
579 
580     return;
581 }
582 
GLSLstd450GetDebugNames(const char ** names)583 static void GLSLstd450GetDebugNames(const char** names)
584 {
585     for (int i = 0; i < GLSLstd450Count; ++i)
586         names[i] = "Unknown";
587 
588     names[GLSLstd450Round]                   = "Round";
589     names[GLSLstd450RoundEven]               = "RoundEven";
590     names[GLSLstd450Trunc]                   = "Trunc";
591     names[GLSLstd450FAbs]                    = "FAbs";
592     names[GLSLstd450SAbs]                    = "SAbs";
593     names[GLSLstd450FSign]                   = "FSign";
594     names[GLSLstd450SSign]                   = "SSign";
595     names[GLSLstd450Floor]                   = "Floor";
596     names[GLSLstd450Ceil]                    = "Ceil";
597     names[GLSLstd450Fract]                   = "Fract";
598     names[GLSLstd450Radians]                 = "Radians";
599     names[GLSLstd450Degrees]                 = "Degrees";
600     names[GLSLstd450Sin]                     = "Sin";
601     names[GLSLstd450Cos]                     = "Cos";
602     names[GLSLstd450Tan]                     = "Tan";
603     names[GLSLstd450Asin]                    = "Asin";
604     names[GLSLstd450Acos]                    = "Acos";
605     names[GLSLstd450Atan]                    = "Atan";
606     names[GLSLstd450Sinh]                    = "Sinh";
607     names[GLSLstd450Cosh]                    = "Cosh";
608     names[GLSLstd450Tanh]                    = "Tanh";
609     names[GLSLstd450Asinh]                   = "Asinh";
610     names[GLSLstd450Acosh]                   = "Acosh";
611     names[GLSLstd450Atanh]                   = "Atanh";
612     names[GLSLstd450Atan2]                   = "Atan2";
613     names[GLSLstd450Pow]                     = "Pow";
614     names[GLSLstd450Exp]                     = "Exp";
615     names[GLSLstd450Log]                     = "Log";
616     names[GLSLstd450Exp2]                    = "Exp2";
617     names[GLSLstd450Log2]                    = "Log2";
618     names[GLSLstd450Sqrt]                    = "Sqrt";
619     names[GLSLstd450InverseSqrt]             = "InverseSqrt";
620     names[GLSLstd450Determinant]             = "Determinant";
621     names[GLSLstd450MatrixInverse]           = "MatrixInverse";
622     names[GLSLstd450Modf]                    = "Modf";
623     names[GLSLstd450ModfStruct]              = "ModfStruct";
624     names[GLSLstd450FMin]                    = "FMin";
625     names[GLSLstd450SMin]                    = "SMin";
626     names[GLSLstd450UMin]                    = "UMin";
627     names[GLSLstd450FMax]                    = "FMax";
628     names[GLSLstd450SMax]                    = "SMax";
629     names[GLSLstd450UMax]                    = "UMax";
630     names[GLSLstd450FClamp]                  = "FClamp";
631     names[GLSLstd450SClamp]                  = "SClamp";
632     names[GLSLstd450UClamp]                  = "UClamp";
633     names[GLSLstd450FMix]                    = "FMix";
634     names[GLSLstd450Step]                    = "Step";
635     names[GLSLstd450SmoothStep]              = "SmoothStep";
636     names[GLSLstd450Fma]                     = "Fma";
637     names[GLSLstd450Frexp]                   = "Frexp";
638     names[GLSLstd450FrexpStruct]             = "FrexpStruct";
639     names[GLSLstd450Ldexp]                   = "Ldexp";
640     names[GLSLstd450PackSnorm4x8]            = "PackSnorm4x8";
641     names[GLSLstd450PackUnorm4x8]            = "PackUnorm4x8";
642     names[GLSLstd450PackSnorm2x16]           = "PackSnorm2x16";
643     names[GLSLstd450PackUnorm2x16]           = "PackUnorm2x16";
644     names[GLSLstd450PackHalf2x16]            = "PackHalf2x16";
645     names[GLSLstd450PackDouble2x32]          = "PackDouble2x32";
646     names[GLSLstd450UnpackSnorm2x16]         = "UnpackSnorm2x16";
647     names[GLSLstd450UnpackUnorm2x16]         = "UnpackUnorm2x16";
648     names[GLSLstd450UnpackHalf2x16]          = "UnpackHalf2x16";
649     names[GLSLstd450UnpackSnorm4x8]          = "UnpackSnorm4x8";
650     names[GLSLstd450UnpackUnorm4x8]          = "UnpackUnorm4x8";
651     names[GLSLstd450UnpackDouble2x32]        = "UnpackDouble2x32";
652     names[GLSLstd450Length]                  = "Length";
653     names[GLSLstd450Distance]                = "Distance";
654     names[GLSLstd450Cross]                   = "Cross";
655     names[GLSLstd450Normalize]               = "Normalize";
656     names[GLSLstd450FaceForward]             = "FaceForward";
657     names[GLSLstd450Reflect]                 = "Reflect";
658     names[GLSLstd450Refract]                 = "Refract";
659     names[GLSLstd450FindILsb]                = "FindILsb";
660     names[GLSLstd450FindSMsb]                = "FindSMsb";
661     names[GLSLstd450FindUMsb]                = "FindUMsb";
662     names[GLSLstd450InterpolateAtCentroid]   = "InterpolateAtCentroid";
663     names[GLSLstd450InterpolateAtSample]     = "InterpolateAtSample";
664     names[GLSLstd450InterpolateAtOffset]     = "InterpolateAtOffset";
665     names[GLSLstd450NMin]                    = "NMin";
666     names[GLSLstd450NMax]                    = "NMax";
667     names[GLSLstd450NClamp]                  = "NClamp";
668 }
669 
GLSLextAMDGetDebugNames(const char * name,unsigned entrypoint)670 static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
671 {
672     if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
673         switch (entrypoint) {
674         case SwizzleInvocationsAMD:         return "SwizzleInvocationsAMD";
675         case SwizzleInvocationsMaskedAMD:   return "SwizzleInvocationsMaskedAMD";
676         case WriteInvocationAMD:            return "WriteInvocationAMD";
677         case MbcntAMD:                      return "MbcntAMD";
678         default:                            return "Bad";
679         }
680     } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
681         switch (entrypoint) {
682         case FMin3AMD:      return "FMin3AMD";
683         case UMin3AMD:      return "UMin3AMD";
684         case SMin3AMD:      return "SMin3AMD";
685         case FMax3AMD:      return "FMax3AMD";
686         case UMax3AMD:      return "UMax3AMD";
687         case SMax3AMD:      return "SMax3AMD";
688         case FMid3AMD:      return "FMid3AMD";
689         case UMid3AMD:      return "UMid3AMD";
690         case SMid3AMD:      return "SMid3AMD";
691         default:            return "Bad";
692         }
693     } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
694         switch (entrypoint) {
695         case InterpolateAtVertexAMD:    return "InterpolateAtVertexAMD";
696         default:                        return "Bad";
697         }
698     }
699     else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
700         switch (entrypoint) {
701         case CubeFaceIndexAMD:      return "CubeFaceIndexAMD";
702         case CubeFaceCoordAMD:      return "CubeFaceCoordAMD";
703         case TimeAMD:               return "TimeAMD";
704         default:
705             break;
706         }
707     }
708 
709     return "Bad";
710 }
711 
GLSLextNVGetDebugNames(const char * name,unsigned entrypoint)712 static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
713 {
714     if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
715         strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
716         strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
717         strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
718         strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 ||
719         strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 ||
720         strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 ||
721         strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) {
722         switch (entrypoint) {
723         // NV builtins
724         case BuiltInViewportMaskNV:                 return "ViewportMaskNV";
725         case BuiltInSecondaryPositionNV:            return "SecondaryPositionNV";
726         case BuiltInSecondaryViewportMaskNV:        return "SecondaryViewportMaskNV";
727         case BuiltInPositionPerViewNV:              return "PositionPerViewNV";
728         case BuiltInViewportMaskPerViewNV:          return "ViewportMaskPerViewNV";
729         case BuiltInBaryCoordNV:                    return "BaryCoordNV";
730         case BuiltInBaryCoordNoPerspNV:             return "BaryCoordNoPerspNV";
731         case BuiltInTaskCountNV:                    return "TaskCountNV";
732         case BuiltInPrimitiveCountNV:               return "PrimitiveCountNV";
733         case BuiltInPrimitiveIndicesNV:             return "PrimitiveIndicesNV";
734         case BuiltInClipDistancePerViewNV:          return "ClipDistancePerViewNV";
735         case BuiltInCullDistancePerViewNV:          return "CullDistancePerViewNV";
736         case BuiltInLayerPerViewNV:                 return "LayerPerViewNV";
737         case BuiltInMeshViewCountNV:                return "MeshViewCountNV";
738         case BuiltInMeshViewIndicesNV:              return "MeshViewIndicesNV";
739 
740         // NV Capabilities
741         case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
742         case CapabilityShaderViewportMaskNV:        return "ShaderViewportMaskNV";
743         case CapabilityShaderStereoViewNV:          return "ShaderStereoViewNV";
744         case CapabilityPerViewAttributesNV:         return "PerViewAttributesNV";
745         case CapabilityFragmentBarycentricNV:       return "FragmentBarycentricNV";
746         case CapabilityMeshShadingNV:               return "MeshShadingNV";
747         case CapabilityImageFootprintNV:            return "ImageFootprintNV";
748         case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
749 
750         // NV Decorations
751         case DecorationOverrideCoverageNV:          return "OverrideCoverageNV";
752         case DecorationPassthroughNV:               return "PassthroughNV";
753         case DecorationViewportRelativeNV:          return "ViewportRelativeNV";
754         case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
755         case DecorationPerVertexNV:                 return "PerVertexNV";
756         case DecorationPerPrimitiveNV:              return "PerPrimitiveNV";
757         case DecorationPerViewNV:                   return "PerViewNV";
758         case DecorationPerTaskNV:                   return "PerTaskNV";
759 
760         default:                                    return "Bad";
761         }
762     }
763     return "Bad";
764 }
765 
NonSemanticShaderDebugInfo100GetDebugNames(unsigned entrypoint)766 static const char* NonSemanticShaderDebugInfo100GetDebugNames(unsigned entrypoint)
767 {
768     switch (entrypoint) {
769         case NonSemanticShaderDebugInfo100DebugInfoNone:                        return "DebugInfoNone";
770         case NonSemanticShaderDebugInfo100DebugCompilationUnit:                 return "DebugCompilationUnit";
771         case NonSemanticShaderDebugInfo100DebugTypeBasic:                       return "DebugTypeBasic";
772         case NonSemanticShaderDebugInfo100DebugTypePointer:                     return "DebugTypePointer";
773         case NonSemanticShaderDebugInfo100DebugTypeQualifier:                   return "DebugTypeQualifier";
774         case NonSemanticShaderDebugInfo100DebugTypeArray:                       return "DebugTypeArray";
775         case NonSemanticShaderDebugInfo100DebugTypeVector:                      return "DebugTypeVector";
776         case NonSemanticShaderDebugInfo100DebugTypedef:                         return "DebugTypedef";
777         case NonSemanticShaderDebugInfo100DebugTypeFunction:                    return "DebugTypeFunction";
778         case NonSemanticShaderDebugInfo100DebugTypeEnum:                        return "DebugTypeEnum";
779         case NonSemanticShaderDebugInfo100DebugTypeComposite:                   return "DebugTypeComposite";
780         case NonSemanticShaderDebugInfo100DebugTypeMember:                      return "DebugTypeMember";
781         case NonSemanticShaderDebugInfo100DebugTypeInheritance:                 return "DebugTypeInheritance";
782         case NonSemanticShaderDebugInfo100DebugTypePtrToMember:                 return "DebugTypePtrToMember";
783         case NonSemanticShaderDebugInfo100DebugTypeTemplate:                    return "DebugTypeTemplate";
784         case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter:           return "DebugTypeTemplateParameter";
785         case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter:   return "DebugTypeTemplateTemplateParameter";
786         case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack:       return "DebugTypeTemplateParameterPack";
787         case NonSemanticShaderDebugInfo100DebugGlobalVariable:                  return "DebugGlobalVariable";
788         case NonSemanticShaderDebugInfo100DebugFunctionDeclaration:             return "DebugFunctionDeclaration";
789         case NonSemanticShaderDebugInfo100DebugFunction:                        return "DebugFunction";
790         case NonSemanticShaderDebugInfo100DebugLexicalBlock:                    return "DebugLexicalBlock";
791         case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator:       return "DebugLexicalBlockDiscriminator";
792         case NonSemanticShaderDebugInfo100DebugScope:                           return "DebugScope";
793         case NonSemanticShaderDebugInfo100DebugNoScope:                         return "DebugNoScope";
794         case NonSemanticShaderDebugInfo100DebugInlinedAt:                       return "DebugInlinedAt";
795         case NonSemanticShaderDebugInfo100DebugLocalVariable:                   return "DebugLocalVariable";
796         case NonSemanticShaderDebugInfo100DebugInlinedVariable:                 return "DebugInlinedVariable";
797         case NonSemanticShaderDebugInfo100DebugDeclare:                         return "DebugDeclare";
798         case NonSemanticShaderDebugInfo100DebugValue:                           return "DebugValue";
799         case NonSemanticShaderDebugInfo100DebugOperation:                       return "DebugOperation";
800         case NonSemanticShaderDebugInfo100DebugExpression:                      return "DebugExpression";
801         case NonSemanticShaderDebugInfo100DebugMacroDef:                        return "DebugMacroDef";
802         case NonSemanticShaderDebugInfo100DebugMacroUndef:                      return "DebugMacroUndef";
803         case NonSemanticShaderDebugInfo100DebugImportedEntity:                  return "DebugImportedEntity";
804         case NonSemanticShaderDebugInfo100DebugSource:                          return "DebugSource";
805         case NonSemanticShaderDebugInfo100DebugFunctionDefinition:              return "DebugFunctionDefinition";
806         case NonSemanticShaderDebugInfo100DebugSourceContinued:                 return "DebugSourceContinued";
807         case NonSemanticShaderDebugInfo100DebugLine:                            return "DebugLine";
808         case NonSemanticShaderDebugInfo100DebugNoLine:                          return "DebugNoLine";
809         case NonSemanticShaderDebugInfo100DebugBuildIdentifier:                 return "DebugBuildIdentifier";
810         case NonSemanticShaderDebugInfo100DebugStoragePath:                     return "DebugStoragePath";
811         case NonSemanticShaderDebugInfo100DebugEntryPoint:                      return "DebugEntryPoint";
812         case NonSemanticShaderDebugInfo100DebugTypeMatrix:                      return "DebugTypeMatrix";
813         default:                                                                return "Bad";
814     }
815 
816     return "Bad";
817 }
818 
Disassemble(std::ostream & out,const std::vector<unsigned int> & stream)819 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
820 {
821     SpirvStream SpirvStream(out, stream);
822     spv::Parameterize();
823     GLSLstd450GetDebugNames(GlslStd450DebugNames);
824     SpirvStream.validate();
825     SpirvStream.processInstructions();
826 }
827 
828 }; // end namespace spv
829