spv: Parse OpExtInst for GLSL.std.450
It will always use the "std::glsl" import name.
Remember all the IDs of such imports.
Never add more than one GLSL.std.450 import to the AST.
Also refactor the Assemble test helper into its own file.
Bug: tint:3
Change-Id: I5b2b70ea0f00d44aacf553aa009756dff2a4cecf
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/16662
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c47a190..ae349de 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -353,8 +353,11 @@
if(${TINT_BUILD_SPV_PARSER})
list (APPEND TINT_TEST_SRCS
reader/spv/fail_stream_test.cc
+ reader/spv/parser_impl_import_test.cc
reader/spv/parser_impl_test.cc
reader/spv/parser_test.cc
+ reader/spv/spirv_tools_helpers_test.cc
+ reader/spv/spirv_tools_helpers_test.h
)
endif()
diff --git a/src/reader/spv/fail_stream.h b/src/reader/spv/fail_stream.h
index aa2c588..714359b 100644
--- a/src/reader/spv/fail_stream.h
+++ b/src/reader/spv/fail_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2020 Google LLC
+// 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.
diff --git a/src/reader/spv/parser_impl.cc b/src/reader/spv/parser_impl.cc
index c31d2c6..8adba19 100644
--- a/src/reader/spv/parser_impl.cc
+++ b/src/reader/spv/parser_impl.cc
@@ -104,6 +104,45 @@
return true;
}
+void ParserImpl::ResetInternalModule() {
+ ir_context_.reset(nullptr);
+ module_ = nullptr;
+ def_use_mgr_ = nullptr;
+ constant_mgr_ = nullptr;
+ type_mgr_ = nullptr;
+ deco_mgr_ = nullptr;
+
+ import_map_.clear();
+ glsl_std_450_imports_.clear();
+}
+
+bool ParserImpl::ParseInternalModule() {
+ return ParseExtendedInstructionImports();
+ // TODO(dneto): fill in the rest
+}
+
+bool ParserImpl::ParseExtendedInstructionImports() {
+ for (const spvtools::opt::Instruction& import : module_->ext_inst_imports()) {
+ std::string name(
+ reinterpret_cast<const char*>(import.GetInOperand(0).words.data()));
+ // TODO(dneto): Handle other extended instruction sets when needed.
+ if (name == "GLSL.std.450") {
+ // Only create the AST import once, so we can use import name 'std::glsl'.
+ // This is a canonicalization.
+ if (glsl_std_450_imports_.empty()) {
+ auto ast_import =
+ std::make_unique<tint::ast::Import>(name, "std::glsl");
+ import_map_[import.result_id()] = ast_import.get();
+ ast_module_.AddImport(std::move(ast_import));
+ }
+ glsl_std_450_imports_.insert(import.result_id());
+ } else {
+ return Fail() << "Unrecognized extended instruction set: " << name;
+ }
+ }
+ 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 f67d5ef..8c5de47 100644
--- a/src/reader/spv/parser_impl.h
+++ b/src/reader/spv/parser_impl.h
@@ -18,6 +18,8 @@
#include <cstdint>
#include <memory>
#include <sstream>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include "source/opt/constants.h"
@@ -26,6 +28,8 @@
#include "source/opt/module.h"
#include "source/opt/type_manager.h"
#include "spirv-tools/libspirv.hpp"
+#include "src/ast/import.h"
+#include "src/ast/module.h"
#include "src/reader/reader.h"
#include "src/reader/spv/fail_stream.h"
@@ -61,6 +65,20 @@
/// @returns the accumulated error string
const std::string error() { return errors_.str(); }
+ /// Builds an internal representation of the SPIR-V binary,
+ /// and parses it into a Tint AST module. Diagnostics are emitted
+ /// to the error stream.
+ /// @returns true if it was successful.
+ bool BuildAndParseInternalModule() {
+ return BuildInternalModule() && ParseInternalModule();
+ }
+
+ /// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
+ /// extended instruction set.
+ const std::unordered_set<uint32_t>& glsl_std_450_imports() const {
+ return glsl_std_450_imports_;
+ }
+
private:
/// Builds the internal representation of the SPIR-V module.
/// Assumes the module is somewhat well-formed. Normally you
@@ -69,6 +87,17 @@
/// @returns true if successful.
bool BuildInternalModule();
+ /// Walks the internal representation of the module to populate
+ /// the AST form of the module.
+ /// @returns true on success
+ bool ParseInternalModule();
+
+ /// Destroys the internal representation of the SPIR-V module.
+ void ResetInternalModule();
+
+ /// Parses OpExtInstImport instructions.
+ bool ParseExtendedInstructionImports();
+
// The SPIR-V binary we're parsing
std::vector<uint32_t> spv_binary_;
@@ -93,6 +122,12 @@
spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr;
spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
+
+ /// Maps a SPIR-V ID for an external instruction import to an AST import
+ std::unordered_map<uint32_t, ast::Import*> import_map_;
+ // The set of IDs that are imports of the GLSL.std.450 extended instruction
+ // sets.
+ std::unordered_set<uint32_t> glsl_std_450_imports_;
};
} // namespace spv
diff --git a/src/reader/spv/parser_impl_import_test.cc b/src/reader/spv/parser_impl_import_test.cc
new file mode 100644
index 0000000..b6ee771
--- /dev/null
+++ b/src/reader/spv/parser_impl_import_test.cc
@@ -0,0 +1,74 @@
+// 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 <memory>
+#include <sstream>
+
+#include "gmock/gmock.h"
+#include "src/reader/spv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spv {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Not;
+using ::testing::UnorderedElementsAre;
+
+using SpvParseImport = ::testing::Test;
+
+TEST_F(SpvParseImport, NoImport) {
+ ParserImpl p(test::Assemble("%1 = OpTypeVoid"));
+ EXPECT_TRUE(p.BuildAndParseInternalModule());
+ EXPECT_TRUE(p.error().empty());
+ const auto module_ast = p.module().to_str();
+ EXPECT_THAT(module_ast, Not(HasSubstr("Import")));
+}
+
+TEST_F(SpvParseImport, ImportGlslStd450) {
+ ParserImpl p(test::Assemble(R"(%1 = OpExtInstImport "GLSL.std.450")"));
+ EXPECT_TRUE(p.BuildAndParseInternalModule());
+ EXPECT_TRUE(p.error().empty());
+ EXPECT_THAT(p.glsl_std_450_imports(), ElementsAre(1));
+ const auto module_ast = p.module().to_str();
+ EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
+}
+
+TEST_F(SpvParseImport, ImportGlslStd450Twice) {
+ ParserImpl p(test::Assemble(R"(
+ %1 = OpExtInstImport "GLSL.std.450"
+ %42 = OpExtInstImport "GLSL.std.450"
+ )"));
+ EXPECT_TRUE(p.BuildAndParseInternalModule());
+ EXPECT_TRUE(p.error().empty());
+ EXPECT_THAT(p.glsl_std_450_imports(), UnorderedElementsAre(1, 42));
+ const auto module = p.module();
+ EXPECT_EQ(module.imports().size(), 1);
+ const auto module_ast = module.to_str();
+ // TODO(dneto): Use a matcher to show there is only one import.
+ EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
+}
+
+// TODO(dneto): We don't currently support other kinds of extended instruction
+// imports.
+
+} // namespace
+} // namespace spv
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/spv/parser_impl_test.cc b/src/reader/spv/parser_impl_test.cc
index 9881fa3..5604e8e 100644
--- a/src/reader/spv/parser_impl_test.cc
+++ b/src/reader/spv/parser_impl_test.cc
@@ -18,7 +18,7 @@
#include <vector>
#include "gmock/gmock.h"
-#include "spirv-tools/libspirv.hpp"
+#include "src/reader/spv/spirv_tools_helpers_test.h"
namespace tint {
namespace reader {
@@ -37,31 +37,8 @@
// 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");
+ auto invalid_spv = test::Assemble("%ty = OpTypeInt 3 0");
ParserImpl p{invalid_spv};
EXPECT_FALSE(p.Parse());
EXPECT_THAT(
diff --git a/src/reader/spv/spirv_tools_helpers_test.cc b/src/reader/spv/spirv_tools_helpers_test.cc
new file mode 100644
index 0000000..00d6d51
--- /dev/null
+++ b/src/reader/spv/spirv_tools_helpers_test.cc
@@ -0,0 +1,55 @@
+// 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/spirv_tools_helpers_test.h"
+
+#include <cstdint>
+#include <sstream>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace tint {
+namespace reader {
+namespace spv {
+namespace test {
+
+/// @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;
+}
+
+} // namespace test
+} // namespace spv
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/spv/spirv_tools_helpers_test.h b/src/reader/spv/spirv_tools_helpers_test.h
new file mode 100644
index 0000000..b77d954
--- /dev/null
+++ b/src/reader/spv/spirv_tools_helpers_test.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_
+#define SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace tint {
+namespace reader {
+namespace spv {
+namespace test {
+
+/// @returns the SPIR-V module assembled from the given text. Numeric IDs
+/// are preserved.
+std::vector<uint32_t> Assemble(const std::string& spirv_assembly);
+
+} // namespace test
+} // namespace spv
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_SPV_SPIRV_TOOLS_HELPERS_TEST_H_