spvIR.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. //
  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. // SPIRV-IR
  35. //
  36. // Simple in-memory representation (IR) of SPIRV. Just for holding
  37. // Each function's CFG of blocks. Has this hierarchy:
  38. // - Module, which is a list of
  39. // - Function, which is a list of
  40. // - Block, which is a list of
  41. // - Instruction
  42. //
  43. #pragma once
  44. #ifndef spvIR_H
  45. #define spvIR_H
  46. #include "spirv.hpp"
  47. #include <algorithm>
  48. #include <cassert>
  49. #include <functional>
  50. #include <iostream>
  51. #include <memory>
  52. #include <vector>
  53. namespace spv {
  54. class Block;
  55. class Function;
  56. class Module;
  57. const Id NoResult = 0;
  58. const Id NoType = 0;
  59. const Decoration NoPrecision = DecorationMax;
  60. #ifdef __GNUC__
  61. # define POTENTIALLY_UNUSED __attribute__((unused))
  62. #else
  63. # define POTENTIALLY_UNUSED
  64. #endif
  65. POTENTIALLY_UNUSED
  66. const MemorySemanticsMask MemorySemanticsAllMemory =
  67. (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask |
  68. MemorySemanticsWorkgroupMemoryMask |
  69. MemorySemanticsAtomicCounterMemoryMask |
  70. MemorySemanticsImageMemoryMask);
  71. //
  72. // SPIR-V IR instruction.
  73. //
  74. class Instruction {
  75. public:
  76. Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
  77. explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
  78. virtual ~Instruction() {}
  79. void addIdOperand(Id id) { operands.push_back(id); }
  80. void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }
  81. void addStringOperand(const char* str)
  82. {
  83. unsigned int word;
  84. char* wordString = (char*)&word;
  85. char* wordPtr = wordString;
  86. int charCount = 0;
  87. char c;
  88. do {
  89. c = *(str++);
  90. *(wordPtr++) = c;
  91. ++charCount;
  92. if (charCount == 4) {
  93. addImmediateOperand(word);
  94. wordPtr = wordString;
  95. charCount = 0;
  96. }
  97. } while (c != 0);
  98. // deal with partial last word
  99. if (charCount > 0) {
  100. // pad with 0s
  101. for (; charCount < 4; ++charCount)
  102. *(wordPtr++) = 0;
  103. addImmediateOperand(word);
  104. }
  105. }
  106. void setBlock(Block* b) { block = b; }
  107. Block* getBlock() const { return block; }
  108. Op getOpCode() const { return opCode; }
  109. int getNumOperands() const { return (int)operands.size(); }
  110. Id getResultId() const { return resultId; }
  111. Id getTypeId() const { return typeId; }
  112. Id getIdOperand(int op) const { return operands[op]; }
  113. unsigned int getImmediateOperand(int op) const { return operands[op]; }
  114. // Write out the binary form.
  115. void dump(std::vector<unsigned int>& out) const
  116. {
  117. // Compute the wordCount
  118. unsigned int wordCount = 1;
  119. if (typeId)
  120. ++wordCount;
  121. if (resultId)
  122. ++wordCount;
  123. wordCount += (unsigned int)operands.size();
  124. // Write out the beginning of the instruction
  125. out.push_back(((wordCount) << WordCountShift) | opCode);
  126. if (typeId)
  127. out.push_back(typeId);
  128. if (resultId)
  129. out.push_back(resultId);
  130. // Write out the operands
  131. for (int op = 0; op < (int)operands.size(); ++op)
  132. out.push_back(operands[op]);
  133. }
  134. protected:
  135. Instruction(const Instruction&);
  136. Id resultId;
  137. Id typeId;
  138. Op opCode;
  139. std::vector<Id> operands;
  140. Block* block;
  141. };
  142. //
  143. // SPIR-V IR block.
  144. //
  145. class Block {
  146. public:
  147. Block(Id id, Function& parent);
  148. virtual ~Block()
  149. {
  150. }
  151. Id getId() { return instructions.front()->getResultId(); }
  152. Function& getParent() const { return parent; }
  153. void addInstruction(std::unique_ptr<Instruction> inst);
  154. void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
  155. void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
  156. const std::vector<Block*>& getPredecessors() const { return predecessors; }
  157. const std::vector<Block*>& getSuccessors() const { return successors; }
  158. const std::vector<std::unique_ptr<Instruction> >& getInstructions() const {
  159. return instructions;
  160. }
  161. void setUnreachable() { unreachable = true; }
  162. bool isUnreachable() const { return unreachable; }
  163. // Returns the block's merge instruction, if one exists (otherwise null).
  164. const Instruction* getMergeInstruction() const {
  165. if (instructions.size() < 2) return nullptr;
  166. const Instruction* nextToLast = (instructions.cend() - 2)->get();
  167. switch (nextToLast->getOpCode()) {
  168. case OpSelectionMerge:
  169. case OpLoopMerge:
  170. return nextToLast;
  171. default:
  172. return nullptr;
  173. }
  174. return nullptr;
  175. }
  176. bool isTerminated() const
  177. {
  178. switch (instructions.back()->getOpCode()) {
  179. case OpBranch:
  180. case OpBranchConditional:
  181. case OpSwitch:
  182. case OpKill:
  183. case OpReturn:
  184. case OpReturnValue:
  185. return true;
  186. default:
  187. return false;
  188. }
  189. }
  190. void dump(std::vector<unsigned int>& out) const
  191. {
  192. instructions[0]->dump(out);
  193. for (int i = 0; i < (int)localVariables.size(); ++i)
  194. localVariables[i]->dump(out);
  195. for (int i = 1; i < (int)instructions.size(); ++i)
  196. instructions[i]->dump(out);
  197. }
  198. protected:
  199. Block(const Block&);
  200. Block& operator=(Block&);
  201. // To enforce keeping parent and ownership in sync:
  202. friend Function;
  203. std::vector<std::unique_ptr<Instruction> > instructions;
  204. std::vector<Block*> predecessors, successors;
  205. std::vector<std::unique_ptr<Instruction> > localVariables;
  206. Function& parent;
  207. // track whether this block is known to be uncreachable (not necessarily
  208. // true for all unreachable blocks, but should be set at least
  209. // for the extraneous ones introduced by the builder).
  210. bool unreachable;
  211. };
  212. // Traverses the control-flow graph rooted at root in an order suited for
  213. // readable code generation. Invokes callback at every node in the traversal
  214. // order.
  215. void inReadableOrder(Block* root, std::function<void(Block*)> callback);
  216. //
  217. // SPIR-V IR Function.
  218. //
  219. class Function {
  220. public:
  221. Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
  222. virtual ~Function()
  223. {
  224. for (int i = 0; i < (int)parameterInstructions.size(); ++i)
  225. delete parameterInstructions[i];
  226. for (int i = 0; i < (int)blocks.size(); ++i)
  227. delete blocks[i];
  228. }
  229. Id getId() const { return functionInstruction.getResultId(); }
  230. Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
  231. Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
  232. void addBlock(Block* block) { blocks.push_back(block); }
  233. void removeBlock(Block* block)
  234. {
  235. auto found = find(blocks.begin(), blocks.end(), block);
  236. assert(found != blocks.end());
  237. blocks.erase(found);
  238. delete block;
  239. }
  240. Module& getParent() const { return parent; }
  241. Block* getEntryBlock() const { return blocks.front(); }
  242. Block* getLastBlock() const { return blocks.back(); }
  243. const std::vector<Block*>& getBlocks() const { return blocks; }
  244. void addLocalVariable(std::unique_ptr<Instruction> inst);
  245. Id getReturnType() const { return functionInstruction.getTypeId(); }
  246. void setImplicitThis() { implicitThis = true; }
  247. bool hasImplicitThis() const { return implicitThis; }
  248. void dump(std::vector<unsigned int>& out) const
  249. {
  250. // OpFunction
  251. functionInstruction.dump(out);
  252. // OpFunctionParameter
  253. for (int p = 0; p < (int)parameterInstructions.size(); ++p)
  254. parameterInstructions[p]->dump(out);
  255. // Blocks
  256. inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); });
  257. Instruction end(0, 0, OpFunctionEnd);
  258. end.dump(out);
  259. }
  260. protected:
  261. Function(const Function&);
  262. Function& operator=(Function&);
  263. Module& parent;
  264. Instruction functionInstruction;
  265. std::vector<Instruction*> parameterInstructions;
  266. std::vector<Block*> blocks;
  267. bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
  268. };
  269. //
  270. // SPIR-V IR Module.
  271. //
  272. class Module {
  273. public:
  274. Module() {}
  275. virtual ~Module()
  276. {
  277. // TODO delete things
  278. }
  279. void addFunction(Function *fun) { functions.push_back(fun); }
  280. void mapInstruction(Instruction *instruction)
  281. {
  282. spv::Id resultId = instruction->getResultId();
  283. // map the instruction's result id
  284. if (resultId >= idToInstruction.size())
  285. idToInstruction.resize(resultId + 16);
  286. idToInstruction[resultId] = instruction;
  287. }
  288. Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
  289. const std::vector<Function*>& getFunctions() const { return functions; }
  290. spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); }
  291. StorageClass getStorageClass(Id typeId) const
  292. {
  293. assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer);
  294. return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
  295. }
  296. void dump(std::vector<unsigned int>& out) const
  297. {
  298. for (int f = 0; f < (int)functions.size(); ++f)
  299. functions[f]->dump(out);
  300. }
  301. protected:
  302. Module(const Module&);
  303. std::vector<Function*> functions;
  304. // map from result id to instruction having that result id
  305. std::vector<Instruction*> idToInstruction;
  306. // map from a result id to its type id
  307. };
  308. //
  309. // Implementation (it's here due to circular type definitions).
  310. //
  311. // Add both
  312. // - the OpFunction instruction
  313. // - all the OpFunctionParameter instructions
  314. __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
  315. : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false)
  316. {
  317. // OpFunction
  318. functionInstruction.addImmediateOperand(FunctionControlMaskNone);
  319. functionInstruction.addIdOperand(functionType);
  320. parent.mapInstruction(&functionInstruction);
  321. parent.addFunction(this);
  322. // OpFunctionParameter
  323. Instruction* typeInst = parent.getInstruction(functionType);
  324. int numParams = typeInst->getNumOperands() - 1;
  325. for (int p = 0; p < numParams; ++p) {
  326. Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
  327. parent.mapInstruction(param);
  328. parameterInstructions.push_back(param);
  329. }
  330. }
  331. __inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
  332. {
  333. Instruction* raw_instruction = inst.get();
  334. blocks[0]->addLocalVariable(std::move(inst));
  335. parent.mapInstruction(raw_instruction);
  336. }
  337. __inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
  338. {
  339. instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, OpLabel)));
  340. instructions.back()->setBlock(this);
  341. parent.getParent().mapInstruction(instructions.back().get());
  342. }
  343. __inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
  344. {
  345. Instruction* raw_instruction = inst.get();
  346. instructions.push_back(std::move(inst));
  347. raw_instruction->setBlock(this);
  348. if (raw_instruction->getResultId())
  349. parent.getParent().mapInstruction(raw_instruction);
  350. }
  351. }; // end spv namespace
  352. #endif // spvIR_H