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