spv: reject invalid SPIR-V
Use the SPIRV-Tools validator, with the WebGPU0 environment.
Bug: tint:3
Change-Id: Id1132d209fd939ed68587034761e97da9b35b21d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16821
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index df73e33..93001a6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -319,6 +319,7 @@
if(${TINT_BUILD_SPV_PARSER})
list (APPEND TINT_TEST_SRCS
reader/spv/fail_stream_test.cc
+ reader/spv/parser_impl_test.cc
reader/spv/parser_test.cc
)
endif()
diff --git a/src/reader/spv/parser_impl.cc b/src/reader/spv/parser_impl.cc
index f06ae08..560f3d1 100644
--- a/src/reader/spv/parser_impl.cc
+++ b/src/reader/spv/parser_impl.cc
@@ -14,6 +14,7 @@
#include <cstring>
+#include "spirv-tools/libspirv.hpp"
#include "src/reader/spv/parser_impl.h"
namespace tint {
@@ -26,9 +27,33 @@
ParserImpl::~ParserImpl() = default;
bool ParserImpl::Parse() {
- // Exit early if we've already failed.
+ if (!success_) {
+ return false;
+ }
+
+ // Set up use of SPIRV-Tools utilities.
+ // TODO(dneto): Add option to handle other environments.
+ spvtools::SpirvTools spv_tools(SPV_ENV_WEBGPU_0);
+
+ // 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);
+
+ // Only consider valid modules.
if (success_) {
- Fail() << "SPIR-V parsing is not supported yet";
+ success_ = spv_tools.Validate(spv_binary_);
}
return success_;
diff --git a/src/reader/spv/parser_impl.h b/src/reader/spv/parser_impl.h
index ac5a38b..33c84c2 100644
--- a/src/reader/spv/parser_impl.h
+++ b/src/reader/spv/parser_impl.h
@@ -33,6 +33,7 @@
/// Creates a new parser
/// @param input the input data to parse
explicit ParserImpl(const std::vector<uint32_t>& input);
+ /// Destructor
~ParserImpl() override;
/// Run the parser
diff --git a/src/reader/spv/parser_impl_test.cc b/src/reader/spv/parser_impl_test.cc
new file mode 100644
index 0000000..9881fa3
--- /dev/null
+++ b/src/reader/spv/parser_impl_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2020 The Tint Authors.
+//
+// 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.
+
+#include "src/reader/spv/parser_impl.h"
+
+#include <cstdint>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace tint {
+namespace reader {
+namespace spv {
+
+namespace {
+
+using ::testing::HasSubstr;
+
+using SpvParserImplTest = testing::Test;
+
+TEST_F(SpvParserImplTest, Uint32VecEmpty) {
+ std::vector<uint32_t> data;
+ ParserImpl p{data};
+ EXPECT_FALSE(p.Parse());
+ // TODO(dneto): What message?
+}
+
+/// @returns the SPIR-V module assembled from the given text. Numeric IDs
+/// are preserved.
+std::vector<uint32_t> Assemble(const std::string& spirv_assembly) {
+ // TODO(dneto): Use ScopedTrace?
+
+ // (The target environment doesn't affect assembly.
+ spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
+ std::stringstream errors;
+ std::vector<uint32_t> result;
+ tools.SetMessageConsumer([&errors](spv_message_level_t, const char*,
+ const spv_position_t& position,
+ const char* message) {
+ errors << "assembly error:" << position.line << ":" << position.column
+ << ": " << message;
+ });
+
+ const auto success = tools.Assemble(
+ spirv_assembly, &result, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ EXPECT_TRUE(success) << errors.str();
+
+ return result;
+}
+
+TEST_F(SpvParserImplTest, InvalidModuleFails) {
+ auto invalid_spv = Assemble("%ty = OpTypeInt 3 0");
+ ParserImpl p{invalid_spv};
+ EXPECT_FALSE(p.Parse());
+ EXPECT_THAT(
+ p.error(),
+ HasSubstr("TypeInt cannot appear before the memory model instruction"));
+ EXPECT_THAT(p.error(), HasSubstr("OpTypeInt 3 0"));
+}
+
+// TODO(dneto): uint32 vec, valid SPIR-V
+
+} // namespace
+
+} // namespace spv
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/spv/parser_test.cc b/src/reader/spv/parser_test.cc
index 717034b..035f3c8 100644
--- a/src/reader/spv/parser_test.cc
+++ b/src/reader/spv/parser_test.cc
@@ -14,8 +14,7 @@
#include "src/reader/spv/parser.h"
-#include <stdint.h>
-
+#include <cstdint>
#include <vector>
#include "gtest/gtest.h"