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