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 
47 #include "disassemble.h"
48 #include "doc.h"
49 
50 namespace spv {
51     extern "C" {
52         // Include C-based headers that don't have a namespace
53         #include "GLSL.std.450.h"
54         #include "GLSL.ext.AMD.h"
55         #include "GLSL.ext.NV.h"
56     }
57 }
58 const char* GlslStd450DebugNames[spv::GLSLstd450Count];
59 
60 namespace spv {
61 
62 static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
63 static const char* GLSLextNVGetDebugNames(const char*, unsigned);
64 
Kill(std::ostream & out,const char * message)65 static void Kill(std::ostream& out, const char* message)
66 {
67     out << std::endl << "Disassembly failed: " << message << std::endl;
68     exit(1);
69 }
70 
71 // used to identify the extended instruction library imported when printing
72 enum ExtInstSet {
73     GLSL450Inst,
74     GLSLextAMDInst,
75     GLSLextNVInst,
76     OpenCLExtInst,
77     NonSemanticDebugPrintfExtInst,
78 };
79 
80 // Container class for a single instance of a SPIR-V stream, with methods for disassembly.
81 class SpirvStream {
82 public:
SpirvStream(std::ostream & out,const std::vector<unsigned int> & stream)83     SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
~SpirvStream()84     virtual ~SpirvStream() { }
85 
86     void validate();
87     void processInstructions();
88 
89 protected:
90     SpirvStream(const SpirvStream&);
91     SpirvStream& operator=(const SpirvStream&);
getOpCode(int id) const92     Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
93 
94     // Output methods
95     void outputIndent();
96     void formatId(Id id, std::stringstream&);
97     void outputResultId(Id id);
98     void outputTypeId(Id id);
99     void outputId(Id id);
100     void outputMask(OperandClass operandClass, unsigned mask);
101     void disassembleImmediates(int numOperands);
102     void disassembleIds(int numOperands);
103     int disassembleString();
104     void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
105 
106     // Data
107     std::ostream& out;                       // where to write the disassembly
108     const std::vector<unsigned int>& stream; // the actual word stream
109     int size;                                // the size of the word stream
110     int word;                                // the next word of the stream to read
111 
112     // map each <id> to the instruction that created it
113     Id bound;
114     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)
115 
116     std::vector<std::string> idDescriptor;    // the best text string known for explaining the <id>
117 
118     // schema
119     unsigned int schema;
120 
121     // stack of structured-merge points
122     std::stack<Id> nestedControl;
123     Id nextNestedControl;         // need a slight delay for when we are nested
124 };
125 
validate()126 void SpirvStream::validate()
127 {
128     size = (int)stream.size();
129     if (size < 4)
130         Kill(out, "stream is too short");
131 
132     // Magic number
133     if (stream[word++] != MagicNumber) {
134         out << "Bad magic number";
135         return;
136     }
137 
138     // Version
139     out << "// Module Version " << std::hex << stream[word++] << std::endl;
140 
141     // Generator's magic number
142     out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
143 
144     // Result <id> bound
145     bound = stream[word++];
146     idInstruction.resize(bound);
147     idDescriptor.resize(bound);
148     out << "// Id's are bound by " << bound << std::endl;
149     out << std::endl;
150 
151     // Reserved schema, must be 0 for now
152     schema = stream[word++];
153     if (schema != 0)
154         Kill(out, "bad schema, must be 0");
155 }
156 
157 // Loop over all the instructions, in order, processing each.
158 // Boiler plate for each is handled here directly, the rest is dispatched.
processInstructions()159 void SpirvStream::processInstructions()
160 {
161     // Instructions
162     while (word < size) {
163         int instructionStart = word;
164 
165         // Instruction wordCount and opcode
166         unsigned int firstWord = stream[word];
167         unsigned wordCount = firstWord >> WordCountShift;
168         Op opCode = (Op)(firstWord & OpCodeMask);
169         int nextInst = word + wordCount;
170         ++word;
171 
172         // Presence of full instruction
173         if (nextInst > size)
174             Kill(out, "stream instruction terminated too early");
175 
176         // Base for computing number of operands; will be updated as more is learned
177         unsigned numOperands = wordCount - 1;
178 
179         // Type <id>
180         Id typeId = 0;
181         if (InstructionDesc[opCode].hasType()) {
182             typeId = stream[word++];
183             --numOperands;
184         }
185 
186         // Result <id>
187         Id resultId = 0;
188         if (InstructionDesc[opCode].hasResult()) {
189             resultId = stream[word++];
190             --numOperands;
191 
192             // save instruction for future reference
193             idInstruction[resultId] = instructionStart;
194         }
195 
196         outputResultId(resultId);
197         outputTypeId(typeId);
198         outputIndent();
199 
200         // Hand off the Op and all its operands
201         disassembleInstruction(resultId, typeId, opCode, numOperands);
202         if (word != nextInst) {
203             out << " ERROR, incorrect number of operands consumed.  At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
204             word = nextInst;
205         }
206         out << std::endl;
207     }
208 }
209 
outputIndent()210 void SpirvStream::outputIndent()
211 {
212     for (int i = 0; i < (int)nestedControl.size(); ++i)
213         out << "  ";
214 }
215 
formatId(Id id,std::stringstream & idStream)216 void SpirvStream::formatId(Id id, std::stringstream& idStream)
217 {
218     if (id != 0) {
219         // On instructions with no IDs, this is called with "0", which does not
220         // have to be within ID bounds on null shaders.
221         if (id >= bound)
222             Kill(out, "Bad <id>");
223 
224         idStream << id;
225         if (idDescriptor[id].size() > 0)
226             idStream << "(" << idDescriptor[id] << ")";
227     }
228 }
229 
outputResultId(Id id)230 void SpirvStream::outputResultId(Id id)
231 {
232     const int width = 16;
233     std::stringstream idStream;
234     formatId(id, idStream);
235     out << std::setw(width) << std::right << idStream.str();
236     if (id != 0)
237         out << ":";
238     else
239         out << " ";
240 
241     if (nestedControl.size() && id == nestedControl.top())
242         nestedControl.pop();
243 }
244 
outputTypeId(Id id)245 void SpirvStream::outputTypeId(Id id)
246 {
247     const int width = 12;
248     std::stringstream idStream;
249     formatId(id, idStream);
250     out << std::setw(width) << std::right << idStream.str() << " ";
251 }
252 
outputId(Id id)253 void SpirvStream::outputId(Id id)
254 {
255     if (id >= bound)
256         Kill(out, "Bad <id>");
257 
258     out << id;
259     if (idDescriptor[id].size() > 0)
260         out << "(" << idDescriptor[id] << ")";
261 }
262 
outputMask(OperandClass operandClass,unsigned mask)263 void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
264 {
265     if (mask == 0)
266         out << "None";
267     else {
268         for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
269             if (mask & (1 << m))
270                 out << OperandClassParams[operandClass].getName(m) << " ";
271         }
272     }
273 }
274 
disassembleImmediates(int numOperands)275 void SpirvStream::disassembleImmediates(int numOperands)
276 {
277     for (int i = 0; i < numOperands; ++i) {
278         out << stream[word++];
279         if (i < numOperands - 1)
280             out << " ";
281     }
282 }
283 
disassembleIds(int numOperands)284 void SpirvStream::disassembleIds(int numOperands)
285 {
286     for (int i = 0; i < numOperands; ++i) {
287         outputId(stream[word++]);
288         if (i < numOperands - 1)
289             out << " ";
290     }
291 }
292 
293 // return the number of operands consumed by the string
disassembleString()294 int SpirvStream::disassembleString()
295 {
296     int startWord = word;
297 
298     out << " \"";
299 
300     const char* wordString;
301     bool done = false;
302     do {
303         unsigned int content = stream[word];
304         wordString = (const char*)&content;
305         for (int charCount = 0; charCount < 4; ++charCount) {
306             if (*wordString == 0) {
307                 done = true;
308                 break;
309             }
310             out << *(wordString++);
311         }
312         ++word;
313     } while (! done);
314 
315     out << "\"";
316 
317     return word - startWord;
318 }
319 
disassembleInstruction(Id resultId,Id,Op opCode,int numOperands)320 void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
321 {
322     // Process the opcode
323 
324     out << (OpcodeString(opCode) + 2);  // leave out the "Op"
325 
326     if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
327         nextNestedControl = stream[word];
328     else if (opCode == OpBranchConditional || opCode == OpSwitch) {
329         if (nextNestedControl) {
330             nestedControl.push(nextNestedControl);
331             nextNestedControl = 0;
332         }
333     } else if (opCode == OpExtInstImport) {
334         idDescriptor[resultId] = (const char*)(&stream[word]);
335     }
336     else {
337         if (resultId != 0 && idDescriptor[resultId].size() == 0) {
338             switch (opCode) {
339             case OpTypeInt:
340                 switch (stream[word]) {
341                 case 8:  idDescriptor[resultId] = "int8_t"; break;
342                 case 16: idDescriptor[resultId] = "int16_t"; break;
343                 default: assert(0); // fallthrough
344                 case 32: idDescriptor[resultId] = "int"; break;
345                 case 64: idDescriptor[resultId] = "int64_t"; break;
346                 }
347                 break;
348             case OpTypeFloat:
349                 switch (stream[word]) {
350                 case 16: idDescriptor[resultId] = "float16_t"; break;
351                 default: assert(0); // fallthrough
352                 case 32: idDescriptor[resultId] = "float"; break;
353                 case 64: idDescriptor[resultId] = "float64_t"; break;
354                 }
355                 break;
356             case OpTypeBool:
357                 idDescriptor[resultId] = "bool";
358                 break;
359             case OpTypeStruct:
360                 idDescriptor[resultId] = "struct";
361                 break;
362             case OpTypePointer:
363                 idDescriptor[resultId] = "ptr";
364                 break;
365             case OpTypeVector:
366                 if (idDescriptor[stream[word]].size() > 0) {
367                     idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
368                     if (strstr(idDescriptor[stream[word]].c_str(), "8")) {
369                         idDescriptor[resultId].append("8");
370                     }
371                     if (strstr(idDescriptor[stream[word]].c_str(), "16")) {
372                         idDescriptor[resultId].append("16");
373                     }
374                     if (strstr(idDescriptor[stream[word]].c_str(), "64")) {
375                         idDescriptor[resultId].append("64");
376                     }
377                 }
378                 idDescriptor[resultId].append("vec");
379                 switch (stream[word + 1]) {
380                 case 2:   idDescriptor[resultId].append("2");   break;
381                 case 3:   idDescriptor[resultId].append("3");   break;
382                 case 4:   idDescriptor[resultId].append("4");   break;
383                 case 8:   idDescriptor[resultId].append("8");   break;
384                 case 16:  idDescriptor[resultId].append("16");  break;
385                 case 32:  idDescriptor[resultId].append("32");  break;
386                 default: break;
387                 }
388                 break;
389             default:
390                 break;
391             }
392         }
393     }
394 
395     // Process the operands.  Note, a new context-dependent set could be
396     // swapped in mid-traversal.
397 
398     // Handle images specially, so can put out helpful strings.
399     if (opCode == OpTypeImage) {
400         out << " ";
401         disassembleIds(1);
402         out << " " << DimensionString((Dim)stream[word++]);
403         out << (stream[word++] != 0 ? " depth" : "");
404         out << (stream[word++] != 0 ? " array" : "");
405         out << (stream[word++] != 0 ? " multi-sampled" : "");
406         switch (stream[word++]) {
407         case 0: out << " runtime";    break;
408         case 1: out << " sampled";    break;
409         case 2: out << " nonsampled"; break;
410         }
411         out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
412 
413         if (numOperands == 8) {
414             out << " " << AccessQualifierString(stream[word++]);
415         }
416         return;
417     }
418 
419     // Handle all the parameterized operands
420     for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
421         out << " ";
422         OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
423         switch (operandClass) {
424         case OperandId:
425         case OperandScope:
426         case OperandMemorySemantics:
427             disassembleIds(1);
428             --numOperands;
429             // Get names for printing "(XXX)" for readability, *after* this id
430             if (opCode == OpName)
431                 idDescriptor[stream[word - 1]] = (const char*)(&stream[word]);
432             break;
433         case OperandVariableIds:
434             disassembleIds(numOperands);
435             return;
436         case OperandImageOperands:
437             outputMask(OperandImageOperands, stream[word++]);
438             --numOperands;
439             disassembleIds(numOperands);
440             return;
441         case OperandOptionalLiteral:
442         case OperandVariableLiterals:
443             if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
444                 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
445                 out << BuiltInString(stream[word++]);
446                 --numOperands;
447                 ++op;
448             }
449             disassembleImmediates(numOperands);
450             return;
451         case OperandVariableIdLiteral:
452             while (numOperands > 0) {
453                 out << std::endl;
454                 outputResultId(0);
455                 outputTypeId(0);
456                 outputIndent();
457                 out << "     Type ";
458                 disassembleIds(1);
459                 out << ", member ";
460                 disassembleImmediates(1);
461                 numOperands -= 2;
462             }
463             return;
464         case OperandVariableLiteralId:
465             while (numOperands > 0) {
466                 out << std::endl;
467                 outputResultId(0);
468                 outputTypeId(0);
469                 outputIndent();
470                 out << "     case ";
471                 disassembleImmediates(1);
472                 out << ": ";
473                 disassembleIds(1);
474                 numOperands -= 2;
475             }
476             return;
477         case OperandLiteralNumber:
478             disassembleImmediates(1);
479             --numOperands;
480             if (opCode == OpExtInst) {
481                 ExtInstSet extInstSet = GLSL450Inst;
482                 const char* name = idDescriptor[stream[word - 2]].c_str();
483                 if (strcmp("OpenCL.std", name) == 0) {
484                     extInstSet = OpenCLExtInst;
485                 } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) {
486                     extInstSet = OpenCLExtInst;
487                 } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) {
488                     extInstSet = NonSemanticDebugPrintfExtInst;
489                 } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
490                            strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
491                            strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
492                            strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
493                     extInstSet = GLSLextAMDInst;
494                 } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
495                           strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
496                           strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
497                           strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 ||
498                           strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
499                           strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) {
500                     extInstSet = GLSLextNVInst;
501                 }
502                 unsigned entrypoint = stream[word - 1];
503                 if (extInstSet == GLSL450Inst) {
504                     if (entrypoint < GLSLstd450Count) {
505                         out << "(" << GlslStd450DebugNames[entrypoint] << ")";
506                     }
507                 } else if (extInstSet == GLSLextAMDInst) {
508                     out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
509                 }
510                 else if (extInstSet == GLSLextNVInst) {
511                     out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
512                 } else if (extInstSet == NonSemanticDebugPrintfExtInst) {
513                     out << "(DebugPrintf)";
514                 }
515             }
516             break;
517         case OperandOptionalLiteralString:
518         case OperandLiteralString:
519             numOperands -= disassembleString();
520             break;
521         case OperandVariableLiteralStrings:
522             while (numOperands > 0)
523                 numOperands -= disassembleString();
524             return;
525         case OperandMemoryAccess:
526             outputMask(OperandMemoryAccess, stream[word++]);
527             --numOperands;
528             // Aligned is the only memory access operand that uses an immediate
529             // value, and it is also the first operand that uses a value at all.
530             if (stream[word-1] & MemoryAccessAlignedMask) {
531                 disassembleImmediates(1);
532                 numOperands--;
533                 if (numOperands)
534                     out << " ";
535             }
536             disassembleIds(numOperands);
537             return;
538         default:
539             assert(operandClass >= OperandSource && operandClass < OperandOpcode);
540 
541             if (OperandClassParams[operandClass].bitmask)
542                 outputMask(operandClass, stream[word++]);
543             else
544                 out << OperandClassParams[operandClass].getName(stream[word++]);
545             --numOperands;
546 
547             break;
548         }
549     }
550 
551     return;
552 }
553 
GLSLstd450GetDebugNames(const char ** names)554 static void GLSLstd450GetDebugNames(const char** names)
555 {
556     for (int i = 0; i < GLSLstd450Count; ++i)
557         names[i] = "Unknown";
558 
559     names[GLSLstd450Round]                   = "Round";
560     names[GLSLstd450RoundEven]               = "RoundEven";
561     names[GLSLstd450Trunc]                   = "Trunc";
562     names[GLSLstd450FAbs]                    = "FAbs";
563     names[GLSLstd450SAbs]                    = "SAbs";
564     names[GLSLstd450FSign]                   = "FSign";
565     names[GLSLstd450SSign]                   = "SSign";
566     names[GLSLstd450Floor]                   = "Floor";
567     names[GLSLstd450Ceil]                    = "Ceil";
568     names[GLSLstd450Fract]                   = "Fract";
569     names[GLSLstd450Radians]                 = "Radians";
570     names[GLSLstd450Degrees]                 = "Degrees";
571     names[GLSLstd450Sin]                     = "Sin";
572     names[GLSLstd450Cos]                     = "Cos";
573     names[GLSLstd450Tan]                     = "Tan";
574     names[GLSLstd450Asin]                    = "Asin";
575     names[GLSLstd450Acos]                    = "Acos";
576     names[GLSLstd450Atan]                    = "Atan";
577     names[GLSLstd450Sinh]                    = "Sinh";
578     names[GLSLstd450Cosh]                    = "Cosh";
579     names[GLSLstd450Tanh]                    = "Tanh";
580     names[GLSLstd450Asinh]                   = "Asinh";
581     names[GLSLstd450Acosh]                   = "Acosh";
582     names[GLSLstd450Atanh]                   = "Atanh";
583     names[GLSLstd450Atan2]                   = "Atan2";
584     names[GLSLstd450Pow]                     = "Pow";
585     names[GLSLstd450Exp]                     = "Exp";
586     names[GLSLstd450Log]                     = "Log";
587     names[GLSLstd450Exp2]                    = "Exp2";
588     names[GLSLstd450Log2]                    = "Log2";
589     names[GLSLstd450Sqrt]                    = "Sqrt";
590     names[GLSLstd450InverseSqrt]             = "InverseSqrt";
591     names[GLSLstd450Determinant]             = "Determinant";
592     names[GLSLstd450MatrixInverse]           = "MatrixInverse";
593     names[GLSLstd450Modf]                    = "Modf";
594     names[GLSLstd450ModfStruct]              = "ModfStruct";
595     names[GLSLstd450FMin]                    = "FMin";
596     names[GLSLstd450SMin]                    = "SMin";
597     names[GLSLstd450UMin]                    = "UMin";
598     names[GLSLstd450FMax]                    = "FMax";
599     names[GLSLstd450SMax]                    = "SMax";
600     names[GLSLstd450UMax]                    = "UMax";
601     names[GLSLstd450FClamp]                  = "FClamp";
602     names[GLSLstd450SClamp]                  = "SClamp";
603     names[GLSLstd450UClamp]                  = "UClamp";
604     names[GLSLstd450FMix]                    = "FMix";
605     names[GLSLstd450Step]                    = "Step";
606     names[GLSLstd450SmoothStep]              = "SmoothStep";
607     names[GLSLstd450Fma]                     = "Fma";
608     names[GLSLstd450Frexp]                   = "Frexp";
609     names[GLSLstd450FrexpStruct]             = "FrexpStruct";
610     names[GLSLstd450Ldexp]                   = "Ldexp";
611     names[GLSLstd450PackSnorm4x8]            = "PackSnorm4x8";
612     names[GLSLstd450PackUnorm4x8]            = "PackUnorm4x8";
613     names[GLSLstd450PackSnorm2x16]           = "PackSnorm2x16";
614     names[GLSLstd450PackUnorm2x16]           = "PackUnorm2x16";
615     names[GLSLstd450PackHalf2x16]            = "PackHalf2x16";
616     names[GLSLstd450PackDouble2x32]          = "PackDouble2x32";
617     names[GLSLstd450UnpackSnorm2x16]         = "UnpackSnorm2x16";
618     names[GLSLstd450UnpackUnorm2x16]         = "UnpackUnorm2x16";
619     names[GLSLstd450UnpackHalf2x16]          = "UnpackHalf2x16";
620     names[GLSLstd450UnpackSnorm4x8]          = "UnpackSnorm4x8";
621     names[GLSLstd450UnpackUnorm4x8]          = "UnpackUnorm4x8";
622     names[GLSLstd450UnpackDouble2x32]        = "UnpackDouble2x32";
623     names[GLSLstd450Length]                  = "Length";
624     names[GLSLstd450Distance]                = "Distance";
625     names[GLSLstd450Cross]                   = "Cross";
626     names[GLSLstd450Normalize]               = "Normalize";
627     names[GLSLstd450FaceForward]             = "FaceForward";
628     names[GLSLstd450Reflect]                 = "Reflect";
629     names[GLSLstd450Refract]                 = "Refract";
630     names[GLSLstd450FindILsb]                = "FindILsb";
631     names[GLSLstd450FindSMsb]                = "FindSMsb";
632     names[GLSLstd450FindUMsb]                = "FindUMsb";
633     names[GLSLstd450InterpolateAtCentroid]   = "InterpolateAtCentroid";
634     names[GLSLstd450InterpolateAtSample]     = "InterpolateAtSample";
635     names[GLSLstd450InterpolateAtOffset]     = "InterpolateAtOffset";
636     names[GLSLstd450NMin]                    = "NMin";
637     names[GLSLstd450NMax]                    = "NMax";
638     names[GLSLstd450NClamp]                  = "NClamp";
639 }
640 
GLSLextAMDGetDebugNames(const char * name,unsigned entrypoint)641 static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
642 {
643     if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
644         switch (entrypoint) {
645         case SwizzleInvocationsAMD:         return "SwizzleInvocationsAMD";
646         case SwizzleInvocationsMaskedAMD:   return "SwizzleInvocationsMaskedAMD";
647         case WriteInvocationAMD:            return "WriteInvocationAMD";
648         case MbcntAMD:                      return "MbcntAMD";
649         default:                            return "Bad";
650         }
651     } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
652         switch (entrypoint) {
653         case FMin3AMD:      return "FMin3AMD";
654         case UMin3AMD:      return "UMin3AMD";
655         case SMin3AMD:      return "SMin3AMD";
656         case FMax3AMD:      return "FMax3AMD";
657         case UMax3AMD:      return "UMax3AMD";
658         case SMax3AMD:      return "SMax3AMD";
659         case FMid3AMD:      return "FMid3AMD";
660         case UMid3AMD:      return "UMid3AMD";
661         case SMid3AMD:      return "SMid3AMD";
662         default:            return "Bad";
663         }
664     } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
665         switch (entrypoint) {
666         case InterpolateAtVertexAMD:    return "InterpolateAtVertexAMD";
667         default:                        return "Bad";
668         }
669     }
670     else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
671         switch (entrypoint) {
672         case CubeFaceIndexAMD:      return "CubeFaceIndexAMD";
673         case CubeFaceCoordAMD:      return "CubeFaceCoordAMD";
674         case TimeAMD:               return "TimeAMD";
675         default:
676             break;
677         }
678     }
679 
680     return "Bad";
681 }
682 
GLSLextNVGetDebugNames(const char * name,unsigned entrypoint)683 static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
684 {
685     if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
686         strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
687         strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
688         strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
689         strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 ||
690         strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 ||
691         strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 ||
692         strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) {
693         switch (entrypoint) {
694         // NV builtins
695         case BuiltInViewportMaskNV:                 return "ViewportMaskNV";
696         case BuiltInSecondaryPositionNV:            return "SecondaryPositionNV";
697         case BuiltInSecondaryViewportMaskNV:        return "SecondaryViewportMaskNV";
698         case BuiltInPositionPerViewNV:              return "PositionPerViewNV";
699         case BuiltInViewportMaskPerViewNV:          return "ViewportMaskPerViewNV";
700         case BuiltInBaryCoordNV:                    return "BaryCoordNV";
701         case BuiltInBaryCoordNoPerspNV:             return "BaryCoordNoPerspNV";
702         case BuiltInTaskCountNV:                    return "TaskCountNV";
703         case BuiltInPrimitiveCountNV:               return "PrimitiveCountNV";
704         case BuiltInPrimitiveIndicesNV:             return "PrimitiveIndicesNV";
705         case BuiltInClipDistancePerViewNV:          return "ClipDistancePerViewNV";
706         case BuiltInCullDistancePerViewNV:          return "CullDistancePerViewNV";
707         case BuiltInLayerPerViewNV:                 return "LayerPerViewNV";
708         case BuiltInMeshViewCountNV:                return "MeshViewCountNV";
709         case BuiltInMeshViewIndicesNV:              return "MeshViewIndicesNV";
710 
711         // NV Capabilities
712         case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
713         case CapabilityShaderViewportMaskNV:        return "ShaderViewportMaskNV";
714         case CapabilityShaderStereoViewNV:          return "ShaderStereoViewNV";
715         case CapabilityPerViewAttributesNV:         return "PerViewAttributesNV";
716         case CapabilityFragmentBarycentricNV:       return "FragmentBarycentricNV";
717         case CapabilityMeshShadingNV:               return "MeshShadingNV";
718         case CapabilityImageFootprintNV:            return "ImageFootprintNV";
719         case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
720 
721         // NV Decorations
722         case DecorationOverrideCoverageNV:          return "OverrideCoverageNV";
723         case DecorationPassthroughNV:               return "PassthroughNV";
724         case DecorationViewportRelativeNV:          return "ViewportRelativeNV";
725         case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
726         case DecorationPerVertexNV:                 return "PerVertexNV";
727         case DecorationPerPrimitiveNV:              return "PerPrimitiveNV";
728         case DecorationPerViewNV:                   return "PerViewNV";
729         case DecorationPerTaskNV:                   return "PerTaskNV";
730 
731         default:                                    return "Bad";
732         }
733     }
734     return "Bad";
735 }
736 
Disassemble(std::ostream & out,const std::vector<unsigned int> & stream)737 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
738 {
739     SpirvStream SpirvStream(out, stream);
740     spv::Parameterize();
741     GLSLstd450GetDebugNames(GlslStd450DebugNames);
742     SpirvStream.validate();
743     SpirvStream.processInstructions();
744 }
745 
746 }; // end namespace spv
747