spv: Build internal representation of the SPIR-V
For convenience, use the SPIRV-Tools' optimizer representation.
Bug: tint:3
Change-Id: I1b046209584e1e907045d496b0f8d7b36fca79bd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16660
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 93001a6..6713085 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -306,14 +306,32 @@
writer/wgsl/generator_impl_test.cc
)
+function(tint_spvtools_compile_options TARGET)
+ # We'll use the optimizer for its nice SPIR-V in-memory representation
+ target_link_libraries(libtint SPIRV-Tools-opt SPIRV-Tools)
+ # We'll be cheating: using internal interfaces to the SPIRV-Tools
+ # optimizer.
+ target_include_directories(libtint PRIVATE ${spirv-tools_SOURCE_DIR} ${spirv-tools_BINARY_DIR})
+ if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+ # The SPIRV-Tools code is conditioned against C++ and an older version of Clang.
+ # Suppress warnings triggered in our current compilation environment.
+ # TODO(dneto): Fix the issues upstream.
+ target_compile_options(${TARGET} PRIVATE
+ -Wno-newline-eof
+ -Wno-sign-conversion
+ -Wno-old-style-cast
+ -Wno-weak-vtables
+ )
+ endif()
+endfunction()
+
+
## Tint library
add_library(libtint ${TINT_LIB_SRCS})
tint_default_compile_options(libtint)
set_target_properties(libtint PROPERTIES OUTPUT_NAME "tint")
-
if(${TINT_BUILD_SPV_PARSER})
- # We'll use the optimizer for its nice SPIR-V in-memory representation
- target_link_libraries(libtint SPIRV-Tools-opt SPIRV-Tools)
+ tint_spvtools_compile_options(libtint)
endif()
if(${TINT_BUILD_SPV_PARSER})
@@ -325,6 +343,9 @@
endif()
add_executable(tint_unittests ${TINT_TEST_SRCS})
+if (${TINT_BUILD_SPV_PARSER})
+ tint_spvtools_compile_options(tint_unittests)
+endif()
if (NOT MSVC)
target_compile_options(tint_unittests PRIVATE
-Wno-global-constructors
@@ -335,6 +356,10 @@
## Test executable
target_include_directories(
tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
+if(${TINT_BUILD_SPV_PARSER})
+ target_include_directories(tint_unittests
+ PRIVATE ${spirv-tools_SOURCE_DIR} ${spirv-tools_BINARY_DIR})
+endif()
target_link_libraries(tint_unittests libtint gmock_main)
tint_default_compile_options(tint_unittests)
diff --git a/src/reader/spv/parser_impl.cc b/src/reader/spv/parser_impl.cc
index 560f3d1..c31d2c6 100644
--- a/src/reader/spv/parser_impl.cc
+++ b/src/reader/spv/parser_impl.cc
@@ -14,6 +14,7 @@
#include <cstring>
+#include "source/opt/build_module.h"
#include "spirv-tools/libspirv.hpp"
#include "src/reader/spv/parser_impl.h"
@@ -21,8 +22,37 @@
namespace reader {
namespace spv {
+namespace {
+
+const spv_target_env kTargetEnv = SPV_ENV_WEBGPU_0;
+
+} // namespace
+
ParserImpl::ParserImpl(const std::vector<uint32_t>& spv_binary)
- : Reader(), spv_binary_(spv_binary), fail_stream_(&success_, &errors_) {}
+ : Reader(),
+ spv_binary_(spv_binary),
+ fail_stream_(&success_, &errors_),
+ tools_context_(kTargetEnv),
+ tools_(kTargetEnv) {
+ // Create a message consumer to propagate error messages from SPIRV-Tools
+ // out as our own failures.
+ message_consumer_ = [this](spv_message_level_t level, const char* /*source*/,
+ const spv_position_t& position,
+ const char* message) {
+ switch (level) {
+ // Ignore info and warning message.
+ case SPV_MSG_WARNING:
+ case SPV_MSG_INFO:
+ break;
+ // Otherwise, propagate the error.
+ default:
+ // For binary validation errors, we only have the instruction
+ // number. It's not text, so there is no column number.
+ this->Fail() << "line:" << position.index << ": " << message;
+ this->Fail() << "error: line " << position.index << ": " << message;
+ }
+ };
+}
ParserImpl::~ParserImpl() = default;
@@ -32,30 +62,20 @@
}
// Set up use of SPIRV-Tools utilities.
- // TODO(dneto): Add option to handle other environments.
- spvtools::SpirvTools spv_tools(SPV_ENV_WEBGPU_0);
+ spvtools::SpirvTools spv_tools(kTargetEnv);
// Error messages from SPIRV-Tools are forwarded as failures.
- auto message_consumer =
- [this](spv_message_level_t level, const char* /*source*/,
- const spv_position_t& position, const char* message) {
- switch (level) {
- // Drop info and warning message.
- case SPV_MSG_WARNING:
- case SPV_MSG_INFO:
- default:
- // For binary validation errors, we only have the instruction
- // number. It's not text, so there is no column number.
- this->Fail() << "line:" << position.index << ": " << message;
- }
- };
- spv_tools.SetMessageConsumer(message_consumer);
+ spv_tools.SetMessageConsumer(message_consumer_);
// Only consider valid modules.
if (success_) {
success_ = spv_tools.Validate(spv_binary_);
}
+ if (success_) {
+ success_ = BuildInternalModule();
+ }
+
return success_;
}
@@ -65,6 +85,25 @@
return std::move(ast_module_);
}
+bool ParserImpl::BuildInternalModule() {
+ tools_.SetMessageConsumer(message_consumer_);
+
+ const spv_context& context = tools_context_.CContext();
+ ir_context_ = spvtools::BuildModule(context->target_env, context->consumer,
+ spv_binary_.data(), spv_binary_.size());
+ if (!ir_context_) {
+ return Fail() << "internal error: couldn't build the internal "
+ "representation of the module";
+ }
+ module_ = ir_context_->module();
+ def_use_mgr_ = ir_context_->get_def_use_mgr();
+ constant_mgr_ = ir_context_->get_constant_mgr();
+ type_mgr_ = ir_context_->get_type_mgr();
+ deco_mgr_ = ir_context_->get_decoration_mgr();
+
+ return true;
+}
+
} // namespace spv
} // namespace reader
} // namespace tint
diff --git a/src/reader/spv/parser_impl.h b/src/reader/spv/parser_impl.h
index 33c84c2..f67d5ef 100644
--- a/src/reader/spv/parser_impl.h
+++ b/src/reader/spv/parser_impl.h
@@ -20,6 +20,12 @@
#include <sstream>
#include <vector>
+#include "source/opt/constants.h"
+#include "source/opt/decoration_manager.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/type_manager.h"
+#include "spirv-tools/libspirv.hpp"
#include "src/reader/reader.h"
#include "src/reader/spv/fail_stream.h"
@@ -56,6 +62,13 @@
const std::string error() { return errors_.str(); }
private:
+ /// Builds the internal representation of the SPIR-V module.
+ /// Assumes the module is somewhat well-formed. Normally you
+ /// would want to validate the SPIR-V module before attempting
+ /// to build this internal representation.
+ /// @returns true if successful.
+ bool BuildInternalModule();
+
// The SPIR-V binary we're parsing
std::vector<uint32_t> spv_binary_;
@@ -67,6 +80,19 @@
// Collector for diagnostic messages.
std::stringstream errors_;
FailStream fail_stream_;
+ spvtools::MessageConsumer message_consumer_;
+
+ // The internal representation of the SPIR-V module and its context.
+ spvtools::Context tools_context_;
+ spvtools::SpirvTools tools_;
+ // All the state is owned by ir_context_.
+ std::unique_ptr<spvtools::opt::IRContext> ir_context_;
+ // The following are borrowed pointers to the internal state of ir_context_.
+ spvtools::opt::Module* module_ = nullptr;
+ spvtools::opt::analysis::DefUseManager* def_use_mgr_ = nullptr;
+ spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr;
+ spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
+ spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
};
} // namespace spv