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