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