Structure.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //===--- Structure.cpp - SPIR-V representation structures -----*- C++ -*---===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "clang/SPIRV/Structure.h"
  10. #include "BlockReadableOrder.h"
  11. #ifdef SUPPORT_QUERY_GIT_COMMIT_INFO
  12. #include "clang/Basic/Version.h"
  13. #else
  14. namespace clang {
  15. uint32_t getGitCommitCount() { return 0; }
  16. const char *getGitCommitHash() { return "<unknown-hash>"; }
  17. } // namespace clang
  18. #endif // SUPPORT_QUERY_GIT_COMMIT_INFO
  19. namespace clang {
  20. namespace spirv {
  21. namespace {
  22. constexpr uint32_t kGeneratorNumber = 14;
  23. constexpr uint32_t kToolVersion = 0;
  24. /// Chops the given original string into multiple smaller ones to make sure they
  25. /// can be encoded in a sequence of OpSourceContinued instructions following an
  26. /// OpSource instruction.
  27. void chopString(llvm::StringRef original,
  28. llvm::SmallVectorImpl<llvm::StringRef> *chopped) {
  29. const uint32_t maxCharInOpSource = 0xFFFFu - 5u; // Minus operands and nul
  30. const uint32_t maxCharInContinue = 0xFFFFu - 2u; // Minus opcode and nul
  31. chopped->clear();
  32. if (original.size() > maxCharInOpSource) {
  33. chopped->push_back(llvm::StringRef(original.data(), maxCharInOpSource));
  34. original = llvm::StringRef(original.data() + maxCharInOpSource,
  35. original.size() - maxCharInOpSource);
  36. while (original.size() > maxCharInContinue) {
  37. chopped->push_back(llvm::StringRef(original.data(), maxCharInContinue));
  38. original = llvm::StringRef(original.data() + maxCharInContinue,
  39. original.size() - maxCharInContinue);
  40. }
  41. if (!original.empty()) {
  42. chopped->push_back(original);
  43. }
  44. } else if (!original.empty()) {
  45. chopped->push_back(original);
  46. }
  47. }
  48. } // namespace
  49. // === Instruction implementations ===
  50. spv::Op Instruction::getOpcode() const {
  51. if (!isEmpty()) {
  52. return static_cast<spv::Op>(words.front() & spv::OpCodeMask);
  53. }
  54. return spv::Op::Max;
  55. }
  56. bool Instruction::isTerminator() const {
  57. switch (getOpcode()) {
  58. case spv::Op::OpBranch:
  59. case spv::Op::OpBranchConditional:
  60. case spv::Op::OpReturn:
  61. case spv::Op::OpReturnValue:
  62. case spv::Op::OpSwitch:
  63. case spv::Op::OpKill:
  64. case spv::Op::OpUnreachable:
  65. return true;
  66. default:
  67. return false;
  68. }
  69. }
  70. // === Basic block implementations ===
  71. BasicBlock::BasicBlock(BasicBlock &&that)
  72. : labelId(that.labelId), debugName(that.debugName),
  73. instructions(std::move(that.instructions)) {
  74. that.clear();
  75. }
  76. BasicBlock &BasicBlock::operator=(BasicBlock &&that) {
  77. labelId = that.labelId;
  78. debugName = that.debugName;
  79. instructions = std::move(that.instructions);
  80. that.clear();
  81. return *this;
  82. }
  83. void BasicBlock::take(InstBuilder *builder) {
  84. // Make sure we have a terminator instruction at the end.
  85. assert(isTerminated() && "found basic block without terminator");
  86. builder->opLabel(labelId).x();
  87. for (auto &inst : instructions) {
  88. builder->getConsumer()(inst.take());
  89. }
  90. clear();
  91. }
  92. bool BasicBlock::isTerminated() const {
  93. return !instructions.empty() && instructions.back().isTerminator();
  94. }
  95. // === Function implementations ===
  96. Function::Function(Function &&that)
  97. : resultType(that.resultType), resultId(that.resultId),
  98. funcControl(that.funcControl), funcType(that.funcType),
  99. parameters(std::move(that.parameters)),
  100. variables(std::move(that.variables)), blocks(std::move(that.blocks)) {
  101. that.clear();
  102. }
  103. Function &Function::operator=(Function &&that) {
  104. resultType = that.resultType;
  105. resultId = that.resultId;
  106. funcControl = that.funcControl;
  107. funcType = that.funcType;
  108. parameters = std::move(that.parameters);
  109. variables = std::move(that.variables);
  110. blocks = std::move(that.blocks);
  111. that.clear();
  112. return *this;
  113. }
  114. void Function::clear() {
  115. resultType = 0;
  116. resultId = 0;
  117. funcControl = spv::FunctionControlMask::MaskNone;
  118. funcType = 0;
  119. parameters.clear();
  120. variables.clear();
  121. blocks.clear();
  122. }
  123. void Function::take(InstBuilder *builder) {
  124. builder->opFunction(resultType, resultId, funcControl, funcType).x();
  125. // Write out all parameters.
  126. for (auto &param : parameters) {
  127. builder->opFunctionParameter(param.first, param.second).x();
  128. }
  129. if (!variables.empty()) {
  130. assert(!blocks.empty());
  131. }
  132. // Preprend all local variables to the entry block.
  133. // This is necessary since SPIR-V requires all local variables to be defined
  134. // at the very begining of the entry block.
  135. // We need to do it in the reverse order to guarantee variables have the
  136. // same definition order in SPIR-V as in the source code.
  137. for (auto it = variables.rbegin(), ie = variables.rend(); it != ie; ++it) {
  138. blocks.front()->prependInstruction(std::move(*it));
  139. }
  140. // Collect basic blocks in a human-readable order that satisfies SPIR-V
  141. // validation rules.
  142. std::vector<BasicBlock *> orderedBlocks;
  143. if (!blocks.empty()) {
  144. BlockReadableOrderVisitor(
  145. [&orderedBlocks](BasicBlock *block) { orderedBlocks.push_back(block); })
  146. .visit(blocks.front().get());
  147. }
  148. // Write out all basic blocks.
  149. for (auto *block : orderedBlocks) {
  150. block->take(builder);
  151. }
  152. builder->opFunctionEnd().x();
  153. clear();
  154. }
  155. void Function::addVariable(uint32_t varType, uint32_t varId,
  156. llvm::Optional<uint32_t> init) {
  157. variables.emplace_back(
  158. InstBuilder(nullptr)
  159. .opVariable(varType, varId, spv::StorageClass::Function, init)
  160. .take());
  161. }
  162. void Function::getReachableBasicBlocks(std::vector<BasicBlock *> *bbVec) const {
  163. if (!blocks.empty()) {
  164. BlockReadableOrderVisitor(
  165. [&bbVec](BasicBlock *block) { bbVec->push_back(block); })
  166. .visit(blocks.front().get());
  167. }
  168. }
  169. // === Module components implementations ===
  170. Header::Header()
  171. // We are using the unfied header, which shows spv::Version as the newest
  172. // version. But we need to stick to 1.0 for Vulkan consumption by default.
  173. : magicNumber(spv::MagicNumber), version(0x00010000),
  174. generator((kGeneratorNumber << 16) | kToolVersion), bound(0),
  175. reserved(0) {}
  176. void Header::collect(const WordConsumer &consumer) {
  177. std::vector<uint32_t> words;
  178. words.push_back(magicNumber);
  179. words.push_back(version);
  180. words.push_back(generator);
  181. words.push_back(bound);
  182. words.push_back(reserved);
  183. consumer(std::move(words));
  184. }
  185. bool DebugName::operator==(const DebugName &that) const {
  186. if (targetId == that.targetId && name == that.name) {
  187. if (memberIndex.hasValue()) {
  188. return that.memberIndex.hasValue() &&
  189. memberIndex.getValue() == that.memberIndex.getValue();
  190. }
  191. return !that.memberIndex.hasValue();
  192. }
  193. return false;
  194. }
  195. bool DebugName::operator<(const DebugName &that) const {
  196. // Sort according to target id first
  197. if (targetId != that.targetId)
  198. return targetId < that.targetId;
  199. if (memberIndex.hasValue()) {
  200. // Sort member decorations according to member index
  201. if (that.memberIndex.hasValue())
  202. return memberIndex.getValue() < that.memberIndex.getValue();
  203. // Decorations on the id itself goes before those on its members
  204. return false;
  205. }
  206. // Decorations on the id itself goes before those on its members
  207. if (that.memberIndex.hasValue())
  208. return true;
  209. return name < that.name;
  210. }
  211. // === Module implementations ===
  212. bool SPIRVModule::isEmpty() const {
  213. return header.bound == 0 && capabilities.empty() && extensions.empty() &&
  214. extInstSets.empty() && !addressingModel.hasValue() &&
  215. !memoryModel.hasValue() && entryPoints.empty() &&
  216. executionModes.empty() && debugNames.empty() && decorations.empty() &&
  217. types.empty() && constants.empty() && variables.empty() &&
  218. functions.empty();
  219. }
  220. void SPIRVModule::clear() {
  221. header.bound = 0;
  222. capabilities.clear();
  223. extensions.clear();
  224. extInstSets.clear();
  225. addressingModel = llvm::None;
  226. memoryModel = llvm::None;
  227. entryPoints.clear();
  228. executionModes.clear();
  229. debugNames.clear();
  230. decorations.clear();
  231. types.clear();
  232. constants.clear();
  233. variables.clear();
  234. functions.clear();
  235. }
  236. void SPIRVModule::take(InstBuilder *builder) {
  237. const auto &consumer = builder->getConsumer();
  238. // Order matters here.
  239. header.collect(consumer);
  240. for (auto &cap : capabilities) {
  241. builder->opCapability(cap).x();
  242. }
  243. for (auto &ext : extensions) {
  244. builder->opExtension(ext).x();
  245. }
  246. for (auto &inst : extInstSets) {
  247. builder->opExtInstImport(inst.second, inst.first).x();
  248. }
  249. if (addressingModel.hasValue() && memoryModel.hasValue()) {
  250. builder->opMemoryModel(*addressingModel, *memoryModel).x();
  251. }
  252. for (auto &inst : entryPoints) {
  253. builder
  254. ->opEntryPoint(inst.executionModel, inst.targetId,
  255. std::move(inst.targetName), inst.interfaces)
  256. .x();
  257. }
  258. for (auto &inst : executionModes) {
  259. consumer(inst.take());
  260. }
  261. if (shaderModelVersion != 0) {
  262. llvm::Optional<uint32_t> fileName = llvm::None;
  263. if (!sourceFileName.empty() && sourceFileNameId) {
  264. builder->opString(sourceFileNameId, sourceFileName).x();
  265. fileName = sourceFileNameId;
  266. }
  267. llvm::SmallVector<llvm::StringRef, 2> choppedSrcCode;
  268. llvm::Optional<llvm::StringRef> firstSnippet;
  269. chopString(sourceFileContent, &choppedSrcCode);
  270. if (!choppedSrcCode.empty()) {
  271. firstSnippet = llvm::Optional<llvm::StringRef>(choppedSrcCode.front());
  272. }
  273. builder
  274. ->opSource(spv::SourceLanguage::HLSL, shaderModelVersion, fileName,
  275. firstSnippet)
  276. .x();
  277. for (uint32_t i = 1; i < choppedSrcCode.size(); ++i) {
  278. builder->opSourceContinued(choppedSrcCode[i]).x();
  279. }
  280. }
  281. // BasicBlock debug names should be emitted only for blocks that are
  282. // reachable.
  283. // The debug name for a basic block is stored in the basic block object.
  284. std::vector<BasicBlock *> reachableBasicBlocks;
  285. for (const auto &fn : functions)
  286. fn->getReachableBasicBlocks(&reachableBasicBlocks);
  287. for (BasicBlock *bb : reachableBasicBlocks)
  288. if (!bb->getDebugName().empty())
  289. builder->opName(bb->getLabelId(), bb->getDebugName()).x();
  290. // Emit other debug names
  291. for (auto &inst : debugNames) {
  292. if (inst.memberIndex.hasValue()) {
  293. builder
  294. ->opMemberName(inst.targetId, *inst.memberIndex, std::move(inst.name))
  295. .x();
  296. } else {
  297. builder->opName(inst.targetId, std::move(inst.name)).x();
  298. }
  299. }
  300. if (isVulkan1p1) {
  301. std::string commitHash =
  302. std::string("dxc-commit-hash: ") + clang::getGitCommitHash();
  303. builder->opModuleProcessed(commitHash).x();
  304. std::string commitCount = std::string("dxc-commit-count: ") +
  305. std::to_string(clang::getGitCommitCount());
  306. builder->opModuleProcessed(commitCount).x();
  307. }
  308. for (const auto &idDecorPair : decorations) {
  309. consumer(idDecorPair.second->withTargetId(idDecorPair.first));
  310. }
  311. // Note on interdependence of types and constants:
  312. // There is only one type (OpTypeArray) that requires the result-id of a
  313. // constant. As a result, the constant integer should be defined before the
  314. // array is defined. The integer type should also be defined before the
  315. // constant integer is defined.
  316. for (auto &v : typeConstant) {
  317. consumer(v.take());
  318. }
  319. for (auto &v : variables) {
  320. consumer(v.take());
  321. }
  322. for (uint32_t i = 0; i < functions.size(); ++i) {
  323. functions[i]->take(builder);
  324. }
  325. clear();
  326. }
  327. void SPIRVModule::addType(const Type *type, uint32_t resultId) {
  328. bool inserted = false;
  329. std::tie(std::ignore, inserted) = types.insert(type);
  330. if (inserted) {
  331. typeConstant.push_back(type->withResultId(resultId));
  332. for (const Decoration *d : type->getDecorations()) {
  333. addDecoration(d, resultId);
  334. }
  335. }
  336. }
  337. void SPIRVModule::addConstant(const Constant *constant, uint32_t resultId) {
  338. bool inserted = false;
  339. std::tie(std::ignore, inserted) = constants.insert(constant);
  340. if (inserted) {
  341. typeConstant.push_back(constant->withResultId(resultId));
  342. }
  343. }
  344. } // end namespace spirv
  345. } // end namespace clang