diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index df73e330ac..93001a60e1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -319,6 +319,7 @@ endif() 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 f06ae0840c..560f3d1c4e 100644 --- a/src/reader/spv/parser_impl.cc +++ b/src/reader/spv/parser_impl.cc @@ -14,6 +14,7 @@ #include +#include "spirv-tools/libspirv.hpp" #include "src/reader/spv/parser_impl.h" namespace tint { @@ -26,9 +27,33 @@ ParserImpl::ParserImpl(const std::vector& spv_binary) 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 ac5a38bbb9..33c84c2c7a 100644 --- a/src/reader/spv/parser_impl.h +++ b/src/reader/spv/parser_impl.h @@ -33,6 +33,7 @@ class ParserImpl : Reader { /// Creates a new parser /// @param input the input data to parse explicit ParserImpl(const std::vector& 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 0000000000..9881fa385c --- /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 +#include + +#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 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 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 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 717034b9d9..035f3c8c0c 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 - +#include #include #include "gtest/gtest.h"