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