spvIR.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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. // ids can't be 0
  88. assert(id);
  89. operands.push_back(id);
  90. idOperand.push_back(true);
  91. }
  92. void addImmediateOperand(unsigned int immediate) {
  93. operands.push_back(immediate);
  94. idOperand.push_back(false);
  95. }
  96. void setImmediateOperand(unsigned idx, unsigned int immediate) {
  97. assert(!idOperand[idx]);
  98. operands[idx] = immediate;
  99. }
  100. void addStringOperand(const char* str)
  101. {
  102. unsigned int word = 0;
  103. unsigned int shiftAmount = 0;
  104. char c;
  105. do {
  106. c = *(str++);
  107. word |= ((unsigned int)c) << shiftAmount;
  108. shiftAmount += 8;
  109. if (shiftAmount == 32) {
  110. addImmediateOperand(word);
  111. word = 0;
  112. shiftAmount = 0;
  113. }
  114. } while (c != 0);
  115. // deal with partial last word
  116. if (shiftAmount > 0) {
  117. addImmediateOperand(word);
  118. }
  119. }
  120. bool isIdOperand(int op) const { return idOperand[op]; }
  121. void setBlock(Block* b) { block = b; }
  122. Block* getBlock() const { return block; }
  123. Op getOpCode() const { return opCode; }
  124. int getNumOperands() const
  125. {
  126. assert(operands.size() == idOperand.size());
  127. return (int)operands.size();
  128. }
  129. Id getResultId() const { return resultId; }
  130. Id getTypeId() const { return typeId; }
  131. Id getIdOperand(int op) const {
  132. assert(idOperand[op]);
  133. return operands[op];
  134. }
  135. unsigned int getImmediateOperand(int op) const {
  136. assert(!idOperand[op]);
  137. return operands[op];
  138. }
  139. // Write out the binary form.
  140. void dump(std::vector<unsigned int>& out) const
  141. {
  142. // Compute the wordCount
  143. unsigned int wordCount = 1;
  144. if (typeId)
  145. ++wordCount;
  146. if (resultId)
  147. ++wordCount;
  148. wordCount += (unsigned int)operands.size();
  149. // Write out the beginning of the instruction
  150. out.push_back(((wordCount) << WordCountShift) | opCode);
  151. if (typeId)
  152. out.push_back(typeId);
  153. if (resultId)
  154. out.push_back(resultId);
  155. // Write out the operands
  156. for (int op = 0; op < (int)operands.size(); ++op)
  157. out.push_back(operands[op]);
  158. }
  159. protected:
  160. Instruction(const Instruction&);
  161. Id resultId;
  162. Id typeId;
  163. Op opCode;
  164. std::vector<Id> operands; // operands, both <id> and immediates (both are unsigned int)
  165. std::vector<bool> idOperand; // true for operands that are <id>, false for immediates
  166. Block* block;
  167. };
  168. //
  169. // SPIR-V IR block.
  170. //
  171. class Block {
  172. public:
  173. Block(Id id, Function& parent);
  174. virtual ~Block()
  175. {
  176. }
  177. Id getId() { return instructions.front()->getResultId(); }
  178. Function& getParent() const { return parent; }
  179. void addInstruction(std::unique_ptr<Instruction> inst);
  180. void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
  181. void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
  182. const std::vector<Block*>& getPredecessors() const { return predecessors; }
  183. const std::vector<Block*>& getSuccessors() const { return successors; }
  184. const std::vector<std::unique_ptr<Instruction> >& getInstructions() const {
  185. return instructions;
  186. }
  187. const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
  188. void setUnreachable() { unreachable = true; }
  189. bool isUnreachable() const { return unreachable; }
  190. // Returns the block's merge instruction, if one exists (otherwise null).
  191. const Instruction* getMergeInstruction() const {
  192. if (instructions.size() < 2) return nullptr;
  193. const Instruction* nextToLast = (instructions.cend() - 2)->get();
  194. switch (nextToLast->getOpCode()) {
  195. case OpSelectionMerge:
  196. case OpLoopMerge:
  197. return nextToLast;
  198. default:
  199. return nullptr;
  200. }
  201. return nullptr;
  202. }
  203. // Change this block into a canonical dead merge block. Delete instructions
  204. // as necessary. A canonical dead merge block has only an OpLabel and an
  205. // OpUnreachable.
  206. void rewriteAsCanonicalUnreachableMerge() {
  207. assert(localVariables.empty());
  208. // Delete all instructions except for the label.
  209. assert(instructions.size() > 0);
  210. instructions.resize(1);
  211. successors.clear();
  212. addInstruction(std::unique_ptr<Instruction>(new Instruction(OpUnreachable)));
  213. }
  214. // Change this block into a canonical dead continue target branching to the
  215. // given header ID. Delete instructions as necessary. A canonical dead continue
  216. // target has only an OpLabel and an unconditional branch back to the corresponding
  217. // header.
  218. void rewriteAsCanonicalUnreachableContinue(Block* header) {
  219. assert(localVariables.empty());
  220. // Delete all instructions except for the label.
  221. assert(instructions.size() > 0);
  222. instructions.resize(1);
  223. successors.clear();
  224. // Add OpBranch back to the header.
  225. assert(header != nullptr);
  226. Instruction* branch = new Instruction(OpBranch);
  227. branch->addIdOperand(header->getId());
  228. addInstruction(std::unique_ptr<Instruction>(branch));
  229. successors.push_back(header);
  230. }
  231. bool isTerminated() const
  232. {
  233. switch (instructions.back()->getOpCode()) {
  234. case OpBranch:
  235. case OpBranchConditional:
  236. case OpSwitch:
  237. case OpKill:
  238. case OpTerminateInvocation:
  239. case OpReturn:
  240. case OpReturnValue:
  241. case OpUnreachable:
  242. return true;
  243. default:
  244. return false;
  245. }
  246. }
  247. void dump(std::vector<unsigned int>& out) const
  248. {
  249. instructions[0]->dump(out);
  250. for (int i = 0; i < (int)localVariables.size(); ++i)
  251. localVariables[i]->dump(out);
  252. for (int i = 1; i < (int)instructions.size(); ++i)
  253. instructions[i]->dump(out);
  254. }
  255. protected:
  256. Block(const Block&);
  257. Block& operator=(Block&);
  258. // To enforce keeping parent and ownership in sync:
  259. friend Function;
  260. std::vector<std::unique_ptr<Instruction> > instructions;
  261. std::vector<Block*> predecessors, successors;
  262. std::vector<std::unique_ptr<Instruction> > localVariables;
  263. Function& parent;
  264. // track whether this block is known to be uncreachable (not necessarily
  265. // true for all unreachable blocks, but should be set at least
  266. // for the extraneous ones introduced by the builder).
  267. bool unreachable;
  268. };
  269. // The different reasons for reaching a block in the inReadableOrder traversal.
  270. enum ReachReason {
  271. // Reachable from the entry block via transfers of control, i.e. branches.
  272. ReachViaControlFlow = 0,
  273. // A continue target that is not reachable via control flow.
  274. ReachDeadContinue,
  275. // A merge block that is not reachable via control flow.
  276. ReachDeadMerge
  277. };
  278. // Traverses the control-flow graph rooted at root in an order suited for
  279. // readable code generation. Invokes callback at every node in the traversal
  280. // order. The callback arguments are:
  281. // - the block,
  282. // - the reason we reached the block,
  283. // - if the reason was that block is an unreachable continue or unreachable merge block
  284. // then the last parameter is the corresponding header block.
  285. void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block* header)> callback);
  286. //
  287. // SPIR-V IR Function.
  288. //
  289. class Function {
  290. public:
  291. Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
  292. virtual ~Function()
  293. {
  294. for (int i = 0; i < (int)parameterInstructions.size(); ++i)
  295. delete parameterInstructions[i];
  296. for (int i = 0; i < (int)blocks.size(); ++i)
  297. delete blocks[i];
  298. }
  299. Id getId() const { return functionInstruction.getResultId(); }
  300. Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
  301. Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
  302. void addBlock(Block* block) { blocks.push_back(block); }
  303. void removeBlock(Block* block)
  304. {
  305. auto found = find(blocks.begin(), blocks.end(), block);
  306. assert(found != blocks.end());
  307. blocks.erase(found);
  308. delete block;
  309. }
  310. Module& getParent() const { return parent; }
  311. Block* getEntryBlock() const { return blocks.front(); }
  312. Block* getLastBlock() const { return blocks.back(); }
  313. const std::vector<Block*>& getBlocks() const { return blocks; }
  314. void addLocalVariable(std::unique_ptr<Instruction> inst);
  315. Id getReturnType() const { return functionInstruction.getTypeId(); }
  316. Id getFuncId() const { return functionInstruction.getResultId(); }
  317. void setReturnPrecision(Decoration precision)
  318. {
  319. if (precision == DecorationRelaxedPrecision)
  320. reducedPrecisionReturn = true;
  321. }
  322. Decoration getReturnPrecision() const
  323. { return reducedPrecisionReturn ? DecorationRelaxedPrecision : NoPrecision; }
  324. void setDebugLineInfo(Id fileName, int line, int column) {
  325. lineInstruction = std::unique_ptr<Instruction>{new Instruction(OpLine)};
  326. lineInstruction->addIdOperand(fileName);
  327. lineInstruction->addImmediateOperand(line);
  328. lineInstruction->addImmediateOperand(column);
  329. }
  330. bool hasDebugLineInfo() const { return lineInstruction != nullptr; }
  331. void setImplicitThis() { implicitThis = true; }
  332. bool hasImplicitThis() const { return implicitThis; }
  333. void addParamPrecision(unsigned param, Decoration precision)
  334. {
  335. if (precision == DecorationRelaxedPrecision)
  336. reducedPrecisionParams.insert(param);
  337. }
  338. Decoration getParamPrecision(unsigned param) const
  339. {
  340. return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ?
  341. DecorationRelaxedPrecision : NoPrecision;
  342. }
  343. void dump(std::vector<unsigned int>& out) const
  344. {
  345. // OpLine
  346. if (lineInstruction != nullptr) {
  347. lineInstruction->dump(out);
  348. }
  349. // OpFunction
  350. functionInstruction.dump(out);
  351. // OpFunctionParameter
  352. for (int p = 0; p < (int)parameterInstructions.size(); ++p)
  353. parameterInstructions[p]->dump(out);
  354. // Blocks
  355. inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); });
  356. Instruction end(0, 0, OpFunctionEnd);
  357. end.dump(out);
  358. }
  359. protected:
  360. Function(const Function&);
  361. Function& operator=(Function&);
  362. Module& parent;
  363. std::unique_ptr<Instruction> lineInstruction;
  364. Instruction functionInstruction;
  365. std::vector<Instruction*> parameterInstructions;
  366. std::vector<Block*> blocks;
  367. bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
  368. bool reducedPrecisionReturn;
  369. std::set<int> reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg
  370. };
  371. //
  372. // SPIR-V IR Module.
  373. //
  374. class Module {
  375. public:
  376. Module() {}
  377. virtual ~Module()
  378. {
  379. // TODO delete things
  380. }
  381. void addFunction(Function *fun) { functions.push_back(fun); }
  382. void mapInstruction(Instruction *instruction)
  383. {
  384. spv::Id resultId = instruction->getResultId();
  385. // map the instruction's result id
  386. if (resultId >= idToInstruction.size())
  387. idToInstruction.resize(resultId + 16);
  388. idToInstruction[resultId] = instruction;
  389. }
  390. Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
  391. const std::vector<Function*>& getFunctions() const { return functions; }
  392. spv::Id getTypeId(Id resultId) const {
  393. return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
  394. }
  395. StorageClass getStorageClass(Id typeId) const
  396. {
  397. assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer);
  398. return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
  399. }
  400. void dump(std::vector<unsigned int>& out) const
  401. {
  402. for (int f = 0; f < (int)functions.size(); ++f)
  403. functions[f]->dump(out);
  404. }
  405. protected:
  406. Module(const Module&);
  407. std::vector<Function*> functions;
  408. // map from result id to instruction having that result id
  409. std::vector<Instruction*> idToInstruction;
  410. // map from a result id to its type id
  411. };
  412. //
  413. // Implementation (it's here due to circular type definitions).
  414. //
  415. // Add both
  416. // - the OpFunction instruction
  417. // - all the OpFunctionParameter instructions
  418. __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
  419. : parent(parent), lineInstruction(nullptr),
  420. functionInstruction(id, resultType, OpFunction), implicitThis(false),
  421. reducedPrecisionReturn(false)
  422. {
  423. // OpFunction
  424. functionInstruction.addImmediateOperand(FunctionControlMaskNone);
  425. functionInstruction.addIdOperand(functionType);
  426. parent.mapInstruction(&functionInstruction);
  427. parent.addFunction(this);
  428. // OpFunctionParameter
  429. Instruction* typeInst = parent.getInstruction(functionType);
  430. int numParams = typeInst->getNumOperands() - 1;
  431. for (int p = 0; p < numParams; ++p) {
  432. Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
  433. parent.mapInstruction(param);
  434. parameterInstructions.push_back(param);
  435. }
  436. }
  437. __inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
  438. {
  439. Instruction* raw_instruction = inst.get();
  440. blocks[0]->addLocalVariable(std::move(inst));
  441. parent.mapInstruction(raw_instruction);
  442. }
  443. __inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
  444. {
  445. instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, OpLabel)));
  446. instructions.back()->setBlock(this);
  447. parent.getParent().mapInstruction(instructions.back().get());
  448. }
  449. __inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
  450. {
  451. Instruction* raw_instruction = inst.get();
  452. instructions.push_back(std::move(inst));
  453. raw_instruction->setBlock(this);
  454. if (raw_instruction->getResultId())
  455. parent.getParent().mapInstruction(raw_instruction);
  456. }
  457. } // end spv namespace
  458. #endif // spvIR_H