blob: faa2701fff7f1a56f18509652dad558704730dc6 [file] [log] [blame]
John Kessenich140f3df2015-06-26 16:58:36 -06001//
John Kessenich927608b2017-01-06 12:34:14 -07002// Copyright (C) 2014 LunarG, Inc.
John Kessenich140f3df2015-06-26 16:58:36 -06003//
John Kessenich927608b2017-01-06 12:34:14 -07004// All rights reserved.
John Kessenich140f3df2015-06-26 16:58:36 -06005//
John Kessenich927608b2017-01-06 12:34:14 -07006// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions
8// are met:
John Kessenich140f3df2015-06-26 16:58:36 -06009//
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//
John Kessenich927608b2017-01-06 12:34:14 -070022// 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.
John Kessenich140f3df2015-06-26 16:58:36 -060034
John Kessenich140f3df2015-06-26 16:58:36 -060035// SPIRV-IR
36//
37// Simple in-memory representation (IR) of SPIRV. Just for holding
38// Each function's CFG of blocks. Has this hierarchy:
John Kessenichecba76f2017-01-06 00:34:48 -070039// - Module, which is a list of
40// - Function, which is a list of
41// - Block, which is a list of
John Kessenich140f3df2015-06-26 16:58:36 -060042// - Instruction
43//
44
45#pragma once
46#ifndef spvIR_H
47#define spvIR_H
48
John Kessenich5e4b1242015-08-06 22:53:06 -060049#include "spirv.hpp"
John Kessenich140f3df2015-06-26 16:58:36 -060050
Dejan Mircevskie537b8b2016-01-10 19:37:00 -050051#include <algorithm>
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -050052#include <cassert>
53#include <functional>
John Kessenich140f3df2015-06-26 16:58:36 -060054#include <iostream>
Andrew Woloszynb7946d12016-01-18 09:23:56 -050055#include <memory>
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -050056#include <vector>
John Kessenich140f3df2015-06-26 16:58:36 -060057
58namespace spv {
59
Dejan Mircevski38d039d2016-01-19 10:01:27 -050060class Block;
John Kessenich140f3df2015-06-26 16:58:36 -060061class Function;
62class Module;
63
64const Id NoResult = 0;
65const Id NoType = 0;
66
John Kessenich4016e382016-07-15 11:53:56 -060067const Decoration NoPrecision = DecorationMax;
LoopDawg2baa7742017-08-31 13:44:34 -060068
69#ifdef __GNUC__
70# define POTENTIALLY_UNUSED __attribute__((unused))
71#else
72# define POTENTIALLY_UNUSED
73#endif
74
75POTENTIALLY_UNUSED
John Kessenichecba76f2017-01-06 00:34:48 -070076const MemorySemanticsMask MemorySemanticsAllMemory =
John Kessenich82979362017-12-11 04:02:24 -070077 (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask |
John Kessenich48891672016-01-22 10:15:03 -070078 MemorySemanticsWorkgroupMemoryMask |
John Kessenich48891672016-01-22 10:15:03 -070079 MemorySemanticsAtomicCounterMemoryMask |
80 MemorySemanticsImageMemoryMask);
John Kessenich140f3df2015-06-26 16:58:36 -060081
82//
83// SPIR-V IR instruction.
84//
85
86class Instruction {
87public:
Dejan Mircevski34bc6c32016-01-19 14:08:32 -050088 Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
89 explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
John Kessenich55e7d112015-11-15 21:33:39 -070090 virtual ~Instruction() {}
John Kessenich140f3df2015-06-26 16:58:36 -060091 void addIdOperand(Id id) { operands.push_back(id); }
92 void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }
93 void addStringOperand(const char* str)
94 {
John Kessenich140f3df2015-06-26 16:58:36 -060095 unsigned int word;
96 char* wordString = (char*)&word;
97 char* wordPtr = wordString;
98 int charCount = 0;
99 char c;
100 do {
101 c = *(str++);
102 *(wordPtr++) = c;
103 ++charCount;
104 if (charCount == 4) {
John Kessenich55e7d112015-11-15 21:33:39 -0700105 addImmediateOperand(word);
John Kessenich140f3df2015-06-26 16:58:36 -0600106 wordPtr = wordString;
107 charCount = 0;
108 }
109 } while (c != 0);
110
111 // deal with partial last word
112 if (charCount > 0) {
113 // pad with 0s
114 for (; charCount < 4; ++charCount)
115 *(wordPtr++) = 0;
John Kessenich55e7d112015-11-15 21:33:39 -0700116 addImmediateOperand(word);
John Kessenich140f3df2015-06-26 16:58:36 -0600117 }
118 }
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500119 void setBlock(Block* b) { block = b; }
Dejan Mircevskifa242902016-01-19 11:31:55 -0500120 Block* getBlock() const { return block; }
John Kessenich140f3df2015-06-26 16:58:36 -0600121 Op getOpCode() const { return opCode; }
122 int getNumOperands() const { return (int)operands.size(); }
123 Id getResultId() const { return resultId; }
124 Id getTypeId() const { return typeId; }
125 Id getIdOperand(int op) const { return operands[op]; }
126 unsigned int getImmediateOperand(int op) const { return operands[op]; }
John Kessenich140f3df2015-06-26 16:58:36 -0600127
128 // Write out the binary form.
129 void dump(std::vector<unsigned int>& out) const
130 {
131 // Compute the wordCount
132 unsigned int wordCount = 1;
133 if (typeId)
134 ++wordCount;
135 if (resultId)
136 ++wordCount;
137 wordCount += (unsigned int)operands.size();
John Kessenich140f3df2015-06-26 16:58:36 -0600138
139 // Write out the beginning of the instruction
140 out.push_back(((wordCount) << WordCountShift) | opCode);
141 if (typeId)
142 out.push_back(typeId);
143 if (resultId)
144 out.push_back(resultId);
145
146 // Write out the operands
147 for (int op = 0; op < (int)operands.size(); ++op)
148 out.push_back(operands[op]);
John Kessenich140f3df2015-06-26 16:58:36 -0600149 }
150
151protected:
152 Instruction(const Instruction&);
153 Id resultId;
154 Id typeId;
155 Op opCode;
156 std::vector<Id> operands;
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500157 Block* block;
John Kessenich140f3df2015-06-26 16:58:36 -0600158};
159
160//
161// SPIR-V IR block.
162//
163
164class Block {
165public:
166 Block(Id id, Function& parent);
167 virtual ~Block()
168 {
John Kessenich140f3df2015-06-26 16:58:36 -0600169 }
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -0500170
John Kessenich140f3df2015-06-26 16:58:36 -0600171 Id getId() { return instructions.front()->getResultId(); }
172
173 Function& getParent() const { return parent; }
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500174 void addInstruction(std::unique_ptr<Instruction> inst);
Dejan Mircevski5fe789b2016-01-17 23:27:45 -0500175 void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500176 void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500177 const std::vector<Block*>& getPredecessors() const { return predecessors; }
178 const std::vector<Block*>& getSuccessors() const { return successors; }
qiningda397332016-03-09 19:54:03 -0500179 const std::vector<std::unique_ptr<Instruction> >& getInstructions() const {
180 return instructions;
181 }
John Kessenich140f3df2015-06-26 16:58:36 -0600182 void setUnreachable() { unreachable = true; }
183 bool isUnreachable() const { return unreachable; }
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -0500184 // Returns the block's merge instruction, if one exists (otherwise null).
185 const Instruction* getMergeInstruction() const {
186 if (instructions.size() < 2) return nullptr;
Dejan Mircevski377f0ca2016-01-19 10:17:33 -0500187 const Instruction* nextToLast = (instructions.cend() - 2)->get();
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -0500188 switch (nextToLast->getOpCode()) {
189 case OpSelectionMerge:
190 case OpLoopMerge:
191 return nextToLast;
192 default:
193 return nullptr;
194 }
195 return nullptr;
196 }
John Kessenich140f3df2015-06-26 16:58:36 -0600197
198 bool isTerminated() const
199 {
200 switch (instructions.back()->getOpCode()) {
201 case OpBranch:
202 case OpBranchConditional:
203 case OpSwitch:
204 case OpKill:
205 case OpReturn:
206 case OpReturnValue:
207 return true;
208 default:
209 return false;
210 }
211 }
212
213 void dump(std::vector<unsigned int>& out) const
214 {
John Kessenich140f3df2015-06-26 16:58:36 -0600215 instructions[0]->dump(out);
216 for (int i = 0; i < (int)localVariables.size(); ++i)
217 localVariables[i]->dump(out);
218 for (int i = 1; i < (int)instructions.size(); ++i)
219 instructions[i]->dump(out);
220 }
221
222protected:
223 Block(const Block&);
224 Block& operator=(Block&);
225
226 // To enforce keeping parent and ownership in sync:
227 friend Function;
228
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500229 std::vector<std::unique_ptr<Instruction> > instructions;
Dejan Mircevski5fe789b2016-01-17 23:27:45 -0500230 std::vector<Block*> predecessors, successors;
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500231 std::vector<std::unique_ptr<Instruction> > localVariables;
John Kessenich140f3df2015-06-26 16:58:36 -0600232 Function& parent;
233
John Kessenichecba76f2017-01-06 00:34:48 -0700234 // track whether this block is known to be uncreachable (not necessarily
John Kessenich140f3df2015-06-26 16:58:36 -0600235 // true for all unreachable blocks, but should be set at least
236 // for the extraneous ones introduced by the builder).
237 bool unreachable;
238};
239
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -0500240// Traverses the control-flow graph rooted at root in an order suited for
241// readable code generation. Invokes callback at every node in the traversal
242// order.
243void inReadableOrder(Block* root, std::function<void(Block*)> callback);
244
John Kessenich140f3df2015-06-26 16:58:36 -0600245//
246// SPIR-V IR Function.
247//
248
249class Function {
250public:
251 Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
252 virtual ~Function()
253 {
254 for (int i = 0; i < (int)parameterInstructions.size(); ++i)
255 delete parameterInstructions[i];
256
257 for (int i = 0; i < (int)blocks.size(); ++i)
258 delete blocks[i];
259 }
260 Id getId() const { return functionInstruction.getResultId(); }
261 Id getParamId(int p) { return parameterInstructions[p]->getResultId(); }
262
263 void addBlock(Block* block) { blocks.push_back(block); }
Dejan Mircevskie537b8b2016-01-10 19:37:00 -0500264 void removeBlock(Block* block)
265 {
266 auto found = find(blocks.begin(), blocks.end(), block);
267 assert(found != blocks.end());
268 blocks.erase(found);
269 delete block;
270 }
John Kessenich140f3df2015-06-26 16:58:36 -0600271
272 Module& getParent() const { return parent; }
273 Block* getEntryBlock() const { return blocks.front(); }
274 Block* getLastBlock() const { return blocks.back(); }
qiningda397332016-03-09 19:54:03 -0500275 const std::vector<Block*>& getBlocks() const { return blocks; }
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500276 void addLocalVariable(std::unique_ptr<Instruction> inst);
John Kessenich140f3df2015-06-26 16:58:36 -0600277 Id getReturnType() const { return functionInstruction.getTypeId(); }
John Kessenich37789792017-03-21 23:56:40 -0600278
279 void setImplicitThis() { implicitThis = true; }
280 bool hasImplicitThis() const { return implicitThis; }
281
John Kessenich140f3df2015-06-26 16:58:36 -0600282 void dump(std::vector<unsigned int>& out) const
283 {
284 // OpFunction
285 functionInstruction.dump(out);
286
287 // OpFunctionParameter
288 for (int p = 0; p < (int)parameterInstructions.size(); ++p)
289 parameterInstructions[p]->dump(out);
290
291 // Blocks
Dejan Mircevski44bfb0d2016-01-18 16:18:37 -0500292 inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); });
John Kessenich140f3df2015-06-26 16:58:36 -0600293 Instruction end(0, 0, OpFunctionEnd);
294 end.dump(out);
295 }
296
297protected:
298 Function(const Function&);
299 Function& operator=(Function&);
300
301 Module& parent;
302 Instruction functionInstruction;
303 std::vector<Instruction*> parameterInstructions;
304 std::vector<Block*> blocks;
John Kessenich37789792017-03-21 23:56:40 -0600305 bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
John Kessenich140f3df2015-06-26 16:58:36 -0600306};
307
308//
309// SPIR-V IR Module.
310//
311
312class Module {
313public:
314 Module() {}
315 virtual ~Module()
316 {
317 // TODO delete things
318 }
319
320 void addFunction(Function *fun) { functions.push_back(fun); }
321
322 void mapInstruction(Instruction *instruction)
323 {
324 spv::Id resultId = instruction->getResultId();
325 // map the instruction's result id
326 if (resultId >= idToInstruction.size())
327 idToInstruction.resize(resultId + 16);
328 idToInstruction[resultId] = instruction;
329 }
330
331 Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
qiningda397332016-03-09 19:54:03 -0500332 const std::vector<Function*>& getFunctions() const { return functions; }
John Kessenich140f3df2015-06-26 16:58:36 -0600333 spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); }
John Kesseniche00e72d2015-12-08 20:48:49 -0700334 StorageClass getStorageClass(Id typeId) const
335 {
336 assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer);
337 return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
338 }
339
John Kessenich140f3df2015-06-26 16:58:36 -0600340 void dump(std::vector<unsigned int>& out) const
341 {
342 for (int f = 0; f < (int)functions.size(); ++f)
343 functions[f]->dump(out);
344 }
345
346protected:
347 Module(const Module&);
348 std::vector<Function*> functions;
349
350 // map from result id to instruction having that result id
351 std::vector<Instruction*> idToInstruction;
352
353 // map from a result id to its type id
354};
355
356//
357// Implementation (it's here due to circular type definitions).
358//
359
360// Add both
361// - the OpFunction instruction
362// - all the OpFunctionParameter instructions
363__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
John Kessenich37789792017-03-21 23:56:40 -0600364 : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false)
John Kessenich140f3df2015-06-26 16:58:36 -0600365{
366 // OpFunction
367 functionInstruction.addImmediateOperand(FunctionControlMaskNone);
368 functionInstruction.addIdOperand(functionType);
369 parent.mapInstruction(&functionInstruction);
370 parent.addFunction(this);
371
372 // OpFunctionParameter
373 Instruction* typeInst = parent.getInstruction(functionType);
374 int numParams = typeInst->getNumOperands() - 1;
375 for (int p = 0; p < numParams; ++p) {
376 Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
377 parent.mapInstruction(param);
378 parameterInstructions.push_back(param);
379 }
380}
381
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500382__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
John Kessenich140f3df2015-06-26 16:58:36 -0600383{
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500384 Instruction* raw_instruction = inst.get();
385 blocks[0]->addLocalVariable(std::move(inst));
386 parent.mapInstruction(raw_instruction);
John Kessenich140f3df2015-06-26 16:58:36 -0600387}
388
389__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
390{
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500391 instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, OpLabel)));
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500392 instructions.back()->setBlock(this);
Dejan Mircevski377f0ca2016-01-19 10:17:33 -0500393 parent.getParent().mapInstruction(instructions.back().get());
John Kessenich140f3df2015-06-26 16:58:36 -0600394}
395
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500396__inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
John Kessenich140f3df2015-06-26 16:58:36 -0600397{
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500398 Instruction* raw_instruction = inst.get();
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500399 instructions.push_back(std::move(inst));
Dejan Mircevski38d039d2016-01-19 10:01:27 -0500400 raw_instruction->setBlock(this);
Andrew Woloszynb7946d12016-01-18 09:23:56 -0500401 if (raw_instruction->getResultId())
402 parent.getParent().mapInstruction(raw_instruction);
John Kessenich140f3df2015-06-26 16:58:36 -0600403}
404
405}; // end spv namespace
406
407#endif // spvIR_H