| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // Copyright (c) 2016 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #ifndef TEST_OPT_ASSEMBLY_BUILDER_H_
- #define TEST_OPT_ASSEMBLY_BUILDER_H_
- #include <algorithm>
- #include <cstdint>
- #include <sstream>
- #include <string>
- #include <unordered_set>
- #include <vector>
- namespace spvtools {
- namespace opt {
- // A simple SPIR-V assembly code builder for test uses. It builds an SPIR-V
- // assembly module from vectors of assembly strings. It allows users to add
- // instructions to the main function and the type-constants-globals section
- // directly. It relies on OpName instructions and friendly-name disassembling
- // to keep the ID names unchanged after assembling.
- //
- // An assembly module is divided into several sections, matching with the
- // SPIR-V Logical Layout:
- // Global Preamble:
- // OpCapability instructions;
- // OpExtension instructions and OpExtInstImport instructions;
- // OpMemoryModel instruction;
- // OpEntryPoint and OpExecutionMode instruction;
- // OpString, OpSourceExtension, OpSource and OpSourceContinued instructions.
- // Names:
- // OpName instructions.
- // Annotations:
- // OpDecorate, OpMemberDecorate, OpGroupDecorate, OpGroupMemberDecorate and
- // OpDecorationGroup.
- // Types, Constants and Global variables:
- // Types, constants and global variables declaration instructions.
- // Main Function:
- // Main function instructions.
- // Main Function Postamble:
- // The return and function end instructions.
- //
- // The assembly code is built by concatenating all the strings in the above
- // sections.
- //
- // Users define the contents in section <Type, Constants and Global Variables>
- // and <Main Function>. The <Names> section is to hold the names for IDs to
- // keep them unchanged before and after assembling. All defined IDs to be added
- // to this code builder will be assigned with a global name through OpName
- // instruction. The name is extracted from the definition instruction.
- // E.g. adding instruction: %var_a = OpConstant %int 2, will also add an
- // instruction: OpName %var_a, "var_a".
- //
- // Note that the name must not be used on more than one defined IDs and
- // friendly-name disassembling must be enabled so that OpName instructions will
- // be respected.
- class AssemblyBuilder {
- // The base ID value for spec constants.
- static const uint32_t SPEC_ID_BASE = 200;
- public:
- // Initialize a minimal SPIR-V assembly code as the template. The minimal
- // module contains an empty main function and some predefined names for the
- // main function.
- AssemblyBuilder()
- : spec_id_counter_(SPEC_ID_BASE),
- global_preamble_({
- // clang-format off
- "OpCapability Shader",
- "OpCapability Float64",
- "%1 = OpExtInstImport \"GLSL.std.450\"",
- "OpMemoryModel Logical GLSL450",
- "OpEntryPoint Vertex %main \"main\"",
- // clang-format on
- }),
- names_(),
- annotations_(),
- types_consts_globals_(),
- main_func_(),
- main_func_postamble_({
- "OpReturn",
- "OpFunctionEnd",
- }) {
- AppendTypesConstantsGlobals({
- "%void = OpTypeVoid",
- "%main_func_type = OpTypeFunction %void",
- });
- AppendInMain({
- "%main = OpFunction %void None %main_func_type",
- "%main_func_entry_block = OpLabel",
- });
- }
- // Appends OpName instructions to this builder. Instruction strings that do
- // not start with 'OpName ' will be skipped. Returns the references of this
- // assembly builder.
- AssemblyBuilder& AppendNames(const std::vector<std::string>& vec_asm_code) {
- for (auto& inst_str : vec_asm_code) {
- if (inst_str.find("OpName ") == 0) {
- names_.push_back(inst_str);
- }
- }
- return *this;
- }
- // Appends instructions to the types-constants-globals section and returns
- // the reference of this assembly builder. IDs defined in the given code will
- // be added to the Names section and then be registered with OpName
- // instruction. Corresponding decoration instruction will be added for spec
- // constants defined with opcode: 'OpSpecConstant'.
- AssemblyBuilder& AppendTypesConstantsGlobals(
- const std::vector<std::string>& vec_asm_code) {
- AddNamesForResultIDsIn(vec_asm_code);
- // Check spec constants defined with OpSpecConstant.
- for (auto& inst_str : vec_asm_code) {
- if (inst_str.find("= OpSpecConstant ") != std::string::npos ||
- inst_str.find("= OpSpecConstantTrue ") != std::string::npos ||
- inst_str.find("= OpSpecConstantFalse ") != std::string::npos) {
- AddSpecIDFor(GetResultIDName(inst_str));
- }
- }
- types_consts_globals_.insert(types_consts_globals_.end(),
- vec_asm_code.begin(), vec_asm_code.end());
- return *this;
- }
- // Appends instructions to the main function block, which is already labelled
- // with "main_func_entry_block". Returns the reference of this assembly
- // builder. IDs defined in the given code will be added to the Names section
- // and then be registered with OpName instruction.
- AssemblyBuilder& AppendInMain(const std::vector<std::string>& vec_asm_code) {
- AddNamesForResultIDsIn(vec_asm_code);
- main_func_.insert(main_func_.end(), vec_asm_code.begin(),
- vec_asm_code.end());
- return *this;
- }
- // Appends annotation instructions to the annotation section, and returns the
- // reference of this assembly builder.
- AssemblyBuilder& AppendAnnotations(
- const std::vector<std::string>& vec_annotations) {
- annotations_.insert(annotations_.end(), vec_annotations.begin(),
- vec_annotations.end());
- return *this;
- }
- // Pre-pends string to the preamble of the module. Useful for EFFCEE checks.
- AssemblyBuilder& PrependPreamble(const std::vector<std::string>& preamble) {
- preamble_.insert(preamble_.end(), preamble.begin(), preamble.end());
- return *this;
- }
- // Get the SPIR-V assembly code as string.
- std::string GetCode() const {
- std::ostringstream ss;
- for (const auto& line : preamble_) {
- ss << line << std::endl;
- }
- for (const auto& line : global_preamble_) {
- ss << line << std::endl;
- }
- for (const auto& line : names_) {
- ss << line << std::endl;
- }
- for (const auto& line : annotations_) {
- ss << line << std::endl;
- }
- for (const auto& line : types_consts_globals_) {
- ss << line << std::endl;
- }
- for (const auto& line : main_func_) {
- ss << line << std::endl;
- }
- for (const auto& line : main_func_postamble_) {
- ss << line << std::endl;
- }
- return ss.str();
- }
- private:
- // Adds a given name to the Name section with OpName. If the given name has
- // been added before, does nothing.
- void AddOpNameIfNotExist(const std::string& id_name) {
- if (!used_names_.count(id_name)) {
- std::stringstream opname_inst;
- opname_inst << "OpName "
- << "%" << id_name << " \"" << id_name << "\"";
- names_.emplace_back(opname_inst.str());
- used_names_.insert(id_name);
- }
- }
- // Adds the names in a vector of assembly code strings to the Names section.
- // If a '=' sign is found in an instruction, this instruction will be treated
- // as an ID defining instruction. The ID name used in the instruction will be
- // extracted and added to the Names section.
- void AddNamesForResultIDsIn(const std::vector<std::string>& vec_asm_code) {
- for (const auto& line : vec_asm_code) {
- std::string name = GetResultIDName(line);
- if (!name.empty()) {
- AddOpNameIfNotExist(name);
- }
- }
- }
- // Adds an OpDecorate SpecId instruction for the given ID name.
- void AddSpecIDFor(const std::string& id_name) {
- std::stringstream decorate_inst;
- decorate_inst << "OpDecorate "
- << "%" << id_name << " SpecId " << spec_id_counter_;
- spec_id_counter_ += 1;
- annotations_.emplace_back(decorate_inst.str());
- }
- // Extracts the ID name from a SPIR-V assembly instruction string. If the
- // instruction is an ID-defining instruction (has result ID), returns the
- // name of the result ID in string. If the instruction does not have result
- // ID, returns an empty string.
- std::string GetResultIDName(const std::string inst_str) {
- std::string name;
- if (inst_str.find('=') != std::string::npos) {
- size_t assign_sign = inst_str.find('=');
- name = inst_str.substr(0, assign_sign);
- name.erase(remove_if(name.begin(), name.end(),
- [](char c) { return c == ' ' || c == '%'; }),
- name.end());
- }
- return name;
- }
- uint32_t spec_id_counter_;
- // User-defined preamble.
- std::vector<std::string> preamble_;
- // The vector that contains common preambles shared across all test SPIR-V
- // code.
- std::vector<std::string> global_preamble_;
- // The vector that contains OpName instructions.
- std::vector<std::string> names_;
- // The vector that contains annotation instructions.
- std::vector<std::string> annotations_;
- // The vector that contains the code to declare types, constants and global
- // variables (aka. the Types-Constants-Globals section).
- std::vector<std::string> types_consts_globals_;
- // The vector that contains the code in main function's entry block.
- std::vector<std::string> main_func_;
- // The vector that contains the postamble of main function body.
- std::vector<std::string> main_func_postamble_;
- // All of the defined variable names.
- std::unordered_set<std::string> used_names_;
- };
- } // namespace opt
- } // namespace spvtools
- #endif // TEST_OPT_ASSEMBLY_BUILDER_H_
|