assembly_builder.h 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) 2016 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef TEST_OPT_ASSEMBLY_BUILDER_H_
  15. #define TEST_OPT_ASSEMBLY_BUILDER_H_
  16. #include <algorithm>
  17. #include <cstdint>
  18. #include <sstream>
  19. #include <string>
  20. #include <unordered_set>
  21. #include <vector>
  22. namespace spvtools {
  23. namespace opt {
  24. // A simple SPIR-V assembly code builder for test uses. It builds an SPIR-V
  25. // assembly module from vectors of assembly strings. It allows users to add
  26. // instructions to the main function and the type-constants-globals section
  27. // directly. It relies on OpName instructions and friendly-name disassembling
  28. // to keep the ID names unchanged after assembling.
  29. //
  30. // An assembly module is divided into several sections, matching with the
  31. // SPIR-V Logical Layout:
  32. // Global Preamble:
  33. // OpCapability instructions;
  34. // OpExtension instructions and OpExtInstImport instructions;
  35. // OpMemoryModel instruction;
  36. // OpEntryPoint and OpExecutionMode instruction;
  37. // OpString, OpSourceExtension, OpSource and OpSourceContinued instructions.
  38. // Names:
  39. // OpName instructions.
  40. // Annotations:
  41. // OpDecorate, OpMemberDecorate, OpGroupDecorate, OpGroupMemberDecorate and
  42. // OpDecorationGroup.
  43. // Types, Constants and Global variables:
  44. // Types, constants and global variables declaration instructions.
  45. // Main Function:
  46. // Main function instructions.
  47. // Main Function Postamble:
  48. // The return and function end instructions.
  49. //
  50. // The assembly code is built by concatenating all the strings in the above
  51. // sections.
  52. //
  53. // Users define the contents in section <Type, Constants and Global Variables>
  54. // and <Main Function>. The <Names> section is to hold the names for IDs to
  55. // keep them unchanged before and after assembling. All defined IDs to be added
  56. // to this code builder will be assigned with a global name through OpName
  57. // instruction. The name is extracted from the definition instruction.
  58. // E.g. adding instruction: %var_a = OpConstant %int 2, will also add an
  59. // instruction: OpName %var_a, "var_a".
  60. //
  61. // Note that the name must not be used on more than one defined IDs and
  62. // friendly-name disassembling must be enabled so that OpName instructions will
  63. // be respected.
  64. class AssemblyBuilder {
  65. // The base ID value for spec constants.
  66. static const uint32_t SPEC_ID_BASE = 200;
  67. public:
  68. // Initialize a minimal SPIR-V assembly code as the template. The minimal
  69. // module contains an empty main function and some predefined names for the
  70. // main function.
  71. AssemblyBuilder()
  72. : spec_id_counter_(SPEC_ID_BASE),
  73. global_preamble_({
  74. // clang-format off
  75. "OpCapability Shader",
  76. "OpCapability Float64",
  77. "%1 = OpExtInstImport \"GLSL.std.450\"",
  78. "OpMemoryModel Logical GLSL450",
  79. "OpEntryPoint Vertex %main \"main\"",
  80. // clang-format on
  81. }),
  82. names_(),
  83. annotations_(),
  84. types_consts_globals_(),
  85. main_func_(),
  86. main_func_postamble_({
  87. "OpReturn",
  88. "OpFunctionEnd",
  89. }) {
  90. AppendTypesConstantsGlobals({
  91. "%void = OpTypeVoid",
  92. "%main_func_type = OpTypeFunction %void",
  93. });
  94. AppendInMain({
  95. "%main = OpFunction %void None %main_func_type",
  96. "%main_func_entry_block = OpLabel",
  97. });
  98. }
  99. // Appends OpName instructions to this builder. Instruction strings that do
  100. // not start with 'OpName ' will be skipped. Returns the references of this
  101. // assembly builder.
  102. AssemblyBuilder& AppendNames(const std::vector<std::string>& vec_asm_code) {
  103. for (auto& inst_str : vec_asm_code) {
  104. if (inst_str.find("OpName ") == 0) {
  105. names_.push_back(inst_str);
  106. }
  107. }
  108. return *this;
  109. }
  110. // Appends instructions to the types-constants-globals section and returns
  111. // the reference of this assembly builder. IDs defined in the given code will
  112. // be added to the Names section and then be registered with OpName
  113. // instruction. Corresponding decoration instruction will be added for spec
  114. // constants defined with opcode: 'OpSpecConstant'.
  115. AssemblyBuilder& AppendTypesConstantsGlobals(
  116. const std::vector<std::string>& vec_asm_code) {
  117. AddNamesForResultIDsIn(vec_asm_code);
  118. // Check spec constants defined with OpSpecConstant.
  119. for (auto& inst_str : vec_asm_code) {
  120. if (inst_str.find("= OpSpecConstant ") != std::string::npos ||
  121. inst_str.find("= OpSpecConstantTrue ") != std::string::npos ||
  122. inst_str.find("= OpSpecConstantFalse ") != std::string::npos) {
  123. AddSpecIDFor(GetResultIDName(inst_str));
  124. }
  125. }
  126. types_consts_globals_.insert(types_consts_globals_.end(),
  127. vec_asm_code.begin(), vec_asm_code.end());
  128. return *this;
  129. }
  130. // Appends instructions to the main function block, which is already labelled
  131. // with "main_func_entry_block". Returns the reference of this assembly
  132. // builder. IDs defined in the given code will be added to the Names section
  133. // and then be registered with OpName instruction.
  134. AssemblyBuilder& AppendInMain(const std::vector<std::string>& vec_asm_code) {
  135. AddNamesForResultIDsIn(vec_asm_code);
  136. main_func_.insert(main_func_.end(), vec_asm_code.begin(),
  137. vec_asm_code.end());
  138. return *this;
  139. }
  140. // Appends annotation instructions to the annotation section, and returns the
  141. // reference of this assembly builder.
  142. AssemblyBuilder& AppendAnnotations(
  143. const std::vector<std::string>& vec_annotations) {
  144. annotations_.insert(annotations_.end(), vec_annotations.begin(),
  145. vec_annotations.end());
  146. return *this;
  147. }
  148. // Pre-pends string to the preamble of the module. Useful for EFFCEE checks.
  149. AssemblyBuilder& PrependPreamble(const std::vector<std::string>& preamble) {
  150. preamble_.insert(preamble_.end(), preamble.begin(), preamble.end());
  151. return *this;
  152. }
  153. // Get the SPIR-V assembly code as string.
  154. std::string GetCode() const {
  155. std::ostringstream ss;
  156. for (const auto& line : preamble_) {
  157. ss << line << std::endl;
  158. }
  159. for (const auto& line : global_preamble_) {
  160. ss << line << std::endl;
  161. }
  162. for (const auto& line : names_) {
  163. ss << line << std::endl;
  164. }
  165. for (const auto& line : annotations_) {
  166. ss << line << std::endl;
  167. }
  168. for (const auto& line : types_consts_globals_) {
  169. ss << line << std::endl;
  170. }
  171. for (const auto& line : main_func_) {
  172. ss << line << std::endl;
  173. }
  174. for (const auto& line : main_func_postamble_) {
  175. ss << line << std::endl;
  176. }
  177. return ss.str();
  178. }
  179. private:
  180. // Adds a given name to the Name section with OpName. If the given name has
  181. // been added before, does nothing.
  182. void AddOpNameIfNotExist(const std::string& id_name) {
  183. if (!used_names_.count(id_name)) {
  184. std::stringstream opname_inst;
  185. opname_inst << "OpName "
  186. << "%" << id_name << " \"" << id_name << "\"";
  187. names_.emplace_back(opname_inst.str());
  188. used_names_.insert(id_name);
  189. }
  190. }
  191. // Adds the names in a vector of assembly code strings to the Names section.
  192. // If a '=' sign is found in an instruction, this instruction will be treated
  193. // as an ID defining instruction. The ID name used in the instruction will be
  194. // extracted and added to the Names section.
  195. void AddNamesForResultIDsIn(const std::vector<std::string>& vec_asm_code) {
  196. for (const auto& line : vec_asm_code) {
  197. std::string name = GetResultIDName(line);
  198. if (!name.empty()) {
  199. AddOpNameIfNotExist(name);
  200. }
  201. }
  202. }
  203. // Adds an OpDecorate SpecId instruction for the given ID name.
  204. void AddSpecIDFor(const std::string& id_name) {
  205. std::stringstream decorate_inst;
  206. decorate_inst << "OpDecorate "
  207. << "%" << id_name << " SpecId " << spec_id_counter_;
  208. spec_id_counter_ += 1;
  209. annotations_.emplace_back(decorate_inst.str());
  210. }
  211. // Extracts the ID name from a SPIR-V assembly instruction string. If the
  212. // instruction is an ID-defining instruction (has result ID), returns the
  213. // name of the result ID in string. If the instruction does not have result
  214. // ID, returns an empty string.
  215. std::string GetResultIDName(const std::string inst_str) {
  216. std::string name;
  217. if (inst_str.find('=') != std::string::npos) {
  218. size_t assign_sign = inst_str.find('=');
  219. name = inst_str.substr(0, assign_sign);
  220. name.erase(remove_if(name.begin(), name.end(),
  221. [](char c) { return c == ' ' || c == '%'; }),
  222. name.end());
  223. }
  224. return name;
  225. }
  226. uint32_t spec_id_counter_;
  227. // User-defined preamble.
  228. std::vector<std::string> preamble_;
  229. // The vector that contains common preambles shared across all test SPIR-V
  230. // code.
  231. std::vector<std::string> global_preamble_;
  232. // The vector that contains OpName instructions.
  233. std::vector<std::string> names_;
  234. // The vector that contains annotation instructions.
  235. std::vector<std::string> annotations_;
  236. // The vector that contains the code to declare types, constants and global
  237. // variables (aka. the Types-Constants-Globals section).
  238. std::vector<std::string> types_consts_globals_;
  239. // The vector that contains the code in main function's entry block.
  240. std::vector<std::string> main_func_;
  241. // The vector that contains the postamble of main function body.
  242. std::vector<std::string> main_func_postamble_;
  243. // All of the defined variable names.
  244. std::unordered_set<std::string> used_names_;
  245. };
  246. } // namespace opt
  247. } // namespace spvtools
  248. #endif // TEST_OPT_ASSEMBLY_BUILDER_H_