spvIR.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. //
  2. // Copyright (C) 2014 LunarG, Inc.
  3. // Copyright (C) 2015-2018 Google, Inc.
  4. //
  5. // All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions
  9. // are met:
  10. //
  11. // Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. //
  14. // Redistributions in binary form must reproduce the above
  15. // copyright notice, this list of conditions and the following
  16. // disclaimer in the documentation and/or other materials provided
  17. // with the distribution.
  18. //
  19. // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
  20. // contributors may be used to endorse or promote products derived
  21. // from this software without specific prior written permission.
  22. //
  23. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  24. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  25. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  26. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  27. // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  28. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  29. // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  30. // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  31. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  33. // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  34. // POSSIBILITY OF SUCH DAMAGE.
  35. // SPIRV-IR
  36. //
  37. // Simple in-memory representation (IR) of SPIRV. Just for holding
  38. // Each function's CFG of blocks. Has this hierarchy:
  39. // - Module, which is a list of
  40. // - Function, which is a list of
  41. // - Block, which is a list of
  42. // - Instruction
  43. //
  44. #pragma once
  45. #ifndef spvIR_H
  46. #define spvIR_H
  47. #include "spirv.hpp"
  48. #include <algorithm>
  49. #include <cassert>
  50. #include <functional>
  51. #include <iostream>
  52. #include <memory>
  53. #include <vector>
  54. #include <set>
  55. namespace spv {
  56. class Block;
  57. class Function;
  58. class Module;
  59. const Id NoResult = 0;
  60. const Id NoType = 0;
  61. const Decoration NoPrecision = DecorationMax;
  62. #ifdef __GNUC__
  63. # define POTENTIALLY_UNUSED __attribute__((unused))
  64. #else
  65. # define POTENTIALLY_UNUSED
  66. #endif
  67. POTENTIALLY_UNUSED
  68. const MemorySemanticsMask MemorySemanticsAllMemory =
  69. (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask |
  70. MemorySemanticsWorkgroupMemoryMask |
  71. MemorySemanticsAtomicCounterMemoryMask |
  72. MemorySemanticsImageMemoryMask);
  73. struct IdImmediate {
  74. bool isId; // true if word is an Id, false if word is an immediate
  75. unsigned word;
  76. IdImmediate(bool i, unsigned w) : isId(i), word(w) {}
  77. };
  78. //
  79. // SPIR-V IR instruction.
  80. //
  81. class Instruction {
  82. public:
  83. Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
  84. explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
  85. virtual ~Instruction() {}
  86. void addIdOperand(Id id) {
  87. operands.push_back(id);
  88. idOperand.push_back(true);
  89. }
  90. void addImmediateOperand(unsigned int immediate) {
  91. operands.push_back(immediate);
  92. idOperand.push_back(false);
  93. }
  94. void setImmediateOperand(unsigned idx, unsigned int immediate) {
  95. assert(!idOperand[idx]);
  96. operands[idx] = immediate;
  97. }
  98. void addStringOperand(const char* str)
  99. {
  100. unsigned int word;
  101. char* wordString = (char*)&word;
  102. char* wordPtr = wordString;
  103. int charCount = 0;
  104. char c;
  105. do {
  106. c = *(str++);
  107. *(wordPtr++) = c;
  108. ++charCount;
  109. if (charCount == 4) {
  110. addImmediateOperand(word);
  111. wordPtr = wordString;
  112. charCount = 0;
  113. }
  114. } while (c != 0);
  115. // deal with partial last word
  116. if (charCount > 0) {
  117. // pad with 0s
  118. for (; charCount < 4; ++charCount)
  119. *(wordPtr++) = 0;
  120. addImmediateOperand(word);
  121. }
  122. }
  123. bool isIdOperand(int op) const { return idOperand[op]; }
  124. void setBlock(Block* b) { block = b; }
  125. Block* getBlock() const { return block; }
  126. Op getOpCode() const { return opCode; }
  127. int getNumOperands() const
  128. {
  129. assert(operands.size() == idOperand.size());
  130. return (int)operands.size();
  131. }
  132. Id getResultId() const { return resultId; }
  133. Id getTypeId() const { return typeId; }
  134. Id getIdOperand(int op) const {
  135. assert(idOperand[op]);
  136. return operands[op];
  137. }
  138. unsigned int getImmediateOperand(int op) const {
  139. assert(!idOperand[op]);
  140. return operands[op];
  141. }
  142. // Write out the binary form.
  143. void dump(std::vector<unsigned int>& out) const
  144. {
  145. // Compute the wordCount
  146. unsigned int wordCount = 1;
  147. if (typeId)
  148. ++wordCount;
  149. if (resultId)
  150. ++wordCount;
  151. wordCount += (unsigned int)operands.size();
  152. // Write out the beginning of the instruction
  153. out.push_back(((wordCount) << WordCountShift) | opCode);
  154. if (typeId)
  155. out.push_back(typeId);
  156. if (resultId)
  157. out.push_back(resultId);
  158. // Write out the operands
  159. for (int op = 0; op < (int)operands.size(); ++op)
  160. out.push_back(operands[op]);
  161. }
  162. protected:
  163. Instruction(const Instruction&);
  164. Id resultId;
  165. Id typeId;
  166. Op opCode;
  167. std::vector<Id> operands; // operands, both <id> and immediates (both are unsigned int)
  168. std::vector<bool> idOperand; // true for operands that are <id>, false for immediates
  169. Block* block;
  170. };
  171. //
  172. // SPIR-V IR block.
  173. //
  174. class Block {
  175. public:
  176. Block(Id id, Function& parent);
  177. virtual ~Block()
  178. {
  179. }
  180. Id getId() { return instructions.front()->getResultId(); }
  181. Function& getParent() const { return parent; }
  182. void addInstruction(std::unique_ptr<Instruction> inst);
  183. void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
  184. void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
  185. const std::vector<Block*>& getPredecessors() const { return predecessors; }
  186. const std::vector<Block*>& getSuccessors() const { return successors; }
  187. const std::vector<std::unique_ptr<Instruction> >& getInstructions() const {
  188. return instructions;
  189. }
  190. const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
  191. void setUnreachable() { unreachable = true; }
  192. bool isUnreachable() const { return unreachable; }
  193. // Returns the block's merge instruction, if one exists (otherwise null).
  194. const Instruction* getMergeInstruction() const {
  195. if (instructions.size() < 2) return nullptr;
  196. const Instruction* nextToLast = (instructions.cend() - 2)->get();
  197. switch (nextToLast->getOpCode()) {
  198. case OpSelectionMerge:
  199. case OpLoopMerge:
  200. return nextToLast;
  201. default:
  202. return nullptr;
  203. }
  204. return nullptr;
  205. }
  206. // Change this block into a canonical dead merge block. Delete instructions
  207. // as necessary. A canonical dead merge block has only an OpLabel and an
  208. // OpUnreachable.
  209. void rewriteAsCanonicalUnreachableMerge() {
  210. assert(localVariables.empty());
  211. // Delete all instructions except for the label.
  212. assert(instructions.size() > 0);
  213. instructions.resize(1);
  214. successors.clear();
  215. addInstruction(std::unique_ptr<Instruction>(new Instruction(OpUnreachable)));
  216. }
  217. // Change this block into a canonical dead continue target branching to the
  218. // given header ID. Delete instructions as necessary. A canonical dead continue
  219. // target has only an OpLabel and an unconditional branch back to the corresponding
  220. // header.
  221. void rewriteAsCanonicalUnreachableContinue(Block* header) {
  222. assert(localVariables.empty());
  223. // Delete all instructions except for the label.
  224. assert(instructions.size() > 0);
  225. instructions.resize(1);
  226. successors.clear();
  227. // Add OpBranch back to the header.
  228. assert(header != nullptr);
  229. Instruction* branch = new Instruction(OpBranch);
  230. branch->addIdOperand(header->getId());
  231. addInstruction(std::unique_ptr<Instruction>(branch));
  232. successors.push_back(header);
  233. }
  234. bool isTerminated() const
  235. {
  236. switch (instructions.back()->getOpCode()) {
  237. case OpBranch:
  238. case OpBranchConditional:
  239. case OpSwitch:
  240. case OpKill:
  241. case OpReturn:
  242. case OpReturnValue:
  243. case OpUnreachable:
  244. return true;
  245. default:
  246. return false;
  247. }
  248. }
  249. void dump(std::vector<unsigned int>& out) const
  250. {
  251. instructions[0]->dump(out);
  252. for (int i = 0; i < (int)localVariables.size(); ++i)
  253. localVariables[i]->dump(out);
  254. for (int i = 1; i < (int)instructions.size(); ++i)
  255. instructions[i]->dump(out);
  256. }
  257. protected:
  258. Block(const Block&);
  259. Block& operator=(Block&);
  260. // To enforce keeping parent and ownership in sync:
  261. friend Function;
  262. std::vector<std::unique_ptr<Instruction> > instructions;
  263. std::vector<Block*> predecessors, successors;
  264. std::vector<std::unique_ptr<Instruction> > localVariables;
  265. Function& parent;
  266. // track whether this block is known to be uncreachable (not necessarily
  267. // true for all unreachable blocks, but should be set at least
  268. // for the extraneous ones introduced by the builder).
  269. bool unreachable;
  270. };
  271. // The different reasons for reaching a block in the inReadableOrder traversal.
  272. enum ReachReason {
  273. // Reachable from the entry block via transfers of control, i.e. branches.
  274. ReachViaControlFlow = 0,
  275. // A continue target that is not reachable via control flow.
  276. ReachDeadContinue,
  277. // A merge block that is not reachable via control flow.
  278. ReachDeadMerge
  279. };
  280. // Traverses the control-flow graph rooted at root in an order suited for
  281. // readable code generation. Invokes callback at every node in the traversal
  282. // order. The callback arguments are:
  283. // - the block,
  284. // - the reason we reached the block,
  285. // - if the reason was that block is an unreachable continue or unreachable merge block
  286. // then the last parameter is the corresponding header block.
  287. void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block* header)> callback);
  288. //
  289. // SPIR-V IR Function.
  290. //
  291. class Function {
  292. public:
  293. Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
  294. virtual ~Function()
  295. {
  296. for (int i = 0; i < (int)parameterInstructions.size(); ++i)
  297. delete parameterInstructions[i];
  298. for (int i = 0; i < (int)blocks.size(); ++i)
  299. delete blocks[i];
  300. }
  301. Id getId() const { return functionInstruction.getResultId(); }
  302. Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
  303. Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
  304. void addBlock(Block* block) { blocks.push_back(block); }
  305. void removeBlock(Block* block)
  306. {
  307. auto found = find(blocks.begin(), blocks.end(), block);
  308. assert(found != blocks.end());
  309. blocks.erase(found);
  310. delete block;
  311. }
  312. Module& getParent() const { return parent; }
  313. Block* getEntryBlock() const { return blocks.front(); }
  314. Block* getLastBlock() const { return blocks.back(); }
  315. const std::vector<Block*>& getBlocks() const { return blocks; }
  316. void addLocalVariable(std::unique_ptr<Instruction> inst);
  317. Id getReturnType() const { return functionInstruction.getTypeId(); }
  318. void setReturnPrecision(Decoration precision)
  319. {
  320. if (precision == DecorationRelaxedPrecision)
  321. reducedPrecisionReturn = true;
  322. }
  323. Decoration getReturnPrecision() const
  324. { return reducedPrecisionReturn ? DecorationRelaxedPrecision : NoPrecision; }
  325. void setImplicitThis() { implicitThis = true; }
  326. bool hasImplicitThis() const { return implicitThis; }
  327. void addParamPrecision(unsigned param, Decoration precision)
  328. {
  329. if (precision == DecorationRelaxedPrecision)
  330. reducedPrecisionParams.insert(param);
  331. }
  332. Decoration getParamPrecision(unsigned param) const
  333. {
  334. return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ?
  335. DecorationRelaxedPrecision : NoPrecision;
  336. }
  337. void dump(std::vector<unsigned int>& out) const
  338. {
  339. // OpFunction
  340. functionInstruction.dump(out);
  341. // OpFunctionParameter
  342. for (int p = 0; p < (int)parameterInstructions.size(); ++p)
  343. parameterInstructions[p]->dump(out);
  344. // Blocks
  345. inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); });
  346. Instruction end(0, 0, OpFunctionEnd);
  347. end.dump(out);
  348. }
  349. protected:
  350. Function(const Function&);
  351. Function& operator=(Function&);
  352. Module& parent;
  353. Instruction functionInstruction;
  354. std::vector<Instruction*> parameterInstructions;
  355. std::vector<Block*> blocks;
  356. bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
  357. bool reducedPrecisionReturn;
  358. std::set<int> reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg
  359. };
  360. //
  361. // SPIR-V IR Module.
  362. //
  363. class Module {
  364. public:
  365. Module() {}
  366. virtual ~Module()
  367. {
  368. // TODO delete things
  369. }
  370. void addFunction(Function *fun) { functions.push_back(fun); }
  371. void mapInstruction(Instruction *instruction)
  372. {
  373. spv::Id resultId = instruction->getResultId();
  374. // map the instruction's result id
  375. if (resultId >= idToInstruction.size())
  376. idToInstruction.resize(resultId + 16);
  377. idToInstruction[resultId] = instruction;
  378. }
  379. Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
  380. const std::vector<Function*>& getFunctions() const { return functions; }
  381. spv::Id getTypeId(Id resultId) const {
  382. return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
  383. }
  384. StorageClass getStorageClass(Id typeId) const
  385. {
  386. assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer);
  387. return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
  388. }
  389. void dump(std::vector<unsigned int>& out) const
  390. {
  391. for (int f = 0; f < (int)functions.size(); ++f)
  392. functions[f]->dump(out);
  393. }
  394. protected:
  395. Module(const Module&);
  396. std::vector<Function*> functions;
  397. // map from result id to instruction having that result id
  398. std::vector<Instruction*> idToInstruction;
  399. // map from a result id to its type id
  400. };
  401. //
  402. // Implementation (it's here due to circular type definitions).
  403. //
  404. // Add both
  405. // - the OpFunction instruction
  406. // - all the OpFunctionParameter instructions
  407. __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
  408. : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false),
  409. reducedPrecisionReturn(false)
  410. {
  411. // OpFunction
  412. functionInstruction.addImmediateOperand(FunctionControlMaskNone);
  413. functionInstruction.addIdOperand(functionType);
  414. parent.mapInstruction(&functionInstruction);
  415. parent.addFunction(this);
  416. // OpFunctionParameter
  417. Instruction* typeInst = parent.getInstruction(functionType);
  418. int numParams = typeInst->getNumOperands() - 1;
  419. for (int p = 0; p < numParams; ++p) {
  420. Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
  421. parent.mapInstruction(param);
  422. parameterInstructions.push_back(param);
  423. }
  424. }
  425. __inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
  426. {
  427. Instruction* raw_instruction = inst.get();
  428. blocks[0]->addLocalVariable(std::move(inst));
  429. parent.mapInstruction(raw_instruction);
  430. }
  431. __inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
  432. {
  433. instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, OpLabel)));
  434. instructions.back()->setBlock(this);
  435. parent.getParent().mapInstruction(instructions.back().get());
  436. }
  437. __inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
  438. {
  439. Instruction* raw_instruction = inst.get();
  440. instructions.push_back(std::move(inst));
  441. raw_instruction->setBlock(this);
  442. if (raw_instruction->getResultId())
  443. parent.getParent().mapInstruction(raw_instruction);
  444. }
  445. } // end spv namespace
  446. #endif // spvIR_H