2 //Copyright (C) 2014 LunarG, Inc.
\r
4 //All rights reserved.
\r
6 //Redistribution and use in source and binary forms, with or without
\r
7 //modification, are permitted provided that the following conditions
\r
10 // Redistributions of source code must retain the above copyright
\r
11 // notice, this list of conditions and the following disclaimer.
\r
13 // Redistributions in binary form must reproduce the above
\r
14 // copyright notice, this list of conditions and the following
\r
15 // disclaimer in the documentation and/or other materials provided
\r
16 // with the distribution.
\r
18 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
\r
19 // contributors may be used to endorse or promote products derived
\r
20 // from this software without specific prior written permission.
\r
22 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
\r
23 //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
\r
24 //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTAstreamITY AND FITNESS
\r
25 //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
\r
26 //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
27 //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
\r
28 //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
\r
29 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
\r
30 //CAUSED AND ON ANY THEORY OF LIAstreamITY, WHETHER IN CONTRACT, STRICT
\r
31 //LIAstreamITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r
32 //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
33 //POSSIstreamITY OF SUCH DAMAGE.
\r
36 // Author: John Kessenich, LunarG
\r
40 // Disassembler for SPIR-V.
\r
49 #include "GLSL450Lib.h"
\r
50 extern const char* GlslStd450DebugNames[GLSL_STD_450::Count];
\r
52 #include "disassemble.h"
\r
57 void Kill(std::ostream& out, const char* message)
\r
59 out << std::endl << "Disassembly failed: " << message << std::endl;
\r
63 // Container class for a single instance of a SPIR-V stream, with methods for disassembly.
\r
66 SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
\r
67 virtual ~SpirvStream() { }
\r
70 void processInstructions();
\r
73 SpirvStream(SpirvStream&);
\r
74 SpirvStream& operator=(SpirvStream&);
\r
76 Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
\r
79 void outputIndent();
\r
80 void formatId(Id id, std::stringstream&);
\r
81 void outputResultId(Id id);
\r
82 void outputTypeId(Id id);
\r
83 void outputId(Id id);
\r
84 void disassembleImmediates(int numOperands);
\r
85 void disassembleIds(int numOperands);
\r
86 void disassembleString();
\r
87 void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
\r
90 std::ostream& out; // where to write the disassembly
\r
91 const std::vector<unsigned int>& stream; // the actual word stream
\r
92 int size; // the size of the word stream
\r
93 int word; // the next word of the stream to read
\r
95 // map each <id> to the instruction that created it
\r
97 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)
\r
99 std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
\r
102 unsigned int schema;
\r
104 // stack of structured-merge points
\r
105 std::stack<Id> nestedControl;
\r
106 Id nextNestedControl; // need a slight delay for when we are nested
\r
109 void SpirvStream::validate()
\r
111 size = (int)stream.size();
\r
113 Kill(out, "stream is too short");
\r
116 if (stream[word++] != MagicNumber) {
\r
117 out << "Bad magic number";
\r
122 out << "// Module Version " << stream[word++] << std::endl;
\r
124 // Generator's magic number
\r
125 out << "// Generated by (magic number): " << std::setbase(16) << stream[word++] << std::setbase(10) << std::endl;
\r
127 // Result <id> bound
\r
128 bound = stream[word++];
\r
129 idInstruction.resize(bound);
\r
130 idDescriptor.resize(bound);
\r
131 out << "// Id's are bound by " << bound << std::endl;
\r
134 // Reserved schema, must be 0 for now
\r
135 schema = stream[word++];
\r
137 Kill(out, "bad schema, must be 0");
\r
140 // Loop over all the instructions, in order, processing each.
\r
141 // Boiler plate for each is handled here directly, the rest is dispatched.
\r
142 void SpirvStream::processInstructions()
\r
145 while (word < size) {
\r
146 int instructionStart = word;
\r
148 // Instruction wordCount and opcode
\r
149 unsigned int firstWord = stream[word];
\r
150 unsigned wordCount = firstWord >> WordCountShift;
\r
151 Op opCode = (Op)(firstWord & OpCodeMask);
\r
152 int nextInst = word + wordCount;
\r
155 // Presence of full instruction
\r
156 if (nextInst > size)
\r
157 Kill(out, "stream instruction terminated too early");
\r
159 // Base for computing number of operands; will be updated as more is learned
\r
160 unsigned numOperands = wordCount - 1;
\r
164 if (InstructionDesc[opCode].hasType()) {
\r
165 typeId = stream[word++];
\r
171 if (InstructionDesc[opCode].hasResult()) {
\r
172 resultId = stream[word++];
\r
175 // save instruction for future reference
\r
176 idInstruction[resultId] = instructionStart;
\r
179 outputResultId(resultId);
\r
180 outputTypeId(typeId);
\r
183 // Hand off the Op and all its operands
\r
184 disassembleInstruction(resultId, typeId, opCode, numOperands);
\r
185 if (word != nextInst) {
\r
186 out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
\r
193 void SpirvStream::outputIndent()
\r
195 for (int i = 0; i < (int)nestedControl.size(); ++i)
\r
199 void SpirvStream::formatId(Id id, std::stringstream& idStream)
\r
202 Kill(out, "Bad <id>");
\r
206 if (idDescriptor[id].size() > 0)
\r
207 idStream << "(" << idDescriptor[id] << ")";
\r
211 void SpirvStream::outputResultId(Id id)
\r
213 const int width = 16;
\r
214 std::stringstream idStream;
\r
215 formatId(id, idStream);
\r
216 out << std::setw(width) << std::right << idStream.str();
\r
222 if (nestedControl.size() && id == nestedControl.top())
\r
223 nestedControl.pop();
\r
226 void SpirvStream::outputTypeId(Id id)
\r
228 const int width = 12;
\r
229 std::stringstream idStream;
\r
230 formatId(id, idStream);
\r
231 out << std::setw(width) << std::right << idStream.str() << " ";
\r
234 void SpirvStream::outputId(Id id)
\r
237 Kill(out, "Bad <id>");
\r
240 if (idDescriptor[id].size() > 0)
\r
241 out << "(" << idDescriptor[id] << ")";
\r
244 void SpirvStream::disassembleImmediates(int numOperands)
\r
246 for (int i = 0; i < numOperands; ++i) {
\r
247 out << stream[word++];
\r
248 if (i < numOperands - 1)
\r
253 void SpirvStream::disassembleIds(int numOperands)
\r
255 for (int i = 0; i < numOperands; ++i) {
\r
256 outputId(stream[word++]);
\r
257 if (i < numOperands - 1)
\r
262 void SpirvStream::disassembleString()
\r
269 unsigned int content = stream[word];
\r
270 wordString = (char*)&content;
\r
271 for (int charCount = 0; charCount < 4; ++charCount) {
\r
272 if (*wordString == 0) {
\r
276 out << *(wordString++);
\r
284 void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
\r
286 // Process the opcode
\r
288 out << (OpcodeString(opCode) + 2); // leave out the "Op"
\r
290 if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
\r
291 nextNestedControl = stream[word];
\r
292 else if (opCode == OpBranchConditional || opCode == OpSwitch) {
\r
293 if (nextNestedControl) {
\r
294 nestedControl.push(nextNestedControl);
\r
295 nextNestedControl = 0;
\r
297 } else if (opCode == OpExtInstImport)
\r
298 idDescriptor[resultId] = (char*)(&stream[word]);
\r
300 if (idDescriptor[resultId].size() == 0) {
\r
303 idDescriptor[resultId] = "int";
\r
306 idDescriptor[resultId] = "float";
\r
309 idDescriptor[resultId] = "bool";
\r
312 idDescriptor[resultId] = "struct";
\r
314 case OpTypePointer:
\r
315 idDescriptor[resultId] = "ptr";
\r
318 if (idDescriptor[stream[word]].size() > 0)
\r
319 idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
\r
320 idDescriptor[resultId].append("vec");
\r
321 switch (stream[word + 1]) {
\r
322 case 2: idDescriptor[resultId].append("2"); break;
\r
323 case 3: idDescriptor[resultId].append("3"); break;
\r
324 case 4: idDescriptor[resultId].append("4"); break;
\r
325 case 8: idDescriptor[resultId].append("8"); break;
\r
326 case 16: idDescriptor[resultId].append("16"); break;
\r
327 case 32: idDescriptor[resultId].append("32"); break;
\r
337 // Process the operands. Note, a new context-dependent set could be
\r
338 // swapped in mid-traversal.
\r
340 // Handle textures specially, so can put out helpful strings.
\r
341 if (opCode == OpTypeSampler) {
\r
343 out << " " << DimensionString((Dim)stream[word++]);
\r
344 switch (stream[word++]) {
\r
345 case 0: out << " texture"; break;
\r
346 case 1: out << " image"; break;
\r
347 case 2: out << " filter+texture"; break;
\r
349 out << (stream[word++] != 0 ? " array" : "");
\r
350 out << (stream[word++] != 0 ? " depth" : "");
\r
351 out << (stream[word++] != 0 ? " multi-sampled" : "");
\r
355 // Handle all the parameterized operands
\r
356 for (int op = 0; op < InstructionDesc[opCode].operands.getNum(); ++op) {
\r
358 OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
\r
359 switch (operandClass) {
\r
362 // Get names for printing "(XXX)" for readability, *after* this id
\r
363 if (opCode == OpName)
\r
364 idDescriptor[stream[word - 1]] = (char*)(&stream[word]);
\r
366 case OperandOptionalId:
\r
367 case OperandVariableIds:
\r
368 disassembleIds(numOperands);
\r
370 case OperandVariableLiterals:
\r
371 if (opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) {
\r
372 out << BuiltInString(stream[word++]);
\r
376 disassembleImmediates(numOperands);
\r
378 case OperandVariableLiteralId:
\r
379 while (numOperands > 0) {
\r
385 disassembleImmediates(1);
\r
391 case OperandLiteralNumber:
\r
392 disassembleImmediates(1);
\r
393 if (opCode == OpExtInst) {
\r
394 unsigned entrypoint = stream[word - 1];
\r
395 if (entrypoint < GLSL_STD_450::Count)
\r
396 out << "(" << GlslStd450DebugNames[entrypoint] << ")";
\r
399 case OperandLiteralString:
\r
400 disassembleString();
\r
403 assert(operandClass >= OperandSource && operandClass < OperandOpcode);
\r
405 if (OperandClassParams[operandClass].bitmask) {
\r
406 unsigned int mask = stream[word++];
\r
410 for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
\r
411 if (mask & (1 << m))
\r
412 out << OperandClassParams[operandClass].getName(m) << " ";
\r
417 out << OperandClassParams[operandClass].getName(stream[word++]);
\r
427 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
\r
429 SpirvStream SpirvStream(out, stream);
\r
430 SpirvStream.validate();
\r
431 SpirvStream.processInstructions();
\r
434 }; // end namespace spv
\r