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#include <utility>
47
48#include "disassemble.h"
49#include "doc.h"
50
51namespace 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}
59const char* GlslStd450DebugNames[spv::GLSLstd450Count];
60
61namespace spv {
62
63static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
64static const char* GLSLextNVGetDebugNames(const char*, unsigned);
65
66static void Kill(std::ostream& out, const char* message)
67{
68 out << std::endl << "Disassembly failed: " << message << std::endl;
69 exit(status: 1);
70}
71
72// used to identify the extended instruction library imported when printing
73enum 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.
82class SpirvStream {
83public:
84 SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
85 virtual ~SpirvStream() { }
86
87 void validate();
88 void processInstructions();
89
90protected:
91 SpirvStream(const SpirvStream&);
92 SpirvStream& operator=(const SpirvStream&);
93 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 std::pair<int, std::string> decodeString();
105 int disassembleString();
106 void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
107
108 // Data
109 std::ostream& out; // where to write the disassembly
110 const std::vector<unsigned int>& stream; // the actual word stream
111 int size; // the size of the word stream
112 int word; // the next word of the stream to read
113
114 // map each <id> to the instruction that created it
115 Id bound;
116 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)
117
118 std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
119
120 // schema
121 unsigned int schema;
122
123 // stack of structured-merge points
124 std::stack<Id> nestedControl;
125 Id nextNestedControl; // need a slight delay for when we are nested
126};
127
128void SpirvStream::validate()
129{
130 size = (int)stream.size();
131 if (size < 4)
132 Kill(out, message: "stream is too short");
133
134 // Magic number
135 if (stream[word++] != MagicNumber) {
136 out << "Bad magic number";
137 return;
138 }
139
140 // Version
141 out << "// Module Version " << std::hex << stream[word++] << std::endl;
142
143 // Generator's magic number
144 out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
145
146 // Result <id> bound
147 bound = stream[word++];
148 idInstruction.resize(new_size: bound);
149 idDescriptor.resize(new_size: bound);
150 out << "// Id's are bound by " << bound << std::endl;
151 out << std::endl;
152
153 // Reserved schema, must be 0 for now
154 schema = stream[word++];
155 if (schema != 0)
156 Kill(out, message: "bad schema, must be 0");
157}
158
159// Loop over all the instructions, in order, processing each.
160// Boiler plate for each is handled here directly, the rest is dispatched.
161void SpirvStream::processInstructions()
162{
163 // Instructions
164 while (word < size) {
165 int instructionStart = word;
166
167 // Instruction wordCount and opcode
168 unsigned int firstWord = stream[word];
169 unsigned wordCount = firstWord >> WordCountShift;
170 Op opCode = (Op)(firstWord & OpCodeMask);
171 int nextInst = word + wordCount;
172 ++word;
173
174 // Presence of full instruction
175 if (nextInst > size)
176 Kill(out, message: "stream instruction terminated too early");
177
178 // Base for computing number of operands; will be updated as more is learned
179 unsigned numOperands = wordCount - 1;
180
181 // Type <id>
182 Id typeId = 0;
183 if (InstructionDesc[opCode].hasType()) {
184 typeId = stream[word++];
185 --numOperands;
186 }
187
188 // Result <id>
189 Id resultId = 0;
190 if (InstructionDesc[opCode].hasResult()) {
191 resultId = stream[word++];
192 --numOperands;
193
194 // save instruction for future reference
195 idInstruction[resultId] = instructionStart;
196 }
197
198 outputResultId(id: resultId);
199 outputTypeId(id: typeId);
200 outputIndent();
201
202 // Hand off the Op and all its operands
203 disassembleInstruction(resultId, typeId, opCode, numOperands);
204 if (word != nextInst) {
205 out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
206 word = nextInst;
207 }
208 out << std::endl;
209 }
210}
211
212void SpirvStream::outputIndent()
213{
214 for (int i = 0; i < (int)nestedControl.size(); ++i)
215 out << " ";
216}
217
218void SpirvStream::formatId(Id id, std::stringstream& idStream)
219{
220 if (id != 0) {
221 // On instructions with no IDs, this is called with "0", which does not
222 // have to be within ID bounds on null shaders.
223 if (id >= bound)
224 Kill(out, message: "Bad <id>");
225
226 idStream << id;
227 if (idDescriptor[id].size() > 0)
228 idStream << "(" << idDescriptor[id] << ")";
229 }
230}
231
232void SpirvStream::outputResultId(Id id)
233{
234 const int width = 16;
235 std::stringstream idStream;
236 formatId(id, idStream);
237 out << std::setw(width) << std::right << idStream.str();
238 if (id != 0)
239 out << ":";
240 else
241 out << " ";
242
243 if (nestedControl.size() && id == nestedControl.top())
244 nestedControl.pop();
245}
246
247void SpirvStream::outputTypeId(Id id)
248{
249 const int width = 12;
250 std::stringstream idStream;
251 formatId(id, idStream);
252 out << std::setw(width) << std::right << idStream.str() << " ";
253}
254
255void SpirvStream::outputId(Id id)
256{
257 if (id >= bound)
258 Kill(out, message: "Bad <id>");
259
260 out << id;
261 if (idDescriptor[id].size() > 0)
262 out << "(" << idDescriptor[id] << ")";
263}
264
265void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
266{
267 if (mask == 0)
268 out << "None";
269 else {
270 for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
271 if (mask & (1 << m))
272 out << OperandClassParams[operandClass].getName(m) << " ";
273 }
274 }
275}
276
277void SpirvStream::disassembleImmediates(int numOperands)
278{
279 for (int i = 0; i < numOperands; ++i) {
280 out << stream[word++];
281 if (i < numOperands - 1)
282 out << " ";
283 }
284}
285
286void SpirvStream::disassembleIds(int numOperands)
287{
288 for (int i = 0; i < numOperands; ++i) {
289 outputId(id: stream[word++]);
290 if (i < numOperands - 1)
291 out << " ";
292 }
293}
294
295// decode string from words at current position (non-consuming)
296std::pair<int, std::string> SpirvStream::decodeString()
297{
298 std::string res;
299 int wordPos = word;
300 char c;
301 bool done = false;
302
303 do {
304 unsigned int content = stream[wordPos];
305 for (int charCount = 0; charCount < 4; ++charCount) {
306 c = content & 0xff;
307 content >>= 8;
308 if (c == '\0') {
309 done = true;
310 break;
311 }
312 res += c;
313 }
314 ++wordPos;
315 } while(! done);
316
317 return std::make_pair(x: wordPos - word, y&: res);
318}
319
320// return the number of operands consumed by the string
321int SpirvStream::disassembleString()
322{
323 out << " \"";
324
325 std::pair<int, std::string> decoderes = decodeString();
326
327 out << decoderes.second;
328 out << "\"";
329
330 word += decoderes.first;
331
332 return decoderes.first;
333}
334
335void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
336{
337 // Process the opcode
338
339 out << (OpcodeString(opCode) + 2); // leave out the "Op"
340
341 if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
342 nextNestedControl = stream[word];
343 else if (opCode == OpBranchConditional || opCode == OpSwitch) {
344 if (nextNestedControl) {
345 nestedControl.push(x: nextNestedControl);
346 nextNestedControl = 0;
347 }
348 } else if (opCode == OpExtInstImport) {
349 idDescriptor[resultId] = decodeString().second;
350 }
351 else {
352 if (resultId != 0 && idDescriptor[resultId].size() == 0) {
353 switch (opCode) {
354 case OpTypeInt:
355 switch (stream[word]) {
356 case 8: idDescriptor[resultId] = "int8_t"; break;
357 case 16: idDescriptor[resultId] = "int16_t"; break;
358 default: assert(0); // fallthrough
359 case 32: idDescriptor[resultId] = "int"; break;
360 case 64: idDescriptor[resultId] = "int64_t"; break;
361 }
362 break;
363 case OpTypeFloat:
364 switch (stream[word]) {
365 case 16: idDescriptor[resultId] = "float16_t"; break;
366 default: assert(0); // fallthrough
367 case 32: idDescriptor[resultId] = "float"; break;
368 case 64: idDescriptor[resultId] = "float64_t"; break;
369 }
370 break;
371 case OpTypeBool:
372 idDescriptor[resultId] = "bool";
373 break;
374 case OpTypeStruct:
375 idDescriptor[resultId] = "struct";
376 break;
377 case OpTypePointer:
378 idDescriptor[resultId] = "ptr";
379 break;
380 case OpTypeVector:
381 if (idDescriptor[stream[word]].size() > 0) {
382 idDescriptor[resultId].append(first: idDescriptor[stream[word]].begin(), last: idDescriptor[stream[word]].begin() + 1);
383 if (strstr(haystack: idDescriptor[stream[word]].c_str(), needle: "8")) {
384 idDescriptor[resultId].append(s: "8");
385 }
386 if (strstr(haystack: idDescriptor[stream[word]].c_str(), needle: "16")) {
387 idDescriptor[resultId].append(s: "16");
388 }
389 if (strstr(haystack: idDescriptor[stream[word]].c_str(), needle: "64")) {
390 idDescriptor[resultId].append(s: "64");
391 }
392 }
393 idDescriptor[resultId].append(s: "vec");
394 switch (stream[word + 1]) {
395 case 2: idDescriptor[resultId].append(s: "2"); break;
396 case 3: idDescriptor[resultId].append(s: "3"); break;
397 case 4: idDescriptor[resultId].append(s: "4"); break;
398 case 8: idDescriptor[resultId].append(s: "8"); break;
399 case 16: idDescriptor[resultId].append(s: "16"); break;
400 case 32: idDescriptor[resultId].append(s: "32"); break;
401 default: break;
402 }
403 break;
404 default:
405 break;
406 }
407 }
408 }
409
410 // Process the operands. Note, a new context-dependent set could be
411 // swapped in mid-traversal.
412
413 // Handle images specially, so can put out helpful strings.
414 if (opCode == OpTypeImage) {
415 out << " ";
416 disassembleIds(numOperands: 1);
417 out << " " << DimensionString((Dim)stream[word++]);
418 out << (stream[word++] != 0 ? " depth" : "");
419 out << (stream[word++] != 0 ? " array" : "");
420 out << (stream[word++] != 0 ? " multi-sampled" : "");
421 switch (stream[word++]) {
422 case 0: out << " runtime"; break;
423 case 1: out << " sampled"; break;
424 case 2: out << " nonsampled"; break;
425 }
426 out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
427
428 if (numOperands == 8) {
429 out << " " << AccessQualifierString(attr: stream[word++]);
430 }
431 return;
432 }
433
434 // Handle all the parameterized operands
435 for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
436 out << " ";
437 OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
438 switch (operandClass) {
439 case OperandId:
440 case OperandScope:
441 case OperandMemorySemantics:
442 disassembleIds(numOperands: 1);
443 --numOperands;
444 // Get names for printing "(XXX)" for readability, *after* this id
445 if (opCode == OpName)
446 idDescriptor[stream[word - 1]] = decodeString().second;
447 break;
448 case OperandVariableIds:
449 disassembleIds(numOperands);
450 return;
451 case OperandImageOperands:
452 outputMask(operandClass: OperandImageOperands, mask: stream[word++]);
453 --numOperands;
454 disassembleIds(numOperands);
455 return;
456 case OperandOptionalLiteral:
457 case OperandVariableLiterals:
458 if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
459 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
460 out << BuiltInString(stream[word++]);
461 --numOperands;
462 ++op;
463 }
464 disassembleImmediates(numOperands);
465 return;
466 case OperandVariableIdLiteral:
467 while (numOperands > 0) {
468 out << std::endl;
469 outputResultId(id: 0);
470 outputTypeId(id: 0);
471 outputIndent();
472 out << " Type ";
473 disassembleIds(numOperands: 1);
474 out << ", member ";
475 disassembleImmediates(numOperands: 1);
476 numOperands -= 2;
477 }
478 return;
479 case OperandVariableLiteralId:
480 while (numOperands > 0) {
481 out << std::endl;
482 outputResultId(id: 0);
483 outputTypeId(id: 0);
484 outputIndent();
485 out << " case ";
486 disassembleImmediates(numOperands: 1);
487 out << ": ";
488 disassembleIds(numOperands: 1);
489 numOperands -= 2;
490 }
491 return;
492 case OperandLiteralNumber:
493 disassembleImmediates(numOperands: 1);
494 --numOperands;
495 if (opCode == OpExtInst) {
496 ExtInstSet extInstSet = GLSL450Inst;
497 const char* name = idDescriptor[stream[word - 2]].c_str();
498 if (strcmp(s1: "OpenCL.std", s2: name) == 0) {
499 extInstSet = OpenCLExtInst;
500 } else if (strcmp(s1: "OpenCL.DebugInfo.100", s2: name) == 0) {
501 extInstSet = OpenCLExtInst;
502 } else if (strcmp(s1: "NonSemantic.DebugPrintf", s2: name) == 0) {
503 extInstSet = NonSemanticDebugPrintfExtInst;
504 } else if (strcmp(s1: spv::E_SPV_AMD_shader_ballot, s2: name) == 0 ||
505 strcmp(s1: spv::E_SPV_AMD_shader_trinary_minmax, s2: name) == 0 ||
506 strcmp(s1: spv::E_SPV_AMD_shader_explicit_vertex_parameter, s2: name) == 0 ||
507 strcmp(s1: spv::E_SPV_AMD_gcn_shader, s2: name) == 0) {
508 extInstSet = GLSLextAMDInst;
509 } else if (strcmp(s1: spv::E_SPV_NV_sample_mask_override_coverage, s2: name) == 0 ||
510 strcmp(s1: spv::E_SPV_NV_geometry_shader_passthrough, s2: name) == 0 ||
511 strcmp(s1: spv::E_SPV_NV_viewport_array2, s2: name) == 0 ||
512 strcmp(s1: spv::E_SPV_NVX_multiview_per_view_attributes, s2: name) == 0 ||
513 strcmp(s1: spv::E_SPV_NV_fragment_shader_barycentric, s2: name) == 0 ||
514 strcmp(s1: spv::E_SPV_NV_mesh_shader, s2: name) == 0) {
515 extInstSet = GLSLextNVInst;
516 }
517 unsigned entrypoint = stream[word - 1];
518 if (extInstSet == GLSL450Inst) {
519 if (entrypoint < GLSLstd450Count) {
520 out << "(" << GlslStd450DebugNames[entrypoint] << ")";
521 }
522 } else if (extInstSet == GLSLextAMDInst) {
523 out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
524 }
525 else if (extInstSet == GLSLextNVInst) {
526 out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
527 } else if (extInstSet == NonSemanticDebugPrintfExtInst) {
528 out << "(DebugPrintf)";
529 }
530 }
531 break;
532 case OperandOptionalLiteralString:
533 case OperandLiteralString:
534 numOperands -= disassembleString();
535 break;
536 case OperandVariableLiteralStrings:
537 while (numOperands > 0)
538 numOperands -= disassembleString();
539 return;
540 case OperandMemoryAccess:
541 outputMask(operandClass: OperandMemoryAccess, mask: 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(numOperands: 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, mask: stream[word++]);
558 else
559 out << OperandClassParams[operandClass].getName(stream[word++]);
560 --numOperands;
561
562 break;
563 }
564 }
565
566 return;
567}
568
569static 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 names[GLSLstd450NMin] = "NMin";
652 names[GLSLstd450NMax] = "NMax";
653 names[GLSLstd450NClamp] = "NClamp";
654}
655
656static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
657{
658 if (strcmp(s1: name, s2: spv::E_SPV_AMD_shader_ballot) == 0) {
659 switch (entrypoint) {
660 case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD";
661 case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD";
662 case WriteInvocationAMD: return "WriteInvocationAMD";
663 case MbcntAMD: return "MbcntAMD";
664 default: return "Bad";
665 }
666 } else if (strcmp(s1: name, s2: spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
667 switch (entrypoint) {
668 case FMin3AMD: return "FMin3AMD";
669 case UMin3AMD: return "UMin3AMD";
670 case SMin3AMD: return "SMin3AMD";
671 case FMax3AMD: return "FMax3AMD";
672 case UMax3AMD: return "UMax3AMD";
673 case SMax3AMD: return "SMax3AMD";
674 case FMid3AMD: return "FMid3AMD";
675 case UMid3AMD: return "UMid3AMD";
676 case SMid3AMD: return "SMid3AMD";
677 default: return "Bad";
678 }
679 } else if (strcmp(s1: name, s2: spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
680 switch (entrypoint) {
681 case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD";
682 default: return "Bad";
683 }
684 }
685 else if (strcmp(s1: name, s2: spv::E_SPV_AMD_gcn_shader) == 0) {
686 switch (entrypoint) {
687 case CubeFaceIndexAMD: return "CubeFaceIndexAMD";
688 case CubeFaceCoordAMD: return "CubeFaceCoordAMD";
689 case TimeAMD: return "TimeAMD";
690 default:
691 break;
692 }
693 }
694
695 return "Bad";
696}
697
698static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
699{
700 if (strcmp(s1: name, s2: spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
701 strcmp(s1: name, s2: spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
702 strcmp(s1: name, s2: spv::E_ARB_shader_viewport_layer_array) == 0 ||
703 strcmp(s1: name, s2: spv::E_SPV_NV_viewport_array2) == 0 ||
704 strcmp(s1: name, s2: spv::E_SPV_NVX_multiview_per_view_attributes) == 0 ||
705 strcmp(s1: name, s2: spv::E_SPV_NV_fragment_shader_barycentric) == 0 ||
706 strcmp(s1: name, s2: spv::E_SPV_NV_mesh_shader) == 0 ||
707 strcmp(s1: name, s2: spv::E_SPV_NV_shader_image_footprint) == 0) {
708 switch (entrypoint) {
709 // NV builtins
710 case BuiltInViewportMaskNV: return "ViewportMaskNV";
711 case BuiltInSecondaryPositionNV: return "SecondaryPositionNV";
712 case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
713 case BuiltInPositionPerViewNV: return "PositionPerViewNV";
714 case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
715 case BuiltInBaryCoordNV: return "BaryCoordNV";
716 case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
717 case BuiltInTaskCountNV: return "TaskCountNV";
718 case BuiltInPrimitiveCountNV: return "PrimitiveCountNV";
719 case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV";
720 case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV";
721 case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV";
722 case BuiltInLayerPerViewNV: return "LayerPerViewNV";
723 case BuiltInMeshViewCountNV: return "MeshViewCountNV";
724 case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV";
725
726 // NV Capabilities
727 case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
728 case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV";
729 case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV";
730 case CapabilityPerViewAttributesNV: return "PerViewAttributesNV";
731 case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV";
732 case CapabilityMeshShadingNV: return "MeshShadingNV";
733 case CapabilityImageFootprintNV: return "ImageFootprintNV";
734 case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
735
736 // NV Decorations
737 case DecorationOverrideCoverageNV: return "OverrideCoverageNV";
738 case DecorationPassthroughNV: return "PassthroughNV";
739 case DecorationViewportRelativeNV: return "ViewportRelativeNV";
740 case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
741 case DecorationPerVertexNV: return "PerVertexNV";
742 case DecorationPerPrimitiveNV: return "PerPrimitiveNV";
743 case DecorationPerViewNV: return "PerViewNV";
744 case DecorationPerTaskNV: return "PerTaskNV";
745
746 default: return "Bad";
747 }
748 }
749 return "Bad";
750}
751
752void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
753{
754 SpirvStream SpirvStream(out, stream);
755 spv::Parameterize();
756 GLSLstd450GetDebugNames(names: GlslStd450DebugNames);
757 SpirvStream.validate();
758 SpirvStream.processInstructions();
759}
760
761}; // end namespace spv
762

source code of qtshadertools/src/3rdparty/glslang/SPIRV/disassemble.cpp