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