blob: 930e79949363afb51a7e399d0500c7b53b87a921 [file] [log] [blame]
John Kessenich140f3df2015-06-26 16:58:36 -06001//
John Kessenich927608b2017-01-06 12:34:14 -07002// Copyright (C) 2014-2015 LunarG, Inc.
John Kessenich140f3df2015-06-26 16:58:36 -06003//
John Kessenich927608b2017-01-06 12:34:14 -07004// All rights reserved.
John Kessenich140f3df2015-06-26 16:58:36 -06005//
John Kessenich927608b2017-01-06 12:34:14 -07006// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions
8// are met:
John Kessenich140f3df2015-06-26 16:58:36 -06009//
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//
John Kessenich927608b2017-01-06 12:34:14 -070022// 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.
John Kessenich140f3df2015-06-26 16:58:36 -060034
35//
John Kessenich140f3df2015-06-26 16:58:36 -060036// Disassembler for SPIR-V.
37//
38
John Kessenich66ec80e2016-08-05 14:04:23 -060039#include <cstdlib>
40#include <cstring>
41#include <cassert>
John Kessenich140f3df2015-06-26 16:58:36 -060042#include <iomanip>
43#include <stack>
44#include <sstream>
John Kessenich5e4b1242015-08-06 22:53:06 -060045#include <cstring>
John Kessenich140f3df2015-06-26 16:58:36 -060046
47#include "disassemble.h"
48#include "doc.h"
John Kessenich717c80a2018-08-23 15:17:10 -060049#include "SpvTools.h"
John Kessenich140f3df2015-06-26 16:58:36 -060050
51namespace spv {
Rex Xu9d93a232016-05-05 12:30:44 +080052 extern "C" {
53 // Include C-based headers that don't have a namespace
54 #include "GLSL.std.450.h"
Rex Xu9d93a232016-05-05 12:30:44 +080055 #include "GLSL.ext.AMD.h"
chaoc0ad6a4e2016-12-19 16:29:34 -080056 #include "GLSL.ext.NV.h"
Rex Xu9d93a232016-05-05 12:30:44 +080057 }
58}
59const char* GlslStd450DebugNames[spv::GLSLstd450Count];
60
61namespace spv {
62
Rex Xu9d93a232016-05-05 12:30:44 +080063static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
chaoc0ad6a4e2016-12-19 16:29:34 -080064static const char* GLSLextNVGetDebugNames(const char*, unsigned);
chaoc0ad6a4e2016-12-19 16:29:34 -080065
Mark Adams18b637f2016-02-23 12:17:11 -050066static void Kill(std::ostream& out, const char* message)
John Kessenich140f3df2015-06-26 16:58:36 -060067{
68 out << std::endl << "Disassembly failed: " << message << std::endl;
69 exit(1);
70}
71
John Kessenich5e4b1242015-08-06 22:53:06 -060072// used to identify the extended instruction library imported when printing
73enum ExtInstSet {
74 GLSL450Inst,
Rex Xu9d93a232016-05-05 12:30:44 +080075 GLSLextAMDInst,
chaoc0ad6a4e2016-12-19 16:29:34 -080076 GLSLextNVInst,
John Kessenich5e4b1242015-08-06 22:53:06 -060077 OpenCLExtInst,
78};
79
John Kessenich140f3df2015-06-26 16:58:36 -060080// Container class for a single instance of a SPIR-V stream, with methods for disassembly.
81class SpirvStream {
82public:
83 SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
84 virtual ~SpirvStream() { }
85
86 void validate();
87 void processInstructions();
88
89protected:
John Kessenich5e4b1242015-08-06 22:53:06 -060090 SpirvStream(const SpirvStream&);
91 SpirvStream& operator=(const SpirvStream&);
John Kessenich140f3df2015-06-26 16:58:36 -060092 Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
93
94 // Output methods
95 void outputIndent();
96 void formatId(Id id, std::stringstream&);
97 void outputResultId(Id id);
98 void outputTypeId(Id id);
99 void outputId(Id id);
John Kessenich5e4b1242015-08-06 22:53:06 -0600100 void outputMask(OperandClass operandClass, unsigned mask);
John Kessenich140f3df2015-06-26 16:58:36 -0600101 void disassembleImmediates(int numOperands);
102 void disassembleIds(int numOperands);
John Kessenich55e7d112015-11-15 21:33:39 -0700103 int disassembleString();
John Kessenich140f3df2015-06-26 16:58:36 -0600104 void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
105
106 // Data
107 std::ostream& out; // where to write the disassembly
108 const std::vector<unsigned int>& stream; // the actual word stream
109 int size; // the size of the word stream
110 int word; // the next word of the stream to read
111
112 // map each <id> to the instruction that created it
113 Id bound;
114 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)
115
116 std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
117
118 // schema
119 unsigned int schema;
120
121 // stack of structured-merge points
122 std::stack<Id> nestedControl;
123 Id nextNestedControl; // need a slight delay for when we are nested
124};
125
126void SpirvStream::validate()
127{
128 size = (int)stream.size();
129 if (size < 4)
130 Kill(out, "stream is too short");
131
132 // Magic number
133 if (stream[word++] != MagicNumber) {
134 out << "Bad magic number";
135 return;
136 }
137
138 // Version
John Kessenich55e7d112015-11-15 21:33:39 -0700139 out << "// Module Version " << std::hex << stream[word++] << std::endl;
John Kessenich140f3df2015-06-26 16:58:36 -0600140
141 // Generator's magic number
John Kessenich55e7d112015-11-15 21:33:39 -0700142 out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
John Kessenich140f3df2015-06-26 16:58:36 -0600143
144 // Result <id> bound
145 bound = stream[word++];
146 idInstruction.resize(bound);
147 idDescriptor.resize(bound);
148 out << "// Id's are bound by " << bound << std::endl;
149 out << std::endl;
150
151 // Reserved schema, must be 0 for now
152 schema = stream[word++];
153 if (schema != 0)
154 Kill(out, "bad schema, must be 0");
155}
156
157// Loop over all the instructions, in order, processing each.
158// Boiler plate for each is handled here directly, the rest is dispatched.
159void SpirvStream::processInstructions()
160{
161 // Instructions
162 while (word < size) {
163 int instructionStart = word;
164
165 // Instruction wordCount and opcode
166 unsigned int firstWord = stream[word];
167 unsigned wordCount = firstWord >> WordCountShift;
168 Op opCode = (Op)(firstWord & OpCodeMask);
169 int nextInst = word + wordCount;
170 ++word;
171
172 // Presence of full instruction
173 if (nextInst > size)
174 Kill(out, "stream instruction terminated too early");
175
176 // Base for computing number of operands; will be updated as more is learned
177 unsigned numOperands = wordCount - 1;
178
179 // Type <id>
180 Id typeId = 0;
181 if (InstructionDesc[opCode].hasType()) {
182 typeId = stream[word++];
183 --numOperands;
184 }
185
186 // Result <id>
187 Id resultId = 0;
188 if (InstructionDesc[opCode].hasResult()) {
189 resultId = stream[word++];
190 --numOperands;
191
192 // save instruction for future reference
193 idInstruction[resultId] = instructionStart;
194 }
195
196 outputResultId(resultId);
197 outputTypeId(typeId);
198 outputIndent();
199
200 // Hand off the Op and all its operands
201 disassembleInstruction(resultId, typeId, opCode, numOperands);
202 if (word != nextInst) {
203 out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
204 word = nextInst;
205 }
206 out << std::endl;
207 }
208}
209
210void SpirvStream::outputIndent()
211{
212 for (int i = 0; i < (int)nestedControl.size(); ++i)
213 out << " ";
214}
215
216void SpirvStream::formatId(Id id, std::stringstream& idStream)
217{
John Kessenich140f3df2015-06-26 16:58:36 -0600218 if (id != 0) {
steve-lunarga8456412016-08-17 16:18:06 -0600219 // On instructions with no IDs, this is called with "0", which does not
220 // have to be within ID bounds on null shaders.
221 if (id >= bound)
222 Kill(out, "Bad <id>");
223
John Kessenich140f3df2015-06-26 16:58:36 -0600224 idStream << id;
225 if (idDescriptor[id].size() > 0)
226 idStream << "(" << idDescriptor[id] << ")";
227 }
228}
229
230void SpirvStream::outputResultId(Id id)
231{
232 const int width = 16;
233 std::stringstream idStream;
234 formatId(id, idStream);
235 out << std::setw(width) << std::right << idStream.str();
236 if (id != 0)
237 out << ":";
238 else
239 out << " ";
240
241 if (nestedControl.size() && id == nestedControl.top())
242 nestedControl.pop();
243}
244
245void SpirvStream::outputTypeId(Id id)
246{
247 const int width = 12;
248 std::stringstream idStream;
249 formatId(id, idStream);
250 out << std::setw(width) << std::right << idStream.str() << " ";
251}
252
253void SpirvStream::outputId(Id id)
254{
255 if (id >= bound)
256 Kill(out, "Bad <id>");
257
258 out << id;
259 if (idDescriptor[id].size() > 0)
260 out << "(" << idDescriptor[id] << ")";
261}
262
John Kessenich5e4b1242015-08-06 22:53:06 -0600263void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
264{
265 if (mask == 0)
266 out << "None";
267 else {
268 for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
269 if (mask & (1 << m))
270 out << OperandClassParams[operandClass].getName(m) << " ";
271 }
272 }
273}
274
John Kessenich140f3df2015-06-26 16:58:36 -0600275void SpirvStream::disassembleImmediates(int numOperands)
276{
277 for (int i = 0; i < numOperands; ++i) {
278 out << stream[word++];
279 if (i < numOperands - 1)
280 out << " ";
281 }
282}
283
284void SpirvStream::disassembleIds(int numOperands)
285{
286 for (int i = 0; i < numOperands; ++i) {
287 outputId(stream[word++]);
288 if (i < numOperands - 1)
289 out << " ";
290 }
291}
292
John Kessenich55e7d112015-11-15 21:33:39 -0700293// return the number of operands consumed by the string
294int SpirvStream::disassembleString()
John Kessenich140f3df2015-06-26 16:58:36 -0600295{
John Kessenich55e7d112015-11-15 21:33:39 -0700296 int startWord = word;
297
John Kessenich140f3df2015-06-26 16:58:36 -0600298 out << " \"";
299
John Kessenich55e7d112015-11-15 21:33:39 -0700300 const char* wordString;
John Kessenich140f3df2015-06-26 16:58:36 -0600301 bool done = false;
302 do {
303 unsigned int content = stream[word];
John Kessenich55e7d112015-11-15 21:33:39 -0700304 wordString = (const char*)&content;
John Kessenich140f3df2015-06-26 16:58:36 -0600305 for (int charCount = 0; charCount < 4; ++charCount) {
306 if (*wordString == 0) {
307 done = true;
308 break;
309 }
310 out << *(wordString++);
311 }
312 ++word;
313 } while (! done);
314
315 out << "\"";
John Kessenich55e7d112015-11-15 21:33:39 -0700316
317 return word - startWord;
John Kessenich140f3df2015-06-26 16:58:36 -0600318}
319
320void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
321{
322 // Process the opcode
323
324 out << (OpcodeString(opCode) + 2); // leave out the "Op"
325
326 if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
327 nextNestedControl = stream[word];
328 else if (opCode == OpBranchConditional || opCode == OpSwitch) {
329 if (nextNestedControl) {
330 nestedControl.push(nextNestedControl);
331 nextNestedControl = 0;
332 }
John Kessenich5e4b1242015-08-06 22:53:06 -0600333 } else if (opCode == OpExtInstImport) {
Mark Adamsc11e95f2015-10-26 12:38:46 -0400334 idDescriptor[resultId] = (const char*)(&stream[word]);
John Kessenich5e4b1242015-08-06 22:53:06 -0600335 }
John Kessenich140f3df2015-06-26 16:58:36 -0600336 else {
steve-lunarga8456412016-08-17 16:18:06 -0600337 if (resultId != 0 && idDescriptor[resultId].size() == 0) {
John Kessenich140f3df2015-06-26 16:58:36 -0600338 switch (opCode) {
339 case OpTypeInt:
Jeff Bolzaf7a9482018-05-22 23:13:30 -0500340 switch (stream[word]) {
341 case 8: idDescriptor[resultId] = "int8_t"; break;
342 case 16: idDescriptor[resultId] = "int16_t"; break;
343 default: assert(0); // fallthrough
344 case 32: idDescriptor[resultId] = "int"; break;
345 case 64: idDescriptor[resultId] = "int64_t"; break;
346 }
John Kessenich140f3df2015-06-26 16:58:36 -0600347 break;
348 case OpTypeFloat:
Jeff Bolzaf7a9482018-05-22 23:13:30 -0500349 switch (stream[word]) {
350 case 16: idDescriptor[resultId] = "float16_t"; break;
351 default: assert(0); // fallthrough
352 case 32: idDescriptor[resultId] = "float"; break;
353 case 64: idDescriptor[resultId] = "float64_t"; break;
354 }
John Kessenich140f3df2015-06-26 16:58:36 -0600355 break;
356 case OpTypeBool:
357 idDescriptor[resultId] = "bool";
358 break;
359 case OpTypeStruct:
360 idDescriptor[resultId] = "struct";
361 break;
362 case OpTypePointer:
363 idDescriptor[resultId] = "ptr";
364 break;
365 case OpTypeVector:
Jeff Bolzaf7a9482018-05-22 23:13:30 -0500366 if (idDescriptor[stream[word]].size() > 0) {
John Kessenich140f3df2015-06-26 16:58:36 -0600367 idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
Jeff Bolzaf7a9482018-05-22 23:13:30 -0500368 if (strstr(idDescriptor[stream[word]].c_str(), "8")) {
369 idDescriptor[resultId].append("8");
370 }
371 if (strstr(idDescriptor[stream[word]].c_str(), "16")) {
372 idDescriptor[resultId].append("16");
373 }
374 if (strstr(idDescriptor[stream[word]].c_str(), "64")) {
375 idDescriptor[resultId].append("64");
376 }
377 }
John Kessenich140f3df2015-06-26 16:58:36 -0600378 idDescriptor[resultId].append("vec");
379 switch (stream[word + 1]) {
380 case 2: idDescriptor[resultId].append("2"); break;
381 case 3: idDescriptor[resultId].append("3"); break;
382 case 4: idDescriptor[resultId].append("4"); break;
383 case 8: idDescriptor[resultId].append("8"); break;
384 case 16: idDescriptor[resultId].append("16"); break;
385 case 32: idDescriptor[resultId].append("32"); break;
386 default: break;
387 }
388 break;
389 default:
390 break;
391 }
392 }
393 }
394
395 // Process the operands. Note, a new context-dependent set could be
396 // swapped in mid-traversal.
397
John Kessenich5e4b1242015-08-06 22:53:06 -0600398 // Handle images specially, so can put out helpful strings.
399 if (opCode == OpTypeImage) {
400 out << " ";
John Kessenich140f3df2015-06-26 16:58:36 -0600401 disassembleIds(1);
402 out << " " << DimensionString((Dim)stream[word++]);
John Kessenich140f3df2015-06-26 16:58:36 -0600403 out << (stream[word++] != 0 ? " depth" : "");
John Kessenich5e4b1242015-08-06 22:53:06 -0600404 out << (stream[word++] != 0 ? " array" : "");
John Kessenich140f3df2015-06-26 16:58:36 -0600405 out << (stream[word++] != 0 ? " multi-sampled" : "");
John Kessenich5e4b1242015-08-06 22:53:06 -0600406 switch (stream[word++]) {
407 case 0: out << " runtime"; break;
408 case 1: out << " sampled"; break;
409 case 2: out << " nonsampled"; break;
410 }
411 out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
412
413 if (numOperands == 8) {
414 out << " " << AccessQualifierString(stream[word++]);
415 }
John Kessenich140f3df2015-06-26 16:58:36 -0600416 return;
417 }
418
419 // Handle all the parameterized operands
John Kessenich5e4b1242015-08-06 22:53:06 -0600420 for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
John Kessenich140f3df2015-06-26 16:58:36 -0600421 out << " ";
422 OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
423 switch (operandClass) {
424 case OperandId:
John Kessenich5e4b1242015-08-06 22:53:06 -0600425 case OperandScope:
426 case OperandMemorySemantics:
John Kessenich140f3df2015-06-26 16:58:36 -0600427 disassembleIds(1);
John Kessenich55e7d112015-11-15 21:33:39 -0700428 --numOperands;
John Kessenich140f3df2015-06-26 16:58:36 -0600429 // Get names for printing "(XXX)" for readability, *after* this id
430 if (opCode == OpName)
Mark Adamsc11e95f2015-10-26 12:38:46 -0400431 idDescriptor[stream[word - 1]] = (const char*)(&stream[word]);
John Kessenich140f3df2015-06-26 16:58:36 -0600432 break;
John Kessenich140f3df2015-06-26 16:58:36 -0600433 case OperandVariableIds:
434 disassembleIds(numOperands);
435 return;
John Kessenich55e7d112015-11-15 21:33:39 -0700436 case OperandImageOperands:
John Kessenich71631272015-10-13 10:39:19 -0600437 outputMask(OperandImageOperands, stream[word++]);
John Kessenich5e4b1242015-08-06 22:53:06 -0600438 --numOperands;
439 disassembleIds(numOperands);
440 return;
441 case OperandOptionalLiteral:
John Kessenich140f3df2015-06-26 16:58:36 -0600442 case OperandVariableLiterals:
Mark Adamsc11e95f2015-10-26 12:38:46 -0400443 if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
444 (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600445 out << BuiltInString(stream[word++]);
446 --numOperands;
447 ++op;
448 }
449 disassembleImmediates(numOperands);
450 return;
John Kessenich5e4b1242015-08-06 22:53:06 -0600451 case OperandVariableIdLiteral:
452 while (numOperands > 0) {
453 out << std::endl;
454 outputResultId(0);
455 outputTypeId(0);
456 outputIndent();
457 out << " Type ";
458 disassembleIds(1);
459 out << ", member ";
460 disassembleImmediates(1);
461 numOperands -= 2;
462 }
463 return;
John Kessenich140f3df2015-06-26 16:58:36 -0600464 case OperandVariableLiteralId:
465 while (numOperands > 0) {
466 out << std::endl;
467 outputResultId(0);
468 outputTypeId(0);
469 outputIndent();
470 out << " case ";
471 disassembleImmediates(1);
472 out << ": ";
473 disassembleIds(1);
474 numOperands -= 2;
475 }
476 return;
477 case OperandLiteralNumber:
478 disassembleImmediates(1);
John Kessenich55e7d112015-11-15 21:33:39 -0700479 --numOperands;
John Kessenich140f3df2015-06-26 16:58:36 -0600480 if (opCode == OpExtInst) {
John Kessenich5e4b1242015-08-06 22:53:06 -0600481 ExtInstSet extInstSet = GLSL450Inst;
Rex Xu9d93a232016-05-05 12:30:44 +0800482 const char* name = idDescriptor[stream[word - 2]].c_str();
483 if (0 == memcmp("OpenCL", name, 6)) {
John Kessenich5e4b1242015-08-06 22:53:06 -0600484 extInstSet = OpenCLExtInst;
Rex Xu9d93a232016-05-05 12:30:44 +0800485 } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
486 strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
487 strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
488 strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
489 extInstSet = GLSLextAMDInst;
John Kessenich23d27752019-07-28 02:12:10 -0600490 } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
chaoc771d89f2017-01-13 01:10:53 -0800491 strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
chaocdf3956c2017-02-14 14:52:34 -0800492 strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
Chao Chen9eada4b2018-09-19 11:39:56 -0700493 strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 ||
Chao Chen3c366992018-09-19 11:41:59 -0700494 strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
495 strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) {
chaoc0ad6a4e2016-12-19 16:29:34 -0800496 extInstSet = GLSLextNVInst;
John Kessenich5e4b1242015-08-06 22:53:06 -0600497 }
John Kessenich140f3df2015-06-26 16:58:36 -0600498 unsigned entrypoint = stream[word - 1];
John Kessenich5e4b1242015-08-06 22:53:06 -0600499 if (extInstSet == GLSL450Inst) {
John Kessenich55e7d112015-11-15 21:33:39 -0700500 if (entrypoint < GLSLstd450Count) {
John Kessenich5e4b1242015-08-06 22:53:06 -0600501 out << "(" << GlslStd450DebugNames[entrypoint] << ")";
502 }
Rex Xu9d93a232016-05-05 12:30:44 +0800503 } else if (extInstSet == GLSLextAMDInst) {
504 out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
chaoc0ad6a4e2016-12-19 16:29:34 -0800505 }
506 else if (extInstSet == GLSLextNVInst) {
507 out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
John Kessenich5e4b1242015-08-06 22:53:06 -0600508 }
John Kessenich140f3df2015-06-26 16:58:36 -0600509 }
510 break;
John Kessenich55e7d112015-11-15 21:33:39 -0700511 case OperandOptionalLiteralString:
John Kessenich140f3df2015-06-26 16:58:36 -0600512 case OperandLiteralString:
John Kessenich55e7d112015-11-15 21:33:39 -0700513 numOperands -= disassembleString();
514 break;
Jeff Bolz36831c92018-09-05 10:11:41 -0500515 case OperandMemoryAccess:
516 outputMask(OperandMemoryAccess, stream[word++]);
517 --numOperands;
Jeff Bolz9f2aec42019-01-06 17:58:04 -0600518 // Aligned is the only memory access operand that uses an immediate
519 // value, and it is also the first operand that uses a value at all.
520 if (stream[word-1] & MemoryAccessAlignedMask) {
521 disassembleImmediates(1);
522 numOperands--;
523 if (numOperands)
524 out << " ";
525 }
Jeff Bolz36831c92018-09-05 10:11:41 -0500526 disassembleIds(numOperands);
527 return;
John Kessenich140f3df2015-06-26 16:58:36 -0600528 default:
529 assert(operandClass >= OperandSource && operandClass < OperandOpcode);
530
John Kessenich5e4b1242015-08-06 22:53:06 -0600531 if (OperandClassParams[operandClass].bitmask)
532 outputMask(operandClass, stream[word++]);
533 else
John Kessenich140f3df2015-06-26 16:58:36 -0600534 out << OperandClassParams[operandClass].getName(stream[word++]);
John Kessenich55e7d112015-11-15 21:33:39 -0700535 --numOperands;
John Kessenich6c292d32016-02-15 20:58:50 -0700536
John Kessenich140f3df2015-06-26 16:58:36 -0600537 break;
538 }
John Kessenich140f3df2015-06-26 16:58:36 -0600539 }
540
541 return;
542}
543
Mark Adams18b637f2016-02-23 12:17:11 -0500544static void GLSLstd450GetDebugNames(const char** names)
John Kessenich5e4b1242015-08-06 22:53:06 -0600545{
546 for (int i = 0; i < GLSLstd450Count; ++i)
547 names[i] = "Unknown";
548
549 names[GLSLstd450Round] = "Round";
550 names[GLSLstd450RoundEven] = "RoundEven";
551 names[GLSLstd450Trunc] = "Trunc";
552 names[GLSLstd450FAbs] = "FAbs";
553 names[GLSLstd450SAbs] = "SAbs";
554 names[GLSLstd450FSign] = "FSign";
555 names[GLSLstd450SSign] = "SSign";
556 names[GLSLstd450Floor] = "Floor";
557 names[GLSLstd450Ceil] = "Ceil";
558 names[GLSLstd450Fract] = "Fract";
559 names[GLSLstd450Radians] = "Radians";
560 names[GLSLstd450Degrees] = "Degrees";
561 names[GLSLstd450Sin] = "Sin";
562 names[GLSLstd450Cos] = "Cos";
563 names[GLSLstd450Tan] = "Tan";
564 names[GLSLstd450Asin] = "Asin";
565 names[GLSLstd450Acos] = "Acos";
566 names[GLSLstd450Atan] = "Atan";
567 names[GLSLstd450Sinh] = "Sinh";
568 names[GLSLstd450Cosh] = "Cosh";
569 names[GLSLstd450Tanh] = "Tanh";
570 names[GLSLstd450Asinh] = "Asinh";
571 names[GLSLstd450Acosh] = "Acosh";
572 names[GLSLstd450Atanh] = "Atanh";
573 names[GLSLstd450Atan2] = "Atan2";
574 names[GLSLstd450Pow] = "Pow";
575 names[GLSLstd450Exp] = "Exp";
576 names[GLSLstd450Log] = "Log";
577 names[GLSLstd450Exp2] = "Exp2";
578 names[GLSLstd450Log2] = "Log2";
579 names[GLSLstd450Sqrt] = "Sqrt";
John Kessenich55e7d112015-11-15 21:33:39 -0700580 names[GLSLstd450InverseSqrt] = "InverseSqrt";
John Kessenich5e4b1242015-08-06 22:53:06 -0600581 names[GLSLstd450Determinant] = "Determinant";
John Kessenich55e7d112015-11-15 21:33:39 -0700582 names[GLSLstd450MatrixInverse] = "MatrixInverse";
John Kessenich5e4b1242015-08-06 22:53:06 -0600583 names[GLSLstd450Modf] = "Modf";
584 names[GLSLstd450ModfStruct] = "ModfStruct";
585 names[GLSLstd450FMin] = "FMin";
586 names[GLSLstd450SMin] = "SMin";
587 names[GLSLstd450UMin] = "UMin";
588 names[GLSLstd450FMax] = "FMax";
589 names[GLSLstd450SMax] = "SMax";
590 names[GLSLstd450UMax] = "UMax";
591 names[GLSLstd450FClamp] = "FClamp";
592 names[GLSLstd450SClamp] = "SClamp";
593 names[GLSLstd450UClamp] = "UClamp";
John Kessenich55e7d112015-11-15 21:33:39 -0700594 names[GLSLstd450FMix] = "FMix";
John Kessenich5e4b1242015-08-06 22:53:06 -0600595 names[GLSLstd450Step] = "Step";
John Kessenich55e7d112015-11-15 21:33:39 -0700596 names[GLSLstd450SmoothStep] = "SmoothStep";
John Kessenich5e4b1242015-08-06 22:53:06 -0600597 names[GLSLstd450Fma] = "Fma";
598 names[GLSLstd450Frexp] = "Frexp";
599 names[GLSLstd450FrexpStruct] = "FrexpStruct";
600 names[GLSLstd450Ldexp] = "Ldexp";
601 names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8";
602 names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8";
603 names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16";
604 names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16";
605 names[GLSLstd450PackHalf2x16] = "PackHalf2x16";
606 names[GLSLstd450PackDouble2x32] = "PackDouble2x32";
607 names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16";
608 names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16";
609 names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16";
610 names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8";
611 names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8";
612 names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32";
613 names[GLSLstd450Length] = "Length";
614 names[GLSLstd450Distance] = "Distance";
615 names[GLSLstd450Cross] = "Cross";
616 names[GLSLstd450Normalize] = "Normalize";
John Kessenich55e7d112015-11-15 21:33:39 -0700617 names[GLSLstd450FaceForward] = "FaceForward";
John Kessenich5e4b1242015-08-06 22:53:06 -0600618 names[GLSLstd450Reflect] = "Reflect";
619 names[GLSLstd450Refract] = "Refract";
John Kessenich55e7d112015-11-15 21:33:39 -0700620 names[GLSLstd450FindILsb] = "FindILsb";
621 names[GLSLstd450FindSMsb] = "FindSMsb";
622 names[GLSLstd450FindUMsb] = "FindUMsb";
John Kessenich5e4b1242015-08-06 22:53:06 -0600623 names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid";
624 names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample";
625 names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset";
John Kessenich605afc72019-06-17 23:33:09 -0600626 names[GLSLstd450NMin] = "NMin";
627 names[GLSLstd450NMax] = "NMax";
628 names[GLSLstd450NClamp] = "NClamp";
John Kessenich5e4b1242015-08-06 22:53:06 -0600629}
630
Rex Xu9d93a232016-05-05 12:30:44 +0800631static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
632{
633 if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
634 switch (entrypoint) {
635 case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD";
636 case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD";
637 case WriteInvocationAMD: return "WriteInvocationAMD";
638 case MbcntAMD: return "MbcntAMD";
639 default: return "Bad";
640 }
641 } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
642 switch (entrypoint) {
643 case FMin3AMD: return "FMin3AMD";
644 case UMin3AMD: return "UMin3AMD";
645 case SMin3AMD: return "SMin3AMD";
646 case FMax3AMD: return "FMax3AMD";
647 case UMax3AMD: return "UMax3AMD";
648 case SMax3AMD: return "SMax3AMD";
649 case FMid3AMD: return "FMid3AMD";
650 case UMid3AMD: return "UMid3AMD";
651 case SMid3AMD: return "SMid3AMD";
652 default: return "Bad";
653 }
654 } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
655 switch (entrypoint) {
656 case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD";
657 default: return "Bad";
658 }
659 }
660 else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
661 switch (entrypoint) {
662 case CubeFaceIndexAMD: return "CubeFaceIndexAMD";
663 case CubeFaceCoordAMD: return "CubeFaceCoordAMD";
664 case TimeAMD: return "TimeAMD";
665 default:
666 break;
667 }
668 }
669
670 return "Bad";
671}
Rex Xu9d93a232016-05-05 12:30:44 +0800672
chaoc0ad6a4e2016-12-19 16:29:34 -0800673static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
674{
chaoc6e5acae2016-12-20 13:28:52 -0800675 if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
chaoc771d89f2017-01-13 01:10:53 -0800676 strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
677 strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
chaocdf3956c2017-02-14 14:52:34 -0800678 strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
Daniel Koch9bb17cd2019-05-28 15:23:10 -0400679 strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 ||
680 strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 ||
681 strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 ||
682 strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) {
chaoc0ad6a4e2016-12-19 16:29:34 -0800683 switch (entrypoint) {
Chao Chen3c366992018-09-19 11:41:59 -0700684 // NV builtins
chaoc771d89f2017-01-13 01:10:53 -0800685 case BuiltInViewportMaskNV: return "ViewportMaskNV";
chaoc771d89f2017-01-13 01:10:53 -0800686 case BuiltInSecondaryPositionNV: return "SecondaryPositionNV";
687 case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
chaocdf3956c2017-02-14 14:52:34 -0800688 case BuiltInPositionPerViewNV: return "PositionPerViewNV";
689 case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
Chao Chen9eada4b2018-09-19 11:39:56 -0700690 case BuiltInBaryCoordNV: return "BaryCoordNV";
691 case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
Chao Chen3c366992018-09-19 11:41:59 -0700692 case BuiltInTaskCountNV: return "TaskCountNV";
693 case BuiltInPrimitiveCountNV: return "PrimitiveCountNV";
694 case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV";
695 case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV";
696 case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV";
697 case BuiltInLayerPerViewNV: return "LayerPerViewNV";
698 case BuiltInMeshViewCountNV: return "MeshViewCountNV";
699 case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV";
700
701 // NV Capabilities
702 case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
703 case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV";
704 case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV";
705 case CapabilityPerViewAttributesNV: return "PerViewAttributesNV";
706 case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV";
707 case CapabilityMeshShadingNV: return "MeshShadingNV";
Daniel Koch9bb17cd2019-05-28 15:23:10 -0400708 case CapabilityImageFootprintNV: return "ImageFootprintNV";
Jason Macnakdbd4c3c2019-07-12 14:33:02 -0700709 case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV";
Chao Chen3c366992018-09-19 11:41:59 -0700710
711 // NV Decorations
712 case DecorationOverrideCoverageNV: return "OverrideCoverageNV";
713 case DecorationPassthroughNV: return "PassthroughNV";
714 case DecorationViewportRelativeNV: return "ViewportRelativeNV";
715 case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
716 case DecorationPerVertexNV: return "PerVertexNV";
717 case DecorationPerPrimitiveNV: return "PerPrimitiveNV";
718 case DecorationPerViewNV: return "PerViewNV";
719 case DecorationPerTaskNV: return "PerTaskNV";
720
chaoc771d89f2017-01-13 01:10:53 -0800721 default: return "Bad";
chaoc0ad6a4e2016-12-19 16:29:34 -0800722 }
723 }
724 return "Bad";
725}
chaoc0ad6a4e2016-12-19 16:29:34 -0800726
John Kessenich140f3df2015-06-26 16:58:36 -0600727void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
728{
729 SpirvStream SpirvStream(out, stream);
Jack Andersen52e61ac2016-02-23 12:03:21 -1000730 spv::Parameterize();
John Kessenich5e4b1242015-08-06 22:53:06 -0600731 GLSLstd450GetDebugNames(GlslStd450DebugNames);
John Kessenich140f3df2015-06-26 16:58:36 -0600732 SpirvStream.validate();
733 SpirvStream.processInstructions();
734}
735
736}; // end namespace spv