blob: 3daff74c5d7b6d91c706ca0e9f38731b05049648 [file] [log] [blame]
John Kessenichacba7722015-03-04 03:48:38 +00001//
2//Copyright (C) 2014 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 MERCHANTAstreamITY 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 LIAstreamITY, WHETHER IN CONTRACT, STRICT
31//LIAstreamITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33//POSSIstreamITY OF SUCH DAMAGE.
34
35//
36// Author: John Kessenich, LunarG
37//
38
39//
40// Disassembler for SPIR-V.
41//
42
John Kessenichb40d6ac2015-03-30 17:41:16 +000043#include <stdlib.h>
44#include <assert.h>
John Kessenichacba7722015-03-04 03:48:38 +000045#include <iomanip>
46#include <stack>
47#include <sstream>
John Kessenichacba7722015-03-04 03:48:38 +000048
49#include "GLSL450Lib.h"
50extern const char* GlslStd450DebugNames[GLSL_STD_450::Count];
51
52#include "disassemble.h"
53#include "doc.h"
54
55namespace spv {
56
57void Kill(std::ostream& out, const char* message)
58{
59 out << std::endl << "Disassembly failed: " << message << std::endl;
60 exit(1);
61}
62
63// Container class for a single instance of a SPIR-V stream, with methods for disassembly.
64class SpirvStream {
65public:
66 SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
67 virtual ~SpirvStream() { }
68
69 void validate();
70 void processInstructions();
71
72protected:
John Kessenichb40d6ac2015-03-30 17:41:16 +000073 Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
John Kessenichacba7722015-03-04 03:48:38 +000074
75 // Output methods
76 void outputIndent();
77 void formatId(Id id, std::stringstream&);
78 void outputResultId(Id id);
79 void outputTypeId(Id id);
80 void outputId(Id id);
81 void disassembleImmediates(int numOperands);
82 void disassembleIds(int numOperands);
83 void disassembleString();
John Kessenichb40d6ac2015-03-30 17:41:16 +000084 void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
John Kessenichacba7722015-03-04 03:48:38 +000085
86 // Data
87 std::ostream& out; // where to write the disassembly
88 const std::vector<unsigned int>& stream; // the actual word stream
89 int size; // the size of the word stream
90 int word; // the next word of the stream to read
91
92 // map each <id> to the instruction that created it
93 Id bound;
94 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)
95
96 std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
97
98 // schema
99 unsigned int schema;
100
101 // stack of structured-merge points
102 std::stack<Id> nestedControl;
103 Id nextNestedControl; // need a slight delay for when we are nested
104};
105
106void SpirvStream::validate()
107{
108 size = (int)stream.size();
109 if (size < 4)
110 Kill(out, "stream is too short");
111
112 // Magic number
113 if (stream[word++] != MagicNumber) {
114 out << "Bad magic number";
115 return;
116 }
117
118 // Version
119 out << "// Module Version " << stream[word++] << std::endl;
120
121 // Generator's magic number
122 out << "// Generated by (magic number): " << std::setbase(16) << stream[word++] << std::setbase(10) << std::endl;
123
124 // Result <id> bound
125 bound = stream[word++];
126 idInstruction.resize(bound);
127 idDescriptor.resize(bound);
128 out << "// Id's are bound by " << bound << std::endl;
129 out << std::endl;
130
131 // Reserved schema, must be 0 for now
132 schema = stream[word++];
133 if (schema != 0)
134 Kill(out, "bad schema, must be 0");
135}
136
137// Loop over all the instructions, in order, processing each.
138// Boiler plate for each is handled here directly, the rest is dispatched.
139void SpirvStream::processInstructions()
140{
141 // Instructions
142 while (word < size) {
143 int instructionStart = word;
144
145 // Instruction wordCount and opcode
146 unsigned int firstWord = stream[word];
147 unsigned wordCount = firstWord >> WordCountShift;
John Kessenichb40d6ac2015-03-30 17:41:16 +0000148 Op opCode = (Op)(firstWord & OpCodeMask);
John Kessenichacba7722015-03-04 03:48:38 +0000149 int nextInst = word + wordCount;
150 ++word;
151
152 // Presence of full instruction
153 if (nextInst > size)
154 Kill(out, "stream instruction terminated too early");
155
156 // Base for computing number of operands; will be updated as more is learned
157 unsigned numOperands = wordCount - 1;
158
159 // Type <id>
160 Id typeId = 0;
161 if (InstructionDesc[opCode].hasType()) {
162 typeId = stream[word++];
163 --numOperands;
164 }
165
166 // Result <id>
167 Id resultId = 0;
168 if (InstructionDesc[opCode].hasResult()) {
169 resultId = stream[word++];
170 --numOperands;
171
172 // save instruction for future reference
173 idInstruction[resultId] = instructionStart;
174 }
175
176 outputResultId(resultId);
177 outputTypeId(typeId);
178 outputIndent();
179
John Kessenichb40d6ac2015-03-30 17:41:16 +0000180 // Hand off the Op and all its operands
John Kessenichacba7722015-03-04 03:48:38 +0000181 disassembleInstruction(resultId, typeId, opCode, numOperands);
182 if (word != nextInst) {
183 out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
184 word = nextInst;
185 }
186 out << std::endl;
187 }
188}
189
190void SpirvStream::outputIndent()
191{
192 for (int i = 0; i < (int)nestedControl.size(); ++i)
193 out << " ";
194}
195
196void SpirvStream::formatId(Id id, std::stringstream& idStream)
197{
198 if (id >= bound)
199 Kill(out, "Bad <id>");
200
201 if (id != 0) {
202 idStream << id;
203 if (idDescriptor[id].size() > 0)
204 idStream << "(" << idDescriptor[id] << ")";
205 }
206}
207
208void SpirvStream::outputResultId(Id id)
209{
210 const int width = 16;
211 std::stringstream idStream;
212 formatId(id, idStream);
213 out << std::setw(width) << std::right << idStream.str();
214 if (id != 0)
215 out << ":";
216 else
217 out << " ";
218
219 if (nestedControl.size() && id == nestedControl.top())
220 nestedControl.pop();
221}
222
223void SpirvStream::outputTypeId(Id id)
224{
225 const int width = 12;
226 std::stringstream idStream;
227 formatId(id, idStream);
228 out << std::setw(width) << std::right << idStream.str() << " ";
229}
230
231void SpirvStream::outputId(Id id)
232{
233 if (id >= bound)
234 Kill(out, "Bad <id>");
235
236 out << id;
237 if (idDescriptor[id].size() > 0)
238 out << "(" << idDescriptor[id] << ")";
239}
240
241void SpirvStream::disassembleImmediates(int numOperands)
242{
243 for (int i = 0; i < numOperands; ++i) {
244 out << stream[word++];
245 if (i < numOperands - 1)
246 out << " ";
247 }
248}
249
250void SpirvStream::disassembleIds(int numOperands)
251{
252 for (int i = 0; i < numOperands; ++i) {
253 outputId(stream[word++]);
254 if (i < numOperands - 1)
255 out << " ";
256 }
257}
258
259void SpirvStream::disassembleString()
260{
261 out << " \"";
262
263 char* wordString;
264 bool done = false;
265 do {
266 unsigned int content = stream[word];
267 wordString = (char*)&content;
268 for (int charCount = 0; charCount < 4; ++charCount) {
269 if (*wordString == 0) {
270 done = true;
271 break;
272 }
273 out << *(wordString++);
274 }
275 ++word;
276 } while (! done);
277
278 out << "\"";
279}
280
John Kessenichb40d6ac2015-03-30 17:41:16 +0000281void SpirvStream::disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands)
John Kessenichacba7722015-03-04 03:48:38 +0000282{
283 // Process the opcode
284
John Kessenichb40d6ac2015-03-30 17:41:16 +0000285 out << (OpcodeString(opCode) + 2); // leave out the "Op"
John Kessenichacba7722015-03-04 03:48:38 +0000286
287 if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
288 nextNestedControl = stream[word];
289 else if (opCode == OpBranchConditional || opCode == OpSwitch) {
290 if (nextNestedControl) {
291 nestedControl.push(nextNestedControl);
292 nextNestedControl = 0;
293 }
294 } else if (opCode == OpExtInstImport)
295 idDescriptor[resultId] = (char*)(&stream[word]);
296 else {
297 if (idDescriptor[resultId].size() == 0) {
298 switch (opCode) {
299 case OpTypeInt:
300 idDescriptor[resultId] = "int";
301 break;
302 case OpTypeFloat:
303 idDescriptor[resultId] = "float";
304 break;
305 case OpTypeBool:
306 idDescriptor[resultId] = "bool";
307 break;
308 case OpTypeStruct:
309 idDescriptor[resultId] = "struct";
310 break;
311 case OpTypePointer:
312 idDescriptor[resultId] = "ptr";
313 break;
314 case OpTypeVector:
315 if (idDescriptor[stream[word]].size() > 0)
316 idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
317 idDescriptor[resultId].append("vec");
318 switch (stream[word + 1]) {
319 case 2: idDescriptor[resultId].append("2"); break;
320 case 3: idDescriptor[resultId].append("3"); break;
321 case 4: idDescriptor[resultId].append("4"); break;
322 case 8: idDescriptor[resultId].append("8"); break;
323 case 16: idDescriptor[resultId].append("16"); break;
324 case 32: idDescriptor[resultId].append("32"); break;
325 default: break;
326 }
327 break;
328 default:
329 break;
330 }
331 }
332 }
333
334 // Process the operands. Note, a new context-dependent set could be
335 // swapped in mid-traversal.
336
337 // Handle textures specially, so can put out helpful strings.
338 if (opCode == OpTypeSampler) {
339 disassembleIds(1);
John Kessenichb40d6ac2015-03-30 17:41:16 +0000340 out << " " << DimensionString((Dim)stream[word++]);
John Kessenichacba7722015-03-04 03:48:38 +0000341 switch (stream[word++]) {
342 case 0: out << " texture"; break;
343 case 1: out << " image"; break;
344 case 2: out << " filter+texture"; break;
345 }
346 out << (stream[word++] != 0 ? " array" : "");
347 out << (stream[word++] != 0 ? " depth" : "");
348 out << (stream[word++] != 0 ? " multi-sampled" : "");
349 return;
350 }
351
352 // Handle all the parameterized operands
353 for (int op = 0; op < InstructionDesc[opCode].operands.getNum(); ++op) {
354 out << " ";
John Kessenichb40d6ac2015-03-30 17:41:16 +0000355 OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
356 switch (operandClass) {
John Kessenichacba7722015-03-04 03:48:38 +0000357 case OperandId:
358 disassembleIds(1);
359 // Get names for printing "(XXX)" for readability, *after* this id
360 if (opCode == OpName)
361 idDescriptor[stream[word - 1]] = (char*)(&stream[word]);
362 break;
363 case OperandOptionalId:
364 case OperandVariableIds:
365 disassembleIds(numOperands);
366 return;
367 case OperandVariableLiterals:
John Kessenichb40d6ac2015-03-30 17:41:16 +0000368 if (opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) {
John Kessenichacba7722015-03-04 03:48:38 +0000369 out << BuiltInString(stream[word++]);
370 --numOperands;
371 ++op;
372 }
373 disassembleImmediates(numOperands);
374 return;
375 case OperandVariableLiteralId:
376 while (numOperands > 0) {
377 out << std::endl;
John Kessenichb40d6ac2015-03-30 17:41:16 +0000378 outputResultId(0);
379 outputTypeId(0);
John Kessenichacba7722015-03-04 03:48:38 +0000380 outputIndent();
381 out << " case ";
382 disassembleImmediates(1);
383 out << ": ";
384 disassembleIds(1);
385 numOperands -= 2;
386 }
387 return;
388 case OperandLiteralNumber:
389 disassembleImmediates(1);
390 if (opCode == OpExtInst) {
391 unsigned entrypoint = stream[word - 1];
392 if (entrypoint < GLSL_STD_450::Count)
393 out << "(" << GlslStd450DebugNames[entrypoint] << ")";
394 }
395 break;
396 case OperandLiteralString:
397 disassembleString();
398 return;
John Kessenichacba7722015-03-04 03:48:38 +0000399 default:
John Kessenichb40d6ac2015-03-30 17:41:16 +0000400 assert(operandClass >= OperandSource && operandClass < OperandOpcode);
401
402 if (OperandClassParams[operandClass].bitmask) {
403 unsigned int mask = stream[word++];
404 if (mask == 0)
405 out << "None";
406 else {
407 for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
408 if (mask & (1 << m))
409 out << OperandClassParams[operandClass].getName(m) << " ";
410 }
411 }
412 break;
413 } else
414 out << OperandClassParams[operandClass].getName(stream[word++]);
415
John Kessenichacba7722015-03-04 03:48:38 +0000416 break;
417 }
418 --numOperands;
419 }
420
421 return;
422}
423
424void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
425{
426 SpirvStream SpirvStream(out, stream);
427 SpirvStream.validate();
428 SpirvStream.processInstructions();
429}
430
431}; // end namespace spv