From 8590f9cc810f5f4f29f43fc8755d756b8f7d4667 Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Sun, 22 May 2016 14:17:39 -0400 Subject: [PATCH] Add IrBuilder for constructing SPIR-V in-memory representation. --- source/opt/CMakeLists.txt | 2 + source/opt/ir_loader.cpp | 123 ++++++++++++++++++++++ source/opt/ir_loader.h | 83 +++++++++++++++ source/opt/module.cpp | 16 ++- source/opt/module.h | 26 ++--- test/CMakeLists.txt | 2 + test/opt/CMakeLists.txt | 30 ++++++ test/opt/opt_test_common.cpp | 116 +++++++++++++++++++++ test/opt/opt_test_common.h | 54 ++++++++++ test/opt/test_ir_loader.cpp | 193 +++++++++++++++++++++++++++++++++++ 10 files changed, 621 insertions(+), 24 deletions(-) create mode 100644 source/opt/ir_loader.cpp create mode 100644 source/opt/ir_loader.h create mode 100644 test/opt/CMakeLists.txt create mode 100644 test/opt/opt_test_common.cpp create mode 100644 test/opt/opt_test_common.h create mode 100644 test/opt/test_ir_loader.cpp diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 8e46b7960..80e7c64c3 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -2,11 +2,13 @@ add_library(SPIRV-Tools-opt basic_block.h function.h instruction.h + ir_loader.h module.h reflect.h function.cpp instruction.cpp + ir_loader.cpp module.cpp ) diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp new file mode 100644 index 000000000..8da7cb332 --- /dev/null +++ b/source/opt/ir_loader.cpp @@ -0,0 +1,123 @@ +// 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. + +#include "ir_loader.h" + +#include + +#include "reflect.h" + +namespace spvtools { +namespace opt { +namespace ir { + +void IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { + const auto opcode = static_cast(inst->opcode); + if (IsDebugLineInst(opcode)) { + dbg_line_info_.push_back(Instruction(*inst)); + return; + } + + Instruction spv_inst(*inst, std::move(dbg_line_info_)); + dbg_line_info_.clear(); + // Handle function and basic block boundaries first, then normal + // instructions. + if (opcode == SpvOpFunction) { + assert(function_ == nullptr); + assert(block_ == nullptr); + function_.reset(new Function(std::move(spv_inst))); + } else if (opcode == SpvOpFunctionEnd) { + assert(function_ != nullptr); + assert(block_ == nullptr); + module_->AddFunction(std::move(*function_.release())); + function_ = nullptr; + } else if (opcode == SpvOpLabel) { + assert(function_ != nullptr); + assert(block_ == nullptr); + block_.reset(new BasicBlock(std::move(spv_inst))); + } else if (IsTerminatorInst(opcode)) { + assert(function_ != nullptr); + assert(block_ != nullptr); + block_->AddInstruction(std::move(spv_inst)); + function_->AddBasicBlock(std::move(*block_.release())); + block_ = nullptr; + } else { + if (function_ == nullptr) { // Outside function definition + assert(block_ == nullptr); + if (opcode == SpvOpCapability) { + module_->AddCapability(std::move(spv_inst)); + } else if (opcode == SpvOpExtension) { + module_->AddExtension(std::move(spv_inst)); + } else if (opcode == SpvOpExtInstImport) { + module_->AddExtInstImport(std::move(spv_inst)); + } else if (opcode == SpvOpMemoryModel) { + module_->SetMemoryModel(std::move(spv_inst)); + } else if (opcode == SpvOpEntryPoint) { + module_->AddEntryPoint(std::move(spv_inst)); + } else if (opcode == SpvOpExecutionMode) { + module_->AddExecutionMode(std::move(spv_inst)); + } else if (IsDebugInst(opcode)) { + module_->AddDebugInst(std::move(spv_inst)); + } else if (IsAnnotationInst(opcode)) { + module_->AddAnnotationInst(std::move(spv_inst)); + } else if (IsTypeInst(opcode)) { + module_->AddType(std::move(spv_inst)); + } else if (IsConstantInst(opcode)) { + module_->AddConstant(std::move(spv_inst)); + } else if (opcode == SpvOpVariable) { + module_->AddGlobalVariable(std::move(spv_inst)); + } else { + assert(0 && "unhandled inst type outside function defintion"); + } + } else { + if (block_ == nullptr) { // Inside function but outside blocks + assert(opcode == SpvOpFunctionParameter); + function_->AddParameter(std::move(spv_inst)); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + } + } +} + +// Resolves internal references among the module, functions, basic blocks, etc. +// This function should be called after adding all instructions. +// +// This concluding call is needed because the whole in memory representation is +// designed around rvalues and move semantics, which subject to pointer +// invalidation during module construction internally. +void IrLoader::EndModule() { + for (auto& function : module_->functions()) { + for (auto& bb : function.basic_blocks()) { + bb.SetParent(&function); + } + function.SetParent(module_); + } +} + +} // namespace ir +} // namespace opt +} // namespace spvtools diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h new file mode 100644 index 000000000..7eedd6a95 --- /dev/null +++ b/source/opt/ir_loader.h @@ -0,0 +1,83 @@ +// 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_OPT_IR_LOADER_H_ +#define LIBSPIRV_OPT_IR_LOADER_H_ + +#include + +#include "basic_block.h" +#include "instruction.h" +#include "module.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { +namespace ir { + +// Loader class for constructing SPIR-V in-memory IR representation. Methods in +// this class are designed to work with the interface for spvBinaryParse() in +// libspirv.h so that we can leverage the syntax checks implemented behind it. +// +// The user is expected to call SetModuleHeader() to fill in the module's +// header, and then AddInstruction() for each decoded instruction, and finally +// EndModule() to finalize the module. The instructions processed in sequence +// by AddInstruction() should comprise a valid SPIR-V module. +class IrLoader { + public: + // Instantiates a builder to construct the given |module| gradually. + IrLoader(Module* module) : module_(module) {} + + // Sets the fields in the module's header to the given parameters. + void SetModuleHeader(uint32_t magic, uint32_t version, uint32_t generator, + uint32_t bound, uint32_t reserved) { + module_->SetHeader({magic, version, generator, bound, reserved}); + } + // Adds an instruction to the module. This method will properly capture and + // store the data provided in |inst| so that |inst| is no longer needed after + // returning. + void AddInstruction(const spv_parsed_instruction_t* inst); + // Finalizes the module construction. This must be called after the module + // header has been set and all instructions have been added. + // Resolves internal bookkeeping. + void EndModule(); + + private: + // The module to be built. + Module* module_; + // The current Function under construction. + std::unique_ptr function_; + // The current BasicBlock under construction. + std::unique_ptr block_; + // Line related debug instructions accumulated thus far. + std::vector dbg_line_info_; +}; + +} // namespace ir +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_IR_LOADER_H_ diff --git a/source/opt/module.cpp b/source/opt/module.cpp index e134fb102..454e066c3 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -33,9 +33,9 @@ namespace ir { std::vector Module::types() { std::vector insts; - for (uint32_t i = 0; i < types_and_constants_.size(); ++i) { - if (IsTypeInst(types_and_constants_[i].opcode())) - insts.push_back(&types_and_constants_[i]); + for (uint32_t i = 0; i < types_values_.size(); ++i) { + if (IsTypeInst(types_values_[i].opcode())) + insts.push_back(&types_values_[i]); } return insts; }; @@ -43,14 +43,13 @@ std::vector Module::types() { void Module::ForEachInst(const std::function& f) { for (auto& i : capabilities_) f(&i); for (auto& i : extensions_) f(&i); - for (auto& i : ext_inst_sets_) f(&i); + for (auto& i : ext_inst_imports_) f(&i); f(&memory_model_); for (auto& i : entry_points_) f(&i); for (auto& i : execution_modes_) f(&i); for (auto& i : debugs_) f(&i); for (auto& i : annotations_) f(&i); - for (auto& i : types_and_constants_) f(&i); - for (auto& i : variables_) f(&i); + for (auto& i : types_values_) f(&i); for (auto& i : functions_) i.ForEachInst(f); } @@ -65,14 +64,13 @@ void Module::ToBinary(std::vector* binary, bool skip_nop) const { // TODO(antiagainst): wow, looks like a duplication of the above. for (const auto& c : capabilities_) c.ToBinary(binary, skip_nop); for (const auto& e : extensions_) e.ToBinary(binary, skip_nop); - for (const auto& e : ext_inst_sets_) e.ToBinary(binary, skip_nop); + for (const auto& e : ext_inst_imports_) e.ToBinary(binary, skip_nop); memory_model_.ToBinary(binary, skip_nop); for (const auto& e : entry_points_) e.ToBinary(binary, skip_nop); for (const auto& e : execution_modes_) e.ToBinary(binary, skip_nop); for (const auto& d : debugs_) d.ToBinary(binary, skip_nop); for (const auto& a : annotations_) a.ToBinary(binary, skip_nop); - for (const auto& t : types_and_constants_) t.ToBinary(binary, skip_nop); - for (const auto& v : variables_) v.ToBinary(binary, skip_nop); + for (const auto& t : types_values_) t.ToBinary(binary, skip_nop); for (const auto& f : functions_) f.ToBinary(binary, skip_nop); } diff --git a/source/opt/module.h b/source/opt/module.h index d6f88158f..ae2e4c08d 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -28,8 +28,8 @@ #define LIBSPIRV_OPT_MODULE_H_ #include -#include #include +#include #include "function.h" #include "instruction.h" @@ -61,8 +61,8 @@ class Module { // Appends an extension instruction to this module. void AddExtension(Instruction&& e) { extensions_.push_back(std::move(e)); } // Appends an extended instruction set instruction to this module. - void AddExtInstSet(Instruction&& e) { - ext_inst_sets_.push_back(std::move(e)); + void AddExtInstImport(Instruction&& e) { + ext_inst_imports_.push_back(std::move(e)); } // Appends a memory model instruction to this module. void SetMemoryModel(Instruction&& m) { memory_model_ = std::move(m); } @@ -79,15 +79,13 @@ class Module { annotations_.push_back(std::move(a)); } // Appends a type-declaration instruction to this module. - void AddType(Instruction&& t) { - types_and_constants_.push_back(std::move(t)); - } + void AddType(Instruction&& t) { types_values_.push_back(std::move(t)); } // Appends a constant-creation instruction to this module. - void AddConstant(Instruction&& c) { - types_and_constants_.push_back(std::move(c)); + void AddConstant(Instruction&& c) { types_values_.push_back(std::move(c)); } + // Appends a global variable-declaration instruction to this module. + void AddGlobalVariable(Instruction&& v) { + types_values_.push_back(std::move(v)); } - // Appends a variable-declaration instruction to this module. - void AddVariable(Instruction&& v) { variables_.push_back(std::move(v)); } // Appends a function to this module. void AddFunction(Function&& f) { functions_.push_back(std::move(f)); } @@ -115,16 +113,14 @@ class Module { // Section 2.4 of the SPIR-V specification. std::vector capabilities_; std::vector extensions_; - std::vector ext_inst_sets_; + std::vector ext_inst_imports_; Instruction memory_model_; // A module only has one memory model instruction. std::vector entry_points_; std::vector execution_modes_; std::vector debugs_; std::vector annotations_; - // Types and constants may depends on each other; thus they are grouped - // together. - std::vector types_and_constants_; - std::vector variables_; + // Type declarations, constants, and global variable declarations. + std::vector types_values_; std::vector functions_; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 42464fb97..197c48aca 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,6 +49,7 @@ function(add_spvtools_unittest) endif() target_include_directories(${ARG_TARGET} PRIVATE ${spirv-tools_SOURCE_DIR} + ${spirv-tools_SOURCE_DIR}/include ${SPIRV_HEADER_INCLUDE_DIR} ${gtest_SOURCE_DIR}/include ${gmock_SOURCE_DIR}/include @@ -131,6 +132,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) TARGET UnitSPIRV SRCS ${TEST_SOURCES} LIBS ${SPIRV_TOOLS}) + add_subdirectory(opt) else() message(STATUS "Did not find googletest, tests will not be built." "To enable tests place googletest in '/external/googletest'.") diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt new file mode 100644 index 000000000..881f3c82f --- /dev/null +++ b/test/opt/CMakeLists.txt @@ -0,0 +1,30 @@ +# 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. + +add_spvtools_unittest(TARGET ir_loader + SRCS test_ir_loader.cpp opt_test_common.cpp + LIBS SPIRV-Tools-opt ${SPIRV_TOOLS} +) diff --git a/test/opt/opt_test_common.cpp b/test/opt/opt_test_common.cpp new file mode 100644 index 000000000..2a5d94bda --- /dev/null +++ b/test/opt/opt_test_common.cpp @@ -0,0 +1,116 @@ +// 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. + +#include "opt_test_common.h" + +#include + +#include "source/opt/ir_loader.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { + +namespace { + +// Sets the module header. Meets the interface requirement of spvBinaryParse(). +spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t reserved) { + reinterpret_cast(builder)->SetModuleHeader( + magic, version, generator, id_bound, reserved); + return SPV_SUCCESS; +}; + +// Processes a parsed instruction. Meets the interface requirement of +// spvBinaryParse(). +spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) { + reinterpret_cast(builder)->AddInstruction(inst); + return SPV_SUCCESS; +}; + +} // annoymous namespace + +// Assembles the given assembly |text| and returns the binary. +std::vector Assemble(const std::string& text) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + spv_binary binary = nullptr; + spv_diagnostic diagnostic = nullptr; + + spv_result_t status = + spvTextToBinary(context, text.data(), text.size(), &binary, &diagnostic); + EXPECT_EQ(SPV_SUCCESS, status) << "assemble text to binary failed"; + std::vector result(binary->code, binary->code + binary->wordCount); + + spvDiagnosticDestroy(diagnostic); + spvBinaryDestroy(binary); + spvContextDestroy(context); + + return result; +} + +// Disassembles the given SPIR-V |binary| and returns the assembly. +std::string Disassemble(const std::vector& binary) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + + spv_result_t status = + spvBinaryToText(context, binary.data(), binary.size(), + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, &text, &diagnostic); + EXPECT_EQ(SPV_SUCCESS, status) << "disassemble binary to text failed"; + std::string result(text->str, text->str + text->length); + + spvDiagnosticDestroy(diagnostic); + spvTextDestroy(text); + spvContextDestroy(context); + + return result; +} + +// Builds and returns a Module for the given SPIR-V |binary|. +std::unique_ptr BuildModule(const std::vector& binary) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + spv_diagnostic diagnostic = nullptr; + + std::unique_ptr module(new ir::Module); + ir::IrLoader builder(module.get()); + + spv_result_t status = + spvBinaryParse(context, &builder, binary.data(), binary.size(), + SetSpvHeader, SetSpvInst, &diagnostic); + EXPECT_EQ(SPV_SUCCESS, status) << "build ir::Module from binary failed"; + + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); + + builder.EndModule(); + + return module; +} + +} // namespace opt +} // namespace spvtools diff --git a/test/opt/opt_test_common.h b/test/opt/opt_test_common.h new file mode 100644 index 000000000..7337943b5 --- /dev/null +++ b/test/opt/opt_test_common.h @@ -0,0 +1,54 @@ +// 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_OPT_TEST_COMMON_H_ +#define LIBSPIRV_TEST_OPT_OPT_TEST_COMMON_H_ + +#include +#include +#include + +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// TODO(antiagainst): expand and export these functions as the C++ interface in +// libspirv.hpp. + +// Assembles the given assembly |text| and returns the binary. +std::vector Assemble(const std::string& text); + +// Disassembles the given SPIR-V |binary| and returns the assembly. +std::string Disassemble(const std::vector& binary); + +// Builds and returns a Module for the given SPIR-V |binary|. +std::unique_ptr BuildModule(const std::vector& binary); + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_TEST_OPT_OPT_TEST_COMMON_H_ diff --git a/test/opt/test_ir_loader.cpp b/test/opt/test_ir_loader.cpp new file mode 100644 index 000000000..c2bbe167a --- /dev/null +++ b/test/opt/test_ir_loader.cpp @@ -0,0 +1,193 @@ +// 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. + +#include + +#include "opt_test_common.h" + +namespace { + +using namespace spvtools::opt; + +TEST(IrBuilder, RoundTrip) { + // #version 310 es + // int add(int a, int b) { return a + b; } + // void main() { add(1, 2); } + const std::string text = + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %2 \"main\"\n" + "OpSource ESSL 310\n" + "OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n" + "OpSourceExtension \"GL_GOOGLE_include_directive\"\n" + "OpName %2 \"main\"\n" + "OpName %3 \"add(i1;i1;\"\n" + "OpName %4 \"a\"\n" + "OpName %5 \"b\"\n" + "OpName %6 \"param\"\n" + "OpName %7 \"param\"\n" + "%8 = OpTypeVoid\n" + "%9 = OpTypeFunction %8\n" + "%10 = OpTypeInt 32 1\n" + "%11 = OpTypePointer Function %10\n" + "%12 = OpTypeFunction %10 %11 %11\n" + "%13 = OpConstant %10 1\n" + "%14 = OpConstant %10 2\n" + "%2 = OpFunction %8 None %9\n" + "%15 = OpLabel\n" + "%6 = OpVariable %11 Function\n" + "%7 = OpVariable %11 Function\n" + "OpStore %6 %13\n" + "OpStore %7 %14\n" + "%16 = OpFunctionCall %10 %3 %6 %7\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%3 = OpFunction %10 None %12\n" + "%4 = OpFunctionParameter %11\n" + "%5 = OpFunctionParameter %11\n" + "%17 = OpLabel\n" + "%18 = OpLoad %10 %4\n" + "%19 = OpLoad %10 %5\n" + "%20 = OpIAdd %10 %18 %19\n" + "OpReturnValue %20\n" + "OpFunctionEnd\n"; + + std::unique_ptr module = BuildModule(Assemble(text)); + std::vector binary; + module->ToBinary(&binary, /* skip_nop = */ false); + + EXPECT_EQ(text, Disassemble(binary)); +} + +TEST(IrBuilder, KeepLineDebugInfo) { + // #version 310 es + // void main() {} + const std::string text = + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %2 \"main\"\n" + "%3 = OpString \"minimal.vert\"\n" + "OpSource ESSL 310\n" + "OpName %2 \"main\"\n" + "OpLine %3 10 10\n" + "%4 = OpTypeVoid\n" + "OpLine %3 100 100\n" + "%5 = OpTypeFunction %4\n" + "%2 = OpFunction %4 None %5\n" + "OpLine %3 1 1\n" + "OpNoLine\n" + "OpLine %3 2 2\n" + "OpLine %3 3 3\n" + "%6 = OpLabel\n" + "OpLine %3 4 4\n" + "OpNoLine\n" + "OpReturn\n" + "OpFunctionEnd\n"; + + std::unique_ptr module = BuildModule(Assemble(text)); + std::vector binary; + module->ToBinary(&binary, /* skip_nop = */ false); + + EXPECT_EQ(text, Disassemble(binary)); +} + +TEST(IrBuilder, LocalGlobalVariables) { + // #version 310 es + // + // float gv1 = 10.; + // float gv2 = 100.; + // + // float f() { + // float lv1 = gv1 + gv2; + // float lv2 = gv1 * gv2; + // return lv1 / lv2; + // } + // + // void main() { + // float lv1 = gv1 - gv2; + // } + const std::string text = + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %2 \"main\"\n" + "OpSource ESSL 310\n" + "OpName %2 \"main\"\n" + "OpName %3 \"f(\"\n" + "OpName %4 \"gv1\"\n" + "OpName %5 \"gv2\"\n" + "OpName %6 \"lv1\"\n" + "OpName %7 \"lv2\"\n" + "OpName %8 \"lv1\"\n" + "%9 = OpTypeVoid\n" + "%10 = OpTypeFunction %9\n" + "%11 = OpTypeFloat 32\n" + "%12 = OpTypeFunction %11\n" + "%13 = OpTypePointer Private %11\n" + "%4 = OpVariable %13 Private\n" + "%14 = OpConstant %11 10\n" + "%5 = OpVariable %13 Private\n" + "%15 = OpConstant %11 100\n" + "%16 = OpTypePointer Function %11\n" + "%2 = OpFunction %9 None %10\n" + "%17 = OpLabel\n" + "%8 = OpVariable %16 Function\n" + "OpStore %4 %14\n" + "OpStore %5 %15\n" + "%18 = OpLoad %11 %4\n" + "%19 = OpLoad %11 %5\n" + "%20 = OpFSub %11 %18 %19\n" + "OpStore %8 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%3 = OpFunction %11 None %12\n" + "%21 = OpLabel\n" + "%6 = OpVariable %16 Function\n" + "%7 = OpVariable %16 Function\n" + "%22 = OpLoad %11 %4\n" + "%23 = OpLoad %11 %5\n" + "%24 = OpFAdd %11 %22 %23\n" + "OpStore %6 %24\n" + "%25 = OpLoad %11 %4\n" + "%26 = OpLoad %11 %5\n" + "%27 = OpFMul %11 %25 %26\n" + "OpStore %7 %27\n" + "%28 = OpLoad %11 %6\n" + "%29 = OpLoad %11 %7\n" + "%30 = OpFDiv %11 %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"; + + std::unique_ptr module = BuildModule(Assemble(text)); + std::vector binary; + module->ToBinary(&binary, /* skip_nop = */ false); + + EXPECT_EQ(text, Disassemble(binary)); +} + +} // anonymous namespace