From a14fec951e15848acb27da0e1e9bfc66ec4c9bb1 Mon Sep 17 00:00:00 2001 From: David Neto Date: Mon, 23 Mar 2020 22:54:36 +0000 Subject: [PATCH] readers::spirv: Emit entry points Bug: tint:3 Change-Id: I66b99ad6ecb3f409f9df7bfa9aa6c4da65e3f66b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17582 Reviewed-by: dan sinclair --- src/CMakeLists.txt | 1 + src/reader/spirv/parser_impl.cc | 39 +++++++- src/reader/spirv/parser_impl.h | 6 ++ .../spirv/parser_impl_entry_point_test.cc | 92 +++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/reader/spirv/parser_impl_entry_point_test.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fcbb43599..b459584586 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -319,6 +319,7 @@ if(${TINT_BUILD_SPV_READER}) reader/spirv/enum_converter_test.cc reader/spirv/fail_stream_test.cc reader/spirv/namer_test.cc + reader/spirv/parser_impl_entry_point_test.cc reader/spirv/parser_impl_import_test.cc reader/spirv/parser_impl_user_name_test.cc reader/spirv/parser_impl_test.cc diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index 6391c15ea4..429ceba4a0 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -38,6 +38,7 @@ ParserImpl::ParserImpl(const std::vector& spv_binary) spv_binary_(spv_binary), fail_stream_(&success_, &errors_), namer_(fail_stream_), + enum_converter_(fail_stream_), tools_context_(kTargetEnv), tools_(kTargetEnv) { // Create a message consumer to propagate error messages from SPIRV-Tools @@ -123,8 +124,20 @@ void ParserImpl::ResetInternalModule() { } bool ParserImpl::ParseInternalModule() { - return RegisterExtendedInstructionImports() && RegisterUserNames(); + if (!success_) { + return false; + }; + if (!RegisterExtendedInstructionImports()) { + return false; + } + if (!RegisterUserNames()) { + return false; + } + if (!EmitEntryPoints()) { + return false; + } // TODO(dneto): fill in the rest + return true; } bool ParserImpl::RegisterExtendedInstructionImports() { @@ -150,6 +163,16 @@ bool ParserImpl::RegisterExtendedInstructionImports() { } bool ParserImpl::RegisterUserNames() { + // Register entry point names. An entry point name is the point of contact + // between the API and the shader. It has the highest priority for + // preservation, so register it first. + for (const spvtools::opt::Instruction& entry_point : + module_->entry_points()) { + const uint32_t function_id = entry_point.GetSingleWordInOperand(1); + const std::string name = entry_point.GetInOperand(2).AsString(); + namer_.SuggestSanitizedName(function_id, name); + } + // Register names from OpName and OpMemberName for (const auto& inst : module_->debugs2()) { switch (inst.opcode()) { @@ -178,6 +201,20 @@ bool ParserImpl::RegisterUserNames() { return true; } +bool ParserImpl::EmitEntryPoints() { + for (const spvtools::opt::Instruction& entry_point : + module_->entry_points()) { + const auto stage = SpvExecutionModel(entry_point.GetSingleWordInOperand(0)); + const uint32_t function_id = entry_point.GetSingleWordInOperand(1); + const std::string name = namer_.GetName(function_id); + + ast_module_.AddEntryPoint(std::make_unique( + enum_converter_.ToPipelineStage(stage), "", name)); + } + // The enum conversion could have failed, so return the existing status value. + return success_; +} + } // namespace spirv } // namespace reader } // namespace tint diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index 573292bbb3..67f1d259d7 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -32,6 +32,7 @@ #include "src/ast/import.h" #include "src/ast/module.h" #include "src/reader/reader.h" +#include "src/reader/spirv/enum_converter.h" #include "src/reader/spirv/fail_stream.h" #include "src/reader/spirv/namer.h" @@ -108,6 +109,9 @@ class ParserImpl : Reader { /// SPIR-V IDs, and uniqueness of names of fields within any single struct. bool RegisterUserNames(); + /// Emit entry point AST nodes. + bool EmitEntryPoints(); + // The SPIR-V binary we're parsing std::vector spv_binary_; @@ -123,6 +127,8 @@ class ParserImpl : Reader { // An object used to store and generate names for SPIR-V objects. Namer namer_; + // An object used to convert SPIR-V enums to Tint enums + EnumConverter enum_converter_; // The internal representation of the SPIR-V module and its context. spvtools::Context tools_context_; diff --git a/src/reader/spirv/parser_impl_entry_point_test.cc b/src/reader/spirv/parser_impl_entry_point_test.cc new file mode 100644 index 0000000000..0e772a40a1 --- /dev/null +++ b/src/reader/spirv/parser_impl_entry_point_test.cc @@ -0,0 +1,92 @@ +// 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 + +#include "gmock/gmock.h" +#include "src/reader/spirv/parser_impl.h" +#include "src/reader/spirv/spirv_tools_helpers_test.h" + +namespace tint { +namespace reader { +namespace spirv { +namespace { + +using ::testing::HasSubstr; + +using SpvParseImport = ::testing::Test; + +std::string MakeEntryPoint(const std::string& stage, + const std::string& name, + const std::string& id = "42") { + return std::string("OpEntryPoint ") + stage + " %" + id + "2 \"" + name + + "\"\n"; +} + +TEST_F(SpvParseImport, NoEntryPoint) { + ParserImpl p(test::Assemble("")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_ast = p.module().to_str(); + EXPECT_THAT(module_ast, Not(HasSubstr("EntryPoint"))); +} + +TEST_F(SpvParseImport, EntryPointVertex) { + ParserImpl p(test::Assemble(MakeEntryPoint("GLCompute", "foobar"))); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_str = p.module().to_str(); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{compute = foobar})")); +} + +TEST_F(SpvParseImport, EntryPointFragment) { + ParserImpl p(test::Assemble(MakeEntryPoint("Fragment", "blitz"))); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_str = p.module().to_str(); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{fragment = blitz})")); +} + +TEST_F(SpvParseImport, EntryPointCompute) { + ParserImpl p(test::Assemble(MakeEntryPoint("GLCompute", "sort"))); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_str = p.module().to_str(); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{compute = sort})")); +} + +TEST_F(SpvParseImport, EntryPointMultiNameConflict) { + ParserImpl p(test::Assemble(MakeEntryPoint("GLCompute", "work", "40") + + MakeEntryPoint("Vertex", "work", "50") + + MakeEntryPoint("Fragment", "work", "60"))); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_str = p.module().to_str(); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{compute = work})")); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{vertex = work_1})")); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{fragment = work_2})")); +} + +TEST_F(SpvParseImport, EntryPointNameIsSanitized) { + ParserImpl p(test::Assemble(MakeEntryPoint("GLCompute", ".1234"))); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_TRUE(p.error().empty()); + const auto module_str = p.module().to_str(); + EXPECT_THAT(module_str, HasSubstr(R"(EntryPoint{compute = x_1234})")); +} + +} // namespace +} // namespace spirv +} // namespace reader +} // namespace tint