glslang: Fix over 100 warnings from MSVC warning level 4.
[platform/upstream/glslang.git] / SPIRV / disassemble.cpp
1 //\r
2 //Copyright (C) 2014 LunarG, Inc.\r
3 //\r
4 //All rights reserved.\r
5 //\r
6 //Redistribution and use in source and binary forms, with or without\r
7 //modification, are permitted provided that the following conditions\r
8 //are met:\r
9 //\r
10 //    Redistributions of source code must retain the above copyright\r
11 //    notice, this list of conditions and the following disclaimer.\r
12 //\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
17 //\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
21 //\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
34 \r
35 //\r
36 // Author: John Kessenich, LunarG\r
37 //\r
38 \r
39 //\r
40 // Disassembler for SPIR-V.\r
41 //\r
42 \r
43 #include <stdlib.h>\r
44 #include <assert.h>\r
45 #include <iomanip>\r
46 #include <stack>\r
47 #include <sstream>\r
48 \r
49 #include "GLSL450Lib.h"\r
50 extern const char* GlslStd450DebugNames[GLSL_STD_450::Count];\r
51 \r
52 #include "disassemble.h"\r
53 #include "doc.h"\r
54 \r
55 namespace spv {\r
56 \r
57 void Kill(std::ostream& out, const char* message)\r
58 {\r
59     out << std::endl << "Disassembly failed: " << message << std::endl;\r
60     exit(1);\r
61 }\r
62 \r
63 // Container class for a single instance of a SPIR-V stream, with methods for disassembly.\r
64 class SpirvStream {\r
65 public:\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
68 \r
69     void validate();\r
70     void processInstructions();\r
71 \r
72 protected:\r
73     SpirvStream(SpirvStream&);\r
74     SpirvStream& operator=(SpirvStream&);\r
75 \r
76     Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }\r
77 \r
78     // Output methods\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
88 \r
89     // Data\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
94 \r
95     // map each <id> to the instruction that created it\r
96     Id bound;\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
98 \r
99     std::vector<std::string> idDescriptor;    // the best text string known for explaining the <id>\r
100 \r
101     // schema\r
102     unsigned int schema;\r
103 \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
107 };\r
108 \r
109 void SpirvStream::validate()\r
110 {\r
111     size = (int)stream.size();\r
112     if (size < 4)\r
113         Kill(out, "stream is too short");\r
114 \r
115     // Magic number\r
116     if (stream[word++] != MagicNumber) {\r
117         out << "Bad magic number";\r
118         return;\r
119     }\r
120 \r
121     // Version\r
122     out << "// Module Version " << stream[word++] << std::endl;\r
123 \r
124     // Generator's magic number\r
125     out << "// Generated by (magic number): " << std::setbase(16) << stream[word++] << std::setbase(10) << std::endl;\r
126 \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
132     out << std::endl;\r
133 \r
134     // Reserved schema, must be 0 for now\r
135     schema = stream[word++];\r
136     if (schema != 0)\r
137         Kill(out, "bad schema, must be 0");\r
138 }\r
139 \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
143 {\r
144     // Instructions\r
145     while (word < size) {\r
146         int instructionStart = word;\r
147 \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
153         ++word;\r
154 \r
155         // Presence of full instruction\r
156         if (nextInst > size)\r
157             Kill(out, "stream instruction terminated too early");\r
158 \r
159         // Base for computing number of operands; will be updated as more is learned\r
160         unsigned numOperands = wordCount - 1;\r
161 \r
162         // Type <id>\r
163         Id typeId = 0;\r
164         if (InstructionDesc[opCode].hasType()) {\r
165             typeId = stream[word++];\r
166             --numOperands;\r
167         }\r
168 \r
169         // Result <id>\r
170         Id resultId = 0;\r
171         if (InstructionDesc[opCode].hasResult()) {\r
172             resultId = stream[word++];\r
173             --numOperands;\r
174 \r
175             // save instruction for future reference\r
176             idInstruction[resultId] = instructionStart;\r
177         }\r
178 \r
179         outputResultId(resultId);\r
180         outputTypeId(typeId);\r
181         outputIndent();\r
182 \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
187             word = nextInst;\r
188         }\r
189         out << std::endl;\r
190     }\r
191 }\r
192 \r
193 void SpirvStream::outputIndent()\r
194 {\r
195     for (int i = 0; i < (int)nestedControl.size(); ++i)\r
196         out << "  ";\r
197 }\r
198 \r
199 void SpirvStream::formatId(Id id, std::stringstream& idStream)\r
200 {\r
201     if (id >= bound)\r
202         Kill(out, "Bad <id>");\r
203 \r
204     if (id != 0) {\r
205         idStream << id;\r
206         if (idDescriptor[id].size() > 0)\r
207             idStream << "(" << idDescriptor[id] << ")";\r
208     }\r
209 }\r
210 \r
211 void SpirvStream::outputResultId(Id id)\r
212 {\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
217     if (id != 0)\r
218         out << ":";\r
219     else\r
220         out << " ";\r
221 \r
222     if (nestedControl.size() && id == nestedControl.top())\r
223         nestedControl.pop();\r
224 }\r
225 \r
226 void SpirvStream::outputTypeId(Id id)\r
227 {\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
232 }\r
233 \r
234 void SpirvStream::outputId(Id id)\r
235 {\r
236     if (id >= bound)\r
237         Kill(out, "Bad <id>");\r
238 \r
239     out << id;\r
240     if (idDescriptor[id].size() > 0)\r
241         out << "(" << idDescriptor[id] << ")";\r
242 }\r
243 \r
244 void SpirvStream::disassembleImmediates(int numOperands)\r
245 {\r
246     for (int i = 0; i < numOperands; ++i) {\r
247         out << stream[word++];\r
248         if (i < numOperands - 1)\r
249             out << " ";\r
250     }\r
251 }\r
252 \r
253 void SpirvStream::disassembleIds(int numOperands)\r
254 {\r
255     for (int i = 0; i < numOperands; ++i) {\r
256         outputId(stream[word++]);\r
257         if (i < numOperands - 1)\r
258             out << " ";\r
259     }\r
260 }\r
261 \r
262 void SpirvStream::disassembleString()\r
263 {\r
264     out << " \"";\r
265 \r
266     char* wordString;\r
267     bool done = false;\r
268     do {\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
273                 done = true;\r
274                 break;\r
275             }\r
276             out << *(wordString++);\r
277         }\r
278         ++word;\r
279     } while (! done);\r
280 \r
281     out << "\"";\r
282 }\r
283 \r
284 void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)\r
285 {\r
286     // Process the opcode\r
287 \r
288     out << (OpcodeString(opCode) + 2);  // leave out the "Op"\r
289 \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
296         }\r
297     } else if (opCode == OpExtInstImport)\r
298         idDescriptor[resultId] = (char*)(&stream[word]);\r
299     else {\r
300         if (idDescriptor[resultId].size() == 0) {\r
301             switch (opCode) {\r
302             case OpTypeInt:\r
303                 idDescriptor[resultId] = "int";\r
304                 break;\r
305             case OpTypeFloat:\r
306                 idDescriptor[resultId] = "float";\r
307                 break;\r
308             case OpTypeBool:\r
309                 idDescriptor[resultId] = "bool";\r
310                 break;\r
311             case OpTypeStruct:\r
312                 idDescriptor[resultId] = "struct";\r
313                 break;\r
314             case OpTypePointer:\r
315                 idDescriptor[resultId] = "ptr";\r
316                 break;\r
317             case OpTypeVector:\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
328                 default: break;\r
329                 }\r
330                 break;\r
331             default:\r
332                 break;\r
333             }\r
334         }\r
335     }\r
336 \r
337     // Process the operands.  Note, a new context-dependent set could be\r
338     // swapped in mid-traversal.\r
339 \r
340     // Handle textures specially, so can put out helpful strings.\r
341     if (opCode == OpTypeSampler) {\r
342         disassembleIds(1);\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
348         }\r
349         out << (stream[word++] != 0 ? " array" : "");\r
350         out << (stream[word++] != 0 ? " depth" : "");\r
351         out << (stream[word++] != 0 ? " multi-sampled" : "");\r
352         return;\r
353     }\r
354 \r
355     // Handle all the parameterized operands\r
356     for (int op = 0; op < InstructionDesc[opCode].operands.getNum(); ++op) {\r
357         out << " ";\r
358         OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);\r
359         switch (operandClass) {\r
360         case OperandId:\r
361             disassembleIds(1);\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
365             break;\r
366         case OperandOptionalId:\r
367         case OperandVariableIds:\r
368             disassembleIds(numOperands);\r
369             return;\r
370         case OperandVariableLiterals:\r
371             if (opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) {\r
372                 out << BuiltInString(stream[word++]);\r
373                 --numOperands;\r
374                 ++op;\r
375             }\r
376             disassembleImmediates(numOperands);\r
377             return;\r
378         case OperandVariableLiteralId:\r
379             while (numOperands > 0) {\r
380                 out << std::endl;\r
381                 outputResultId(0);\r
382                 outputTypeId(0);\r
383                 outputIndent();\r
384                 out << "     case ";\r
385                 disassembleImmediates(1);\r
386                 out << ": ";\r
387                 disassembleIds(1);\r
388                 numOperands -= 2;\r
389             }\r
390             return;\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
397             }\r
398             break;\r
399         case OperandLiteralString:\r
400             disassembleString();\r
401             return;\r
402         default:\r
403             assert(operandClass >= OperandSource && operandClass < OperandOpcode);\r
404 \r
405             if (OperandClassParams[operandClass].bitmask) {\r
406                 unsigned int mask = stream[word++];\r
407                 if (mask == 0)\r
408                     out << "None";\r
409                 else {\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
413                     }\r
414                 }\r
415                 break;\r
416             } else\r
417                 out << OperandClassParams[operandClass].getName(stream[word++]);\r
418 \r
419             break;\r
420         }\r
421         --numOperands;\r
422     }\r
423 \r
424     return;\r
425 }\r
426 \r
427 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)\r
428 {\r
429     SpirvStream SpirvStream(out, stream);\r
430     SpirvStream.validate();\r
431     SpirvStream.processInstructions();\r
432 }\r
433 \r
434 }; // end namespace spv\r