From 1b1ff07bf57c618ab2d45822d9bde2bfaec33031 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 22 Nov 2015 17:41:39 -0800 Subject: [PATCH] Assembler/disassembler via SPIRV-Tools. --- .gitmodules | 3 + premake5.lua | 1 + src/xenia/gpu/spirv/premake5.lua | 3 + src/xenia/gpu/spirv/spirv_util.h | 10 +++ src/xenia/gpu/spirv/spv_assembler.cc | 77 +++++++++++++++++++++++ src/xenia/gpu/spirv/spv_assembler.h | 69 +++++++++++++++++++++ src/xenia/gpu/spirv/spv_disassembler.cc | 81 +++++++++++++++++++++++++ src/xenia/gpu/spirv/spv_disassembler.h | 66 ++++++++++++++++++++ third_party/spirv-tools | 1 + third_party/spirv-tools.lua | 54 +++++++++++++++++ third_party/spirv/spirv.h | 6 +- 11 files changed, 368 insertions(+), 3 deletions(-) create mode 100644 src/xenia/gpu/spirv/spv_assembler.cc create mode 100644 src/xenia/gpu/spirv/spv_assembler.h create mode 100644 src/xenia/gpu/spirv/spv_disassembler.cc create mode 100644 src/xenia/gpu/spirv/spv_disassembler.h create mode 160000 third_party/spirv-tools create mode 100644 third_party/spirv-tools.lua diff --git a/.gitmodules b/.gitmodules index 8a55370ac..f8f217492 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "third_party/zlib"] path = third_party/zlib url = https://github.com/madler/zlib +[submodule "third_party/spirv-tools"] + path = third_party/spirv-tools + url = https://github.com/xenia-project/SPIRV-Tools.git diff --git a/premake5.lua b/premake5.lua index eafc82d52..16dc121ce 100644 --- a/premake5.lua +++ b/premake5.lua @@ -163,6 +163,7 @@ solution("xenia") include("third_party/glew.lua") include("third_party/imgui.lua") include("third_party/libav.lua") + include("third_party/spirv-tools.lua") include("third_party/xxhash.lua") include("third_party/zlib.lua") include("build_tools/third_party/gflags.lua") diff --git a/src/xenia/gpu/spirv/premake5.lua b/src/xenia/gpu/spirv/premake5.lua index d9b189c3e..a80a4f97f 100644 --- a/src/xenia/gpu/spirv/premake5.lua +++ b/src/xenia/gpu/spirv/premake5.lua @@ -7,6 +7,7 @@ project("xenia-gpu-spirv") kind("StaticLib") language("C++") links({ + "spirv-tools", "xenia-base", "xenia-gpu", }) @@ -14,6 +15,7 @@ project("xenia-gpu-spirv") }) includedirs({ project_root.."/build_tools/third_party/gflags/src", + project_root.."/third_party/spirv-tools/external/include", }) local_platform_files() @@ -24,6 +26,7 @@ project("xenia-gpu-spirv-compiler") language("C++") links({ "gflags", + "spirv-tools", "xenia-base", "xenia-gpu", "xenia-gpu-spirv", diff --git a/src/xenia/gpu/spirv/spirv_util.h b/src/xenia/gpu/spirv/spirv_util.h index f4461600e..ed7977010 100644 --- a/src/xenia/gpu/spirv/spirv_util.h +++ b/src/xenia/gpu/spirv/spirv_util.h @@ -13,6 +13,16 @@ #include "third_party/spirv/GLSL.std.450.h" #include "third_party/spirv/spirv.h" +// Forward declarations from SPIRV-Tools so we don't pollute /so/ much. +struct spv_binary_t; +typedef spv_binary_t* spv_binary; +struct spv_context_t; +typedef spv_context_t* spv_context; +struct spv_diagnostic_t; +typedef spv_diagnostic_t* spv_diagnostic; +struct spv_text_t; +typedef spv_text_t* spv_text; + namespace xe { namespace gpu { namespace spirv { diff --git a/src/xenia/gpu/spirv/spv_assembler.cc b/src/xenia/gpu/spirv/spv_assembler.cc new file mode 100644 index 000000000..413b28af7 --- /dev/null +++ b/src/xenia/gpu/spirv/spv_assembler.cc @@ -0,0 +1,77 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/spirv/spv_assembler.h" + +#include "third_party/spirv-tools/include/libspirv/libspirv.h" +#include "xenia/base/logging.h" + +namespace xe { +namespace gpu { +namespace spirv { + +SpvAssembler::Result::Result(spv_binary binary, spv_diagnostic diagnostic) + : binary_(binary), diagnostic_(diagnostic) {} + +SpvAssembler::Result::~Result() { + if (binary_) { + spvBinaryDestroy(binary_); + } + if (diagnostic_) { + spvDiagnosticDestroy(diagnostic_); + } +} + +bool SpvAssembler::Result::has_error() const { return !!diagnostic_; } + +size_t SpvAssembler::Result::error_source_line() const { + return diagnostic_ ? diagnostic_->position.line : 0; +} + +size_t SpvAssembler::Result::error_source_column() const { + return diagnostic_ ? diagnostic_->position.column : 0; +} + +const char* SpvAssembler::Result::error_string() const { + return diagnostic_ ? diagnostic_->error : ""; +} + +const uint32_t* SpvAssembler::Result::words() const { + return binary_ ? binary_->code : nullptr; +} + +size_t SpvAssembler::Result::word_count() const { + return binary_ ? binary_->wordCount : 0; +} + +SpvAssembler::SpvAssembler() : spv_context_(spvContextCreate()) {} + +SpvAssembler::~SpvAssembler() { spvContextDestroy(spv_context_); } + +std::unique_ptr SpvAssembler::Assemble( + const char* source_text, size_t source_text_length) { + spv_binary binary = nullptr; + spv_diagnostic diagnostic = nullptr; + auto result_code = spvTextToBinary(spv_context_, source_text, + source_text_length, &binary, &diagnostic); + std::unique_ptr result(new Result(binary, diagnostic)); + if (result_code) { + XELOGE("Failed to assemble spv: %d", result_code); + if (result->has_error()) { + return result; + } else { + return nullptr; + } + } + return result; +} + +} // namespace spirv +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/spirv/spv_assembler.h b/src/xenia/gpu/spirv/spv_assembler.h new file mode 100644 index 000000000..5a8b38206 --- /dev/null +++ b/src/xenia/gpu/spirv/spv_assembler.h @@ -0,0 +1,69 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_SPIRV_SPV_ASSEMBLER_H_ +#define XENIA_GPU_SPIRV_SPV_ASSEMBLER_H_ + +#include +#include + +#include "xenia/gpu/spirv/spirv_util.h" + +namespace xe { +namespace gpu { +namespace spirv { + +class SpvAssembler { + public: + class Result { + public: + Result(spv_binary binary, spv_diagnostic diagnostic); + ~Result(); + + // True if the result has an error associated with it. + bool has_error() const; + // Line of the error in the provided source text. + size_t error_source_line() const; + // Column of the error in the provided source text. + size_t error_source_column() const; + // Human-readable description of the error. + const char* error_string() const; + + // Assembled SPIRV binary. + // Returned pointer lifetime is tied to this Result instance. + const uint32_t* words() const; + // Size of the SPIRV binary, in words. + size_t word_count() const; + + private: + spv_binary binary_ = nullptr; + spv_diagnostic diagnostic_ = nullptr; + }; + + SpvAssembler(); + ~SpvAssembler(); + + // Assembles the given source text into a SPIRV binary. + // The return will be nullptr if assembly fails due to a library error. + // The return may have an error set on it if the source text is malformed. + std::unique_ptr Assemble(const char* source_text, + size_t source_text_length); + std::unique_ptr Assemble(const std::string& source_text) { + return Assemble(source_text.c_str(), source_text.size()); + } + + private: + spv_context spv_context_ = nullptr; +}; + +} // namespace spirv +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_SPIRV_SPV_ASSEMBLER_H_ diff --git a/src/xenia/gpu/spirv/spv_disassembler.cc b/src/xenia/gpu/spirv/spv_disassembler.cc new file mode 100644 index 000000000..d518d0943 --- /dev/null +++ b/src/xenia/gpu/spirv/spv_disassembler.cc @@ -0,0 +1,81 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/gpu/spirv/spv_disassembler.h" + +#include "third_party/spirv-tools/include/libspirv/libspirv.h" +#include "xenia/base/logging.h" + +namespace xe { +namespace gpu { +namespace spirv { + +SpvDisassembler::Result::Result(spv_text text, spv_diagnostic diagnostic) + : text_(text), diagnostic_(diagnostic) {} + +SpvDisassembler::Result::~Result() { + if (text_) { + spvTextDestroy(text_); + } + if (diagnostic_) { + spvDiagnosticDestroy(diagnostic_); + } +} + +bool SpvDisassembler::Result::has_error() const { return !!diagnostic_; } + +size_t SpvDisassembler::Result::error_word_index() const { + return diagnostic_ ? diagnostic_->position.index : 0; +} + +const char* SpvDisassembler::Result::error_string() const { + return diagnostic_ ? diagnostic_->error : ""; +} + +const char* SpvDisassembler::Result::text() const { + return text_ ? text_->str : ""; +} + +std::string SpvDisassembler::Result::to_string() const { + return text_ ? std::string(text_->str, text_->length) : ""; +} + +void SpvDisassembler::Result::AppendText(StringBuffer* target_buffer) const { + if (text_) { + target_buffer->AppendBytes(reinterpret_cast(text_->str), + text_->length); + } +} + +SpvDisassembler::SpvDisassembler() : spv_context_(spvContextCreate()) {} + +SpvDisassembler::~SpvDisassembler() { spvContextDestroy(spv_context_); } + +std::unique_ptr SpvDisassembler::Disassemble( + const uint32_t* words, size_t word_count) { + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + auto result_code = + spvBinaryToText(spv_context_, words, word_count, + SPV_BINARY_TO_TEXT_OPTION_INDENT, &text, &diagnostic); + std::unique_ptr result(new Result(text, diagnostic)); + if (result_code) { + XELOGE("Failed to disassemble spv: %d", result_code); + if (result->has_error()) { + return result; + } else { + return nullptr; + } + } + return result; +} + +} // namespace spirv +} // namespace gpu +} // namespace xe diff --git a/src/xenia/gpu/spirv/spv_disassembler.h b/src/xenia/gpu/spirv/spv_disassembler.h new file mode 100644 index 000000000..36929dc1e --- /dev/null +++ b/src/xenia/gpu/spirv/spv_disassembler.h @@ -0,0 +1,66 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_GPU_SPIRV_SPV_DISASSEMBLER_H_ +#define XENIA_GPU_SPIRV_SPV_DISASSEMBLER_H_ + +#include +#include + +#include "xenia/base/string_buffer.h" +#include "xenia/gpu/spirv/spirv_util.h" + +namespace xe { +namespace gpu { +namespace spirv { + +class SpvDisassembler { + public: + class Result { + public: + Result(spv_text text, spv_diagnostic diagnostic); + ~Result(); + + // True if the result has an error associated with it. + bool has_error() const; + // Index of the error in the provided binary word data. + size_t error_word_index() const; + // Human-readable description of the error. + const char* error_string() const; + + // Disassembled source text. + // Returned pointer lifetime is tied to this Result instance. + const char* text() const; + // Converts the disassembled source text to a string. + std::string to_string() const; + // Appends the disassembled source text to the given buffer. + void AppendText(StringBuffer* target_buffer) const; + + private: + spv_text text_ = nullptr; + spv_diagnostic diagnostic_ = nullptr; + }; + + SpvDisassembler(); + ~SpvDisassembler(); + + // Disassembles the given SPIRV binary. + // The return will be nullptr if disassembly fails due to a library error. + // The return may have an error set on it if the SPIRV binary is malformed. + std::unique_ptr Disassemble(const uint32_t* words, size_t word_count); + + private: + spv_context spv_context_ = nullptr; +}; + +} // namespace spirv +} // namespace gpu +} // namespace xe + +#endif // XENIA_GPU_SPIRV_SPV_DISASSEMBLER_H_ diff --git a/third_party/spirv-tools b/third_party/spirv-tools new file mode 160000 index 000000000..df2f5a1b6 --- /dev/null +++ b/third_party/spirv-tools @@ -0,0 +1 @@ +Subproject commit df2f5a1b6eebe50d8e5f3becf22f1e70551e3fd1 diff --git a/third_party/spirv-tools.lua b/third_party/spirv-tools.lua new file mode 100644 index 000000000..90d0e151a --- /dev/null +++ b/third_party/spirv-tools.lua @@ -0,0 +1,54 @@ +group("third_party") +project("spirv-tools") + uuid("621512da-bb50-40f2-85ba-ae615ff13e68") + kind("StaticLib") + language("C++") + links({ + }) + defines({ + "_LIB", + }) + includedirs({ + "spirv-tools/external/include", + "spirv-tools/include", + }) + files({ + "spirv-tools/external/include/headers/GLSL.std.450.h", + "spirv-tools/external/include/headers/OpenCL.std.h", + "spirv-tools/external/include/headers/spirv.h", + "spirv-tools/include/libspirv/libspirv.h", + "spirv-tools/include/util/bitutils.h", + "spirv-tools/include/util/hex_float.h", + "spirv-tools/source/assembly_grammar.cpp", + "spirv-tools/source/assembly_grammar.h", + "spirv-tools/source/binary.cpp", + "spirv-tools/source/binary.h", + "spirv-tools/source/diagnostic.cpp", + "spirv-tools/source/diagnostic.h", + "spirv-tools/source/disassemble.cpp", + "spirv-tools/source/endian.cpp", + "spirv-tools/source/endian.h", + "spirv-tools/source/ext_inst.cpp", + "spirv-tools/source/ext_inst.h", + "spirv-tools/source/instruction.h", + "spirv-tools/source/opcode.cpp", + "spirv-tools/source/opcode.h", + "spirv-tools/source/opcode.inc", + "spirv-tools/source/opencl_std_ext_inst.inc", + "spirv-tools/source/operand.cpp", + "spirv-tools/source/operand.h", + "spirv-tools/source/print.cpp", + "spirv-tools/source/print.h", + "spirv-tools/source/spirv_constant.h", + "spirv-tools/source/spirv_definition.h", + "spirv-tools/source/spirv_operands.h", + "spirv-tools/source/table.cpp", + "spirv-tools/source/table.h", + "spirv-tools/source/text.cpp", + "spirv-tools/source/text.h", + "spirv-tools/source/text_handler.cpp", + "spirv-tools/source/text_handler.h", + "spirv-tools/source/validate.cpp", + "spirv-tools/source/validate.h", + "spirv-tools/source/validate_id.cpp", + }) diff --git a/third_party/spirv/spirv.h b/third_party/spirv/spirv.h index e00c7cd73..136121600 100644 --- a/third_party/spirv/spirv.h +++ b/third_party/spirv/spirv.h @@ -39,8 +39,8 @@ // "Mask" in their name, and a parallel enum that has the shift // amount (1 << x) for each corresponding enumerant. -#ifndef spirv_H -#define spirv_H +#ifndef spirv_H11 +#define spirv_H11 namespace spv { @@ -874,4 +874,4 @@ inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfil } // end namespace spv -#endif // #ifndef spirv_H +#endif // #ifndef spirv_H11