spvIR.h 14 KB

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