blob: eeadfc9ae716b70b4b47010220408c2b312d2c5a [file] [log] [blame]
John Kessenich140f3df2015-06-26 16:58:36 -06001//
John Kessenich5e4b1242015-08-06 22:53:06 -06002//Copyright (C) 2014-2015 LunarG, Inc.
John Kessenich140f3df2015-06-26 16:58:36 -06003//
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
John Kessenich5e4b1242015-08-06 22:53:06 -060024//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
John Kessenich140f3df2015-06-26 16:58:36 -060025//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
John Kessenich5e4b1242015-08-06 22:53:06 -060030//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
John Kessenich140f3df2015-06-26 16:58:36 -060032//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
John Kessenich5e4b1242015-08-06 22:53:06 -060033//POSSIBILITY OF SUCH DAMAGE.
John Kessenich140f3df2015-06-26 16:58:36 -060034
35//
36// Author: John Kessenich, LunarG
37//
38
39//
40// Disassembler for SPIR-V.
41//
42
43#include <stdlib.h>
John Kessenich5e4b1242015-08-06 22:53:06 -060044#include <string.h>
John Kessenich140f3df2015-06-26 16:58:36 -060045#include <assert.h>
46#include <iomanip>
47#include <stack>
48#include <sstream>
John Kessenich5e4b1242015-08-06 22:53:06 -060049#include <cstring>
John Kessenich140f3df2015-06-26 16:58:36 -060050
51#include "disassemble.h"
52#include "doc.h"
53
54namespace spv {
55
John Kessenich5e4b1242015-08-06 22:53:06 -060056#include "GLSL.std.450.h"
57
58const char* GlslStd450DebugNames[spv::GLSLstd450Count];
59
John Kessenich140f3df2015-06-26 16:58:36 -060060void Kill(std::ostream& out, const char* message)
61{
62 out << std::endl << "Disassembly failed: " << message << std::endl;
63 exit(1);
64}
65
John Kessenich5e4b1242015-08-06 22:53:06 -060066// used to identify the extended instruction library imported when printing
67enum ExtInstSet {
68 GLSL450Inst,
69 OpenCLExtInst,
70};
71
John Kessenich140f3df2015-06-26 16:58:36 -060072// Container class for a single instance of a SPIR-V stream, with methods for disassembly.
73class SpirvStream {
74public:
75 SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
76 virtual ~SpirvStream() { }
77
78 void validate();
79 void processInstructions();
80
81protected:
John Kessenich5e4b1242015-08-06 22:53:06 -060082 SpirvStream(const SpirvStream&);
83 SpirvStream& operator=(const SpirvStream&);
John Kessenich140f3df2015-06-26 16:58:36 -060084 Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
85
86 // Output methods
87 void outputIndent();
88 void formatId(Id id, std::stringstream&);
89 void outputResultId(Id id);
90 void outputTypeId(Id id);
91 void outputId(Id id);
John Kessenich5e4b1242015-08-06 22:53:06 -060092 void outputMask(OperandClass operandClass, unsigned mask);
John Kessenich140f3df2015-06-26 16:58:36 -060093 void disassembleImmediates(int numOperands);
94 void disassembleIds(int numOperands);
95 void disassembleString();
96 void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
97
98 // Data
99 std::ostream& out; // where to write the disassembly
100 const std::vector<unsigned int>& stream; // the actual word stream
101 int size; // the size of the word stream
102 int word; // the next word of the stream to read
103
104 // map each <id> to the instruction that created it
105 Id bound;
106 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)
107
108 std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
109
110 // schema
111 unsigned int schema;
112
113 // stack of structured-merge points
114 std::stack<Id> nestedControl;
115 Id nextNestedControl; // need a slight delay for when we are nested
116};
117
118void SpirvStream::validate()
119{
120 size = (int)stream.size();
121 if (size < 4)
122 Kill(out, "stream is too short");
123
124 // Magic number
125 if (stream[word++] != MagicNumber) {
126 out << "Bad magic number";
127 return;
128 }
129
130 // Version
131 out << "// Module Version " << stream[word++] << std::endl;
132
133 // Generator's magic number
134 out << "// Generated by (magic number): " << std::setbase(16) << stream[word++] << std::setbase(10) << std::endl;
135
136 // Result <id> bound
137 bound = stream[word++];
138 idInstruction.resize(bound);
139 idDescriptor.resize(bound);
140 out << "// Id's are bound by " << bound << std::endl;
141 out << std::endl;
142
143 // Reserved schema, must be 0 for now
144 schema = stream[word++];
145 if (schema != 0)
146 Kill(out, "bad schema, must be 0");
147}
148
149// Loop over all the instructions, in order, processing each.
150// Boiler plate for each is handled here directly, the rest is dispatched.
151void SpirvStream::processInstructions()
152{
153 // Instructions
154 while (word < size) {
155 int instructionStart = word;
156
157 // Instruction wordCount and opcode
158 unsigned int firstWord = stream[word];
159 unsigned wordCount = firstWord >> WordCountShift;
160 Op opCode = (Op)(firstWord & OpCodeMask);
161 int nextInst = word + wordCount;
162 ++word;
163
164 // Presence of full instruction
165 if (nextInst > size)
166 Kill(out, "stream instruction terminated too early");
167
168 // Base for computing number of operands; will be updated as more is learned
169 unsigned numOperands = wordCount - 1;
170
171 // Type <id>
172 Id typeId = 0;
173 if (InstructionDesc[opCode].hasType()) {
174 typeId = stream[word++];
175 --numOperands;
176 }
177
178 // Result <id>
179 Id resultId = 0;
180 if (InstructionDesc[opCode].hasResult()) {
181 resultId = stream[word++];
182 --numOperands;
183
184 // save instruction for future reference
185 idInstruction[resultId] = instructionStart;
186 }
187
188 outputResultId(resultId);
189 outputTypeId(typeId);
190 outputIndent();
191
192 // Hand off the Op and all its operands
193 disassembleInstruction(resultId, typeId, opCode, numOperands);
194 if (word != nextInst) {
195 out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
196 word = nextInst;
197 }
198 out << std::endl;
199 }
200}
201
202void SpirvStream::outputIndent()
203{
204 for (int i = 0; i < (int)nestedControl.size(); ++i)
205 out << " ";
206}
207
208void SpirvStream::formatId(Id id, std::stringstream& idStream)
209{
210 if (id >= bound)
211 Kill(out, "Bad <id>");
212
213 if (id != 0) {
214 idStream << id;
215 if (idDescriptor[id].size() > 0)
216 idStream << "(" << idDescriptor[id] << ")";
217 }
218}
219
220void SpirvStream::outputResultId(Id id)
221{
222 const int width = 16;
223 std::stringstream idStream;
224 formatId(id, idStream);
225 out << std::setw(width) << std::right << idStream.str();
226 if (id != 0)
227 out << ":";
228 else
229 out << " ";
230
231 if (nestedControl.size() && id == nestedControl.top())
232 nestedControl.pop();
233}
234
235void SpirvStream::outputTypeId(Id id)
236{
237 const int width = 12;
238 std::stringstream idStream;
239 formatId(id, idStream);
240 out << std::setw(width) << std::right << idStream.str() << " ";
241}
242
243void SpirvStream::outputId(Id id)
244{
245 if (id >= bound)
246 Kill(out, "Bad <id>");
247
248 out << id;
249 if (idDescriptor[id].size() > 0)
250 out << "(" << idDescriptor[id] << ")";
251}
252
John Kessenich5e4b1242015-08-06 22:53:06 -0600253void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
254{
255 if (mask == 0)
256 out << "None";
257 else {
258 for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
259 if (mask & (1 << m))
260 out << OperandClassParams[operandClass].getName(m) << " ";
261 }
262 }
263}
264
John Kessenich140f3df2015-06-26 16:58:36 -0600265void SpirvStream::disassembleImmediates(int numOperands)
266{
267 for (int i = 0; i < numOperands; ++i) {
268 out << stream[word++];
269 if (i < numOperands - 1)
270 out << " ";
271 }
272}
273
274void SpirvStream::disassembleIds(int numOperands)
275{
276 for (int i = 0; i < numOperands; ++i) {
277 outputId(stream[word++]);
278 if (i < numOperands - 1)
279 out << " ";
280 }
281}
282
283void SpirvStream::disassembleString()
284{
285 out << " \"";
286
287 char* wordString;
288 bool done = false;
289 do {
290 unsigned int content = stream[word];
291 wordString = (char*)&content;
292 for (int charCount = 0; charCount < 4; ++charCount) {
293 if (*wordString == 0) {
294 done = true;
295 break;
296 }
297 out << *(wordString++);
298 }
299 ++word;
300 } while (! done);
301
302 out << "\"";
303}
304
305void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
306{
307 // Process the opcode
308
309 out << (OpcodeString(opCode) + 2); // leave out the "Op"
310
311 if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
312 nextNestedControl = stream[word];
313 else if (opCode == OpBranchConditional || opCode == OpSwitch) {
314 if (nextNestedControl) {
315 nestedControl.push(nextNestedControl);
316 nextNestedControl = 0;
317 }
John Kessenich5e4b1242015-08-06 22:53:06 -0600318 } else if (opCode == OpExtInstImport) {
John Kessenich140f3df2015-06-26 16:58:36 -0600319 idDescriptor[resultId] = (char*)(&stream[word]);
John Kessenich5e4b1242015-08-06 22:53:06 -0600320 }
John Kessenich140f3df2015-06-26 16:58:36 -0600321 else {
322 if (idDescriptor[resultId].size() == 0) {
323 switch (opCode) {
324 case OpTypeInt:
325 idDescriptor[resultId] = "int";
326 break;
327 case OpTypeFloat:
328 idDescriptor[resultId] = "float";
329 break;
330 case OpTypeBool:
331 idDescriptor[resultId] = "bool";
332 break;
333 case OpTypeStruct:
334 idDescriptor[resultId] = "struct";
335 break;
336 case OpTypePointer:
337 idDescriptor[resultId] = "ptr";
338 break;
339 case OpTypeVector:
340 if (idDescriptor[stream[word]].size() > 0)
341 idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
342 idDescriptor[resultId].append("vec");
343 switch (stream[word + 1]) {
344 case 2: idDescriptor[resultId].append("2"); break;
345 case 3: idDescriptor[resultId].append("3"); break;
346 case 4: idDescriptor[resultId].append("4"); break;
347 case 8: idDescriptor[resultId].append("8"); break;
348 case 16: idDescriptor[resultId].append("16"); break;
349 case 32: idDescriptor[resultId].append("32"); break;
350 default: break;
351 }
352 break;
353 default:
354 break;
355 }
356 }
357 }
358
359 // Process the operands. Note, a new context-dependent set could be
360 // swapped in mid-traversal.
361
John Kessenich5e4b1242015-08-06 22:53:06 -0600362 // Handle images specially, so can put out helpful strings.
363 if (opCode == OpTypeImage) {
364 out << " ";
John Kessenich140f3df2015-06-26 16:58:36 -0600365 disassembleIds(1);
366 out << " " << DimensionString((Dim)stream[word++]);
John Kessenich140f3df2015-06-26 16:58:36 -0600367 out << (stream[word++] != 0 ? " depth" : "");
John Kessenich5e4b1242015-08-06 22:53:06 -0600368 out << (stream[word++] != 0 ? " array" : "");
John Kessenich140f3df2015-06-26 16:58:36 -0600369 out << (stream[word++] != 0 ? " multi-sampled" : "");
John Kessenich5e4b1242015-08-06 22:53:06 -0600370 switch (stream[word++]) {
371 case 0: out << " runtime"; break;
372 case 1: out << " sampled"; break;
373 case 2: out << " nonsampled"; break;
374 }
375 out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
376
377 if (numOperands == 8) {
378 out << " " << AccessQualifierString(stream[word++]);
379 }
John Kessenich140f3df2015-06-26 16:58:36 -0600380 return;
381 }
382
383 // Handle all the parameterized operands
John Kessenich5e4b1242015-08-06 22:53:06 -0600384 for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
John Kessenich140f3df2015-06-26 16:58:36 -0600385 out << " ";
386 OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
387 switch (operandClass) {
388 case OperandId:
John Kessenich5e4b1242015-08-06 22:53:06 -0600389 case OperandScope:
390 case OperandMemorySemantics:
John Kessenich140f3df2015-06-26 16:58:36 -0600391 disassembleIds(1);
392 // Get names for printing "(XXX)" for readability, *after* this id
393 if (opCode == OpName)
394 idDescriptor[stream[word - 1]] = (char*)(&stream[word]);
395 break;
396 case OperandOptionalId:
397 case OperandVariableIds:
398 disassembleIds(numOperands);
399 return;
John Kessenich5e4b1242015-08-06 22:53:06 -0600400 case OperandOptionalImage:
401 outputMask(operandClass, stream[word++]);
402 --numOperands;
403 disassembleIds(numOperands);
404 return;
405 case OperandOptionalLiteral:
John Kessenich140f3df2015-06-26 16:58:36 -0600406 case OperandVariableLiterals:
John Kessenich5e4b1242015-08-06 22:53:06 -0600407 if (opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn ||
408 opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn) {
John Kessenich140f3df2015-06-26 16:58:36 -0600409 out << BuiltInString(stream[word++]);
410 --numOperands;
411 ++op;
412 }
413 disassembleImmediates(numOperands);
414 return;
John Kessenich5e4b1242015-08-06 22:53:06 -0600415 case OperandVariableIdLiteral:
416 while (numOperands > 0) {
417 out << std::endl;
418 outputResultId(0);
419 outputTypeId(0);
420 outputIndent();
421 out << " Type ";
422 disassembleIds(1);
423 out << ", member ";
424 disassembleImmediates(1);
425 numOperands -= 2;
426 }
427 return;
John Kessenich140f3df2015-06-26 16:58:36 -0600428 case OperandVariableLiteralId:
429 while (numOperands > 0) {
430 out << std::endl;
431 outputResultId(0);
432 outputTypeId(0);
433 outputIndent();
434 out << " case ";
435 disassembleImmediates(1);
436 out << ": ";
437 disassembleIds(1);
438 numOperands -= 2;
439 }
440 return;
441 case OperandLiteralNumber:
442 disassembleImmediates(1);
443 if (opCode == OpExtInst) {
John Kessenich5e4b1242015-08-06 22:53:06 -0600444 ExtInstSet extInstSet = GLSL450Inst;
445 if (0 == memcmp("OpenCL", (char*)(idDescriptor[stream[word-2]].c_str()), 6)) {
446 extInstSet = OpenCLExtInst;
447 }
John Kessenich140f3df2015-06-26 16:58:36 -0600448 unsigned entrypoint = stream[word - 1];
John Kessenich5e4b1242015-08-06 22:53:06 -0600449 if (extInstSet == GLSL450Inst) {
450 if (entrypoint < spv::GLSLstd450Count) {
451 out << "(" << GlslStd450DebugNames[entrypoint] << ")";
452 }
453 }
John Kessenich140f3df2015-06-26 16:58:36 -0600454 }
455 break;
456 case OperandLiteralString:
457 disassembleString();
458 return;
459 default:
460 assert(operandClass >= OperandSource && operandClass < OperandOpcode);
461
John Kessenich5e4b1242015-08-06 22:53:06 -0600462 if (OperandClassParams[operandClass].bitmask)
463 outputMask(operandClass, stream[word++]);
464 else
John Kessenich140f3df2015-06-26 16:58:36 -0600465 out << OperandClassParams[operandClass].getName(stream[word++]);
466
467 break;
468 }
469 --numOperands;
470 }
471
472 return;
473}
474
John Kessenich5e4b1242015-08-06 22:53:06 -0600475void GLSLstd450GetDebugNames(const char** names)
476{
477 for (int i = 0; i < GLSLstd450Count; ++i)
478 names[i] = "Unknown";
479
480 names[GLSLstd450Round] = "Round";
481 names[GLSLstd450RoundEven] = "RoundEven";
482 names[GLSLstd450Trunc] = "Trunc";
483 names[GLSLstd450FAbs] = "FAbs";
484 names[GLSLstd450SAbs] = "SAbs";
485 names[GLSLstd450FSign] = "FSign";
486 names[GLSLstd450SSign] = "SSign";
487 names[GLSLstd450Floor] = "Floor";
488 names[GLSLstd450Ceil] = "Ceil";
489 names[GLSLstd450Fract] = "Fract";
490 names[GLSLstd450Radians] = "Radians";
491 names[GLSLstd450Degrees] = "Degrees";
492 names[GLSLstd450Sin] = "Sin";
493 names[GLSLstd450Cos] = "Cos";
494 names[GLSLstd450Tan] = "Tan";
495 names[GLSLstd450Asin] = "Asin";
496 names[GLSLstd450Acos] = "Acos";
497 names[GLSLstd450Atan] = "Atan";
498 names[GLSLstd450Sinh] = "Sinh";
499 names[GLSLstd450Cosh] = "Cosh";
500 names[GLSLstd450Tanh] = "Tanh";
501 names[GLSLstd450Asinh] = "Asinh";
502 names[GLSLstd450Acosh] = "Acosh";
503 names[GLSLstd450Atanh] = "Atanh";
504 names[GLSLstd450Atan2] = "Atan2";
505 names[GLSLstd450Pow] = "Pow";
506 names[GLSLstd450Exp] = "Exp";
507 names[GLSLstd450Log] = "Log";
508 names[GLSLstd450Exp2] = "Exp2";
509 names[GLSLstd450Log2] = "Log2";
510 names[GLSLstd450Sqrt] = "Sqrt";
511 names[GLSLstd450InverseSqrt] = "Inversesqrt";
512 names[GLSLstd450Determinant] = "Determinant";
513 names[GLSLstd450MatrixInverse] = "Inverse";
514 names[GLSLstd450Modf] = "Modf";
515 names[GLSLstd450ModfStruct] = "ModfStruct";
516 names[GLSLstd450FMin] = "FMin";
517 names[GLSLstd450SMin] = "SMin";
518 names[GLSLstd450UMin] = "UMin";
519 names[GLSLstd450FMax] = "FMax";
520 names[GLSLstd450SMax] = "SMax";
521 names[GLSLstd450UMax] = "UMax";
522 names[GLSLstd450FClamp] = "FClamp";
523 names[GLSLstd450SClamp] = "SClamp";
524 names[GLSLstd450UClamp] = "UClamp";
525 names[GLSLstd450Mix] = "Mix";
526 names[GLSLstd450Step] = "Step";
527 names[GLSLstd450SmoothStep] = "Smoothstep";
528 names[GLSLstd450Fma] = "Fma";
529 names[GLSLstd450Frexp] = "Frexp";
530 names[GLSLstd450FrexpStruct] = "FrexpStruct";
531 names[GLSLstd450Ldexp] = "Ldexp";
532 names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8";
533 names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8";
534 names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16";
535 names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16";
536 names[GLSLstd450PackHalf2x16] = "PackHalf2x16";
537 names[GLSLstd450PackDouble2x32] = "PackDouble2x32";
538 names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16";
539 names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16";
540 names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16";
541 names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8";
542 names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8";
543 names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32";
544 names[GLSLstd450Length] = "Length";
545 names[GLSLstd450Distance] = "Distance";
546 names[GLSLstd450Cross] = "Cross";
547 names[GLSLstd450Normalize] = "Normalize";
548 names[GLSLstd450FaceForward] = "Faceforward";
549 names[GLSLstd450Reflect] = "Reflect";
550 names[GLSLstd450Refract] = "Refract";
551 names[GLSLstd450AddCarry] = "UaddCarry";
552 names[GLSLstd450SubBorrow] = "UsubBorrow";
553 names[GLSLstd450MulExtended] = "UmulExtended";
554 names[GLSLstd450FindILSB] = "FindILsb";
555 names[GLSLstd450FindSMSB] = "FindSMsb";
556 names[GLSLstd450FindUMSB] = "FindUMsb";
557 names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid";
558 names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample";
559 names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset";
560}
561
John Kessenich140f3df2015-06-26 16:58:36 -0600562void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
563{
564 SpirvStream SpirvStream(out, stream);
John Kessenich5e4b1242015-08-06 22:53:06 -0600565 GLSLstd450GetDebugNames(GlslStd450DebugNames);
John Kessenich140f3df2015-06-26 16:58:36 -0600566 SpirvStream.validate();
567 SpirvStream.processInstructions();
568}
569
570}; // end namespace spv