glslang -> SPV: 1) Include post switch-break unreachable blocks and 2) Generally...
[platform/upstream/glslang.git] / SPIRV / spvIR.h
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 MERCHANTABILITY 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 LIABILITY, WHETHER IN CONTRACT, STRICT\r
31 //LIABILITY, 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 //POSSIBILITY OF SUCH DAMAGE.\r
34 \r
35 //\r
36 // Author: John Kessenich, LunarG\r
37 //\r
38 \r
39 // SPIRV-IR\r
40 //\r
41 // Simple in-memory representation (IR) of SPIRV.  Just for holding\r
42 // Each function's CFG of blocks.  Has this hierarchy:\r
43 //  - Module, which is a list of \r
44 //    - Function, which is a list of \r
45 //      - Block, which is a list of \r
46 //        - Instruction\r
47 //\r
48 \r
49 #pragma once\r
50 #ifndef spvIR_H\r
51 #define spvIR_H\r
52 \r
53 #include "spirv.h"\r
54 \r
55 #include <vector>\r
56 #include <iostream>\r
57 \r
58 namespace spv {\r
59 \r
60 class Function;\r
61 class Module;\r
62 \r
63 const Id NoResult = 0;\r
64 const Id NoType = 0;\r
65 \r
66 const unsigned int BadValue = 0xFFFFFFFF;\r
67 const Decoration NoPrecision = (Decoration)BadValue;\r
68 const MemorySemanticsMask MemorySemanticsAllMemory = (MemorySemanticsMask)0x3FF;\r
69 \r
70 //\r
71 // SPIR-V IR instruction.\r
72 //\r
73 \r
74 class Instruction {\r
75 public:\r
76     Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), string(0) { }\r
77     explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), string(0) { }\r
78     virtual ~Instruction()\r
79     {\r
80         delete string;\r
81     }\r
82     void addIdOperand(Id id) { operands.push_back(id); }\r
83     void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }\r
84     void addStringOperand(const char* str)\r
85     {\r
86         string = new std::vector<unsigned int>;\r
87         unsigned int word;\r
88         char* wordString = (char*)&word;\r
89         char* wordPtr = wordString;\r
90         int charCount = 0;\r
91         char c;\r
92         do {\r
93             c = *(str++);\r
94             *(wordPtr++) = c;\r
95             ++charCount;\r
96             if (charCount == 4) {\r
97                 string->push_back(word);\r
98                 wordPtr = wordString;\r
99                 charCount = 0;\r
100             }\r
101         } while (c != 0);\r
102 \r
103         // deal with partial last word\r
104         if (charCount > 0) {\r
105             // pad with 0s\r
106             for (; charCount < 4; ++charCount)\r
107                 *(wordPtr++) = 0;\r
108             string->push_back(word);\r
109         }\r
110 \r
111         originalString = str;\r
112     }\r
113     Op getOpCode() const { return opCode; }\r
114     int getNumOperands() const { return operands.size(); }\r
115     Id getResultId() const { return resultId; }\r
116     Id getTypeId() const { return typeId; }\r
117     Id getIdOperand(int op) const { return operands[op]; }\r
118     unsigned int getImmediateOperand(int op) const { return operands[op]; }\r
119     const char* getStringOperand() const { return originalString.c_str(); }\r
120 \r
121     // Write out the binary form.\r
122     void dump(std::vector<unsigned int>& out) const\r
123     {\r
124         // Compute the wordCount\r
125         unsigned int wordCount = 1;\r
126         if (typeId)\r
127             ++wordCount;\r
128         if (resultId)\r
129             ++wordCount;\r
130         wordCount += operands.size();\r
131         if (string)\r
132             wordCount += string->size();\r
133 \r
134         // Write out the beginning of the instruction\r
135         out.push_back(((wordCount) << WordCountShift) | opCode);\r
136         if (typeId)\r
137             out.push_back(typeId);\r
138         if (resultId)\r
139             out.push_back(resultId);\r
140 \r
141         // Write out the operands\r
142         for (int op = 0; op < (int)operands.size(); ++op)\r
143             out.push_back(operands[op]);\r
144         if (string)\r
145             for (int op = 0; op < (int)string->size(); ++op)\r
146                 out.push_back((*string)[op]);\r
147     }\r
148 \r
149 protected:\r
150     Instruction(const Instruction&);\r
151     Id resultId;\r
152     Id typeId;\r
153     Op opCode;\r
154     std::vector<Id> operands;\r
155     std::vector<unsigned int>* string; // usually non-existent\r
156     std::string originalString;        // could be optimized away; convenience for getting string operand\r
157 };\r
158 \r
159 //\r
160 // SPIR-V IR block.\r
161 //\r
162 \r
163 class Block {\r
164 public:\r
165     Block(Id id, Function& parent);\r
166     virtual ~Block()\r
167     {\r
168         // TODO: free instructions\r
169     }\r
170     \r
171     Id getId() { return instructions.front()->getResultId(); }\r
172 \r
173     Function& getParent() const { return parent; }\r
174     void addInstruction(Instruction* inst);\r
175     void addPredecessor(Block* pred) { predecessors.push_back(pred); }\r
176     void addLocalVariable(Instruction* inst) { localVariables.push_back(inst); }\r
177     int getNumPredecessors() const { return (int)predecessors.size(); }\r
178     void setUnreachable() { unreachable = true; }\r
179     bool isUnreachable() const { return unreachable; }\r
180 \r
181     bool isTerminated() const\r
182     {\r
183         switch (instructions.back()->getOpCode()) {\r
184         case OpBranch:\r
185         case OpBranchConditional:\r
186         case OpSwitch:\r
187         case OpKill:\r
188         case OpReturn:\r
189         case OpReturnValue:\r
190             return true;\r
191         default:\r
192             return false;\r
193         }\r
194     }\r
195 \r
196     void dump(std::vector<unsigned int>& out) const\r
197     {\r
198         // skip the degenerate unreachable blocks\r
199         // TODO: code gen: skip all unreachable blocks (transitive closure)\r
200         //                 (but, until that's done safer to keep non-degenerate unreachable blocks, in case others depend on something)\r
201         if (unreachable && instructions.size() <= 2)\r
202             return;\r
203 \r
204         instructions[0]->dump(out);\r
205         for (int i = 0; i < (int)localVariables.size(); ++i)\r
206             localVariables[i]->dump(out);\r
207         for (int i = 1; i < (int)instructions.size(); ++i)\r
208             instructions[i]->dump(out);\r
209     }\r
210 \r
211 protected:\r
212     Block(const Block&);\r
213 \r
214     // To enforce keeping parent and ownership in sync:\r
215     friend Function;\r
216 \r
217     std::vector<Instruction*> instructions;\r
218     std::vector<Block*> predecessors;\r
219     std::vector<Instruction*> localVariables;\r
220     Function& parent;\r
221 \r
222     // track whether this block is known to be uncreachable (not necessarily \r
223     // true for all unreachable blocks, but should be set at least\r
224     // for the extraneous ones introduced by the builder).\r
225     bool unreachable;\r
226 };\r
227 \r
228 //\r
229 // SPIR-V IR Function.\r
230 //\r
231 \r
232 class Function {\r
233 public:\r
234     Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);\r
235     virtual ~Function()\r
236     {\r
237         for (int i = 0; i < (int)parameterInstructions.size(); ++i)\r
238             delete parameterInstructions[i];\r
239 \r
240         for (int i = 0; i < (int)blocks.size(); ++i)\r
241             delete blocks[i];\r
242     }\r
243     Id getId() const { return functionInstruction.getResultId(); }\r
244     Id getParamId(int p) { return parameterInstructions[p]->getResultId(); }\r
245 \r
246     void addBlock(Block* block) { blocks.push_back(block); }\r
247     void popBlock(Block* block) { assert(blocks.back() == block); blocks.pop_back(); }\r
248 \r
249     Module& getParent() const { return parent; }\r
250     Block* getEntryBlock() const { return blocks.front(); }\r
251     Block* getLastBlock() const { return blocks.back(); }\r
252     void addLocalVariable(Instruction* inst);\r
253     Id getReturnType() const { return functionInstruction.getTypeId(); }\r
254     void dump(std::vector<unsigned int>& out) const\r
255     {\r
256         // OpFunction\r
257         functionInstruction.dump(out);\r
258 \r
259         // OpFunctionParameter\r
260         for (int p = 0; p < (int)parameterInstructions.size(); ++p)\r
261             parameterInstructions[p]->dump(out);\r
262 \r
263         // Blocks\r
264         for (int b = 0; b < (int)blocks.size(); ++b)\r
265             blocks[b]->dump(out);\r
266         Instruction end(0, 0, OpFunctionEnd);\r
267         end.dump(out);\r
268     }\r
269 \r
270 protected:\r
271     Function(const Function&);\r
272     Module& parent;\r
273     Instruction functionInstruction;\r
274     std::vector<Instruction*> parameterInstructions;\r
275     std::vector<Block*> blocks;\r
276 };\r
277 \r
278 //\r
279 // SPIR-V IR Module.\r
280 //\r
281 \r
282 class Module {\r
283 public:\r
284     Module() {}\r
285     virtual ~Module()\r
286     {\r
287         // TODO delete things\r
288     }\r
289 \r
290     void addFunction(Function *fun) { functions.push_back(fun); }\r
291 \r
292     void mapInstruction(Instruction *instruction)\r
293     {\r
294         spv::Id resultId = instruction->getResultId();\r
295         // map the instruction's result id\r
296         if (resultId >= idToInstruction.size())\r
297             idToInstruction.resize(resultId + 16);\r
298         idToInstruction[resultId] = instruction;\r
299     }\r
300 \r
301     Instruction* getInstruction(Id id) const { return idToInstruction[id]; }\r
302     spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); }\r
303     StorageClass getStorageClass(Id typeId) const { return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0); }\r
304     void dump(std::vector<unsigned int>& out) const\r
305     {\r
306         for (int f = 0; f < (int)functions.size(); ++f)\r
307             functions[f]->dump(out);\r
308     }\r
309 \r
310 protected:\r
311     Module(const Module&);\r
312     std::vector<Function*> functions;\r
313 \r
314     // map from result id to instruction having that result id\r
315     std::vector<Instruction*> idToInstruction;\r
316 \r
317     // map from a result id to its type id\r
318 };\r
319 \r
320 //\r
321 // Implementation (it's here due to circular type definitions).\r
322 //\r
323 \r
324 // Add both\r
325 // - the OpFunction instruction\r
326 // - all the OpFunctionParameter instructions\r
327 __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)\r
328     : parent(parent), functionInstruction(id, resultType, OpFunction)\r
329 {\r
330     // OpFunction\r
331     functionInstruction.addImmediateOperand(FunctionControlMaskNone);\r
332     functionInstruction.addIdOperand(functionType);\r
333     parent.mapInstruction(&functionInstruction);\r
334     parent.addFunction(this);\r
335 \r
336     // OpFunctionParameter\r
337     Instruction* typeInst = parent.getInstruction(functionType);\r
338     int numParams = typeInst->getNumOperands() - 1;\r
339     for (int p = 0; p < numParams; ++p) {\r
340         Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);\r
341         parent.mapInstruction(param);\r
342         parameterInstructions.push_back(param);\r
343     }\r
344 }\r
345 \r
346 __inline void Function::addLocalVariable(Instruction* inst)\r
347 {\r
348     blocks[0]->addLocalVariable(inst);\r
349     parent.mapInstruction(inst);\r
350 }\r
351 \r
352 __inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)\r
353 {\r
354     instructions.push_back(new Instruction(id, NoType, OpLabel));\r
355 }\r
356 \r
357 __inline void Block::addInstruction(Instruction* inst)\r
358 {\r
359     instructions.push_back(inst);\r
360     if (inst->getResultId())\r
361         parent.getParent().mapInstruction(inst);\r
362 }\r
363 \r
364 };  // end spv namespace\r
365 \r
366 #endif // spvIR_H\r