mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-27 05:40:06 +00:00
38c9471a0e
assembly_builder was missing an include for cstdint.
252 lines
9.9 KiB
C++
Executable File
252 lines
9.9 KiB
C++
Executable File
// Copyright (c) 2016 Google Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and/or associated documentation files (the
|
|
// "Materials"), to deal in the Materials without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Materials, and to
|
|
// permit persons to whom the Materials are furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Materials.
|
|
//
|
|
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
|
|
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
|
|
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
|
|
// https://www.khronos.org/registry/
|
|
//
|
|
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
|
|
|
#ifndef LIBSPIRV_TEST_OPT_ASSEMBLY_BUILDER
|
|
#define LIBSPIRV_TEST_OPT_ASSEMBLY_BUILDER
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace spvtools {
|
|
|
|
// 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:
|
|
// Initalize 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 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;
|
|
}
|
|
|
|
// Get the SPIR-V assembly code as string.
|
|
std::string GetCode() const {
|
|
std::ostringstream ss;
|
|
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_;
|
|
// 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 spvtools
|
|
|
|
#endif // LIBSPIRV_TEST_OPT_ASSEMBLY_BUILDER
|