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