diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39156b5948..a64b78beba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -317,6 +317,7 @@ if(${TINT_BUILD_SPV_READER}) reader/spirv/fail_stream_test.cc reader/spirv/namer_test.cc reader/spirv/parser_impl_import_test.cc + reader/spirv/parser_impl_user_name_test.cc reader/spirv/parser_impl_test.cc reader/spirv/parser_test.cc reader/spirv/spirv_tools_helpers_test.cc diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index f30d0487db..6391c15ea4 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -19,6 +19,8 @@ #include #include "source/opt/build_module.h" +#include "source/opt/instruction.h" +#include "source/opt/module.h" #include "spirv-tools/libspirv.hpp" namespace tint { @@ -35,6 +37,7 @@ ParserImpl::ParserImpl(const std::vector& spv_binary) : Reader(), spv_binary_(spv_binary), fail_stream_(&success_, &errors_), + namer_(fail_stream_), tools_context_(kTargetEnv), tools_(kTargetEnv) { // Create a message consumer to propagate error messages from SPIRV-Tools @@ -120,7 +123,7 @@ void ParserImpl::ResetInternalModule() { } bool ParserImpl::ParseInternalModule() { - return RegisterExtendedInstructionImports(); + return RegisterExtendedInstructionImports() && RegisterUserNames(); // TODO(dneto): fill in the rest } @@ -146,6 +149,35 @@ bool ParserImpl::RegisterExtendedInstructionImports() { return true; } +bool ParserImpl::RegisterUserNames() { + // Register names from OpName and OpMemberName + for (const auto& inst : module_->debugs2()) { + switch (inst.opcode()) { + case SpvOpName: + namer_.SuggestSanitizedName(inst.GetSingleWordInOperand(0), + inst.GetInOperand(1).AsString()); + break; + case SpvOpMemberName: + namer_.SuggestSanitizedMemberName(inst.GetSingleWordInOperand(0), + inst.GetSingleWordInOperand(1), + inst.GetInOperand(2).AsString()); + break; + default: + break; + } + } + + // Fill in struct member names, and disambiguate them. + for (const auto* type_inst : module_->GetTypes()) { + if (type_inst->opcode() == SpvOpTypeStruct) { + namer_.ResolveMemberNamesForStruct(type_inst->result_id(), + type_inst->NumInOperands()); + } + } + + return true; +} + } // namespace spirv } // namespace reader } // namespace tint diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index 3c1b921705..573292bbb3 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -33,6 +33,7 @@ #include "src/ast/module.h" #include "src/reader/reader.h" #include "src/reader/spirv/fail_stream.h" +#include "src/reader/spirv/namer.h" namespace tint { namespace reader { @@ -80,6 +81,9 @@ class ParserImpl : Reader { return glsl_std_450_imports_; } + /// @returns the namer object + Namer& namer() { return namer_; } + private: /// Builds the internal representation of the SPIR-V module. /// Assumes the module is somewhat well-formed. Normally you @@ -99,6 +103,11 @@ class ParserImpl : Reader { /// Registers extended instruction imports. Only "GLSL.std.450" is supported. bool RegisterExtendedInstructionImports(); + /// Registers user names for SPIR-V objects, from OpName, and OpMemberName. + /// Also synthesizes struct field names. Ensures uniqueness for names for + /// SPIR-V IDs, and uniqueness of names of fields within any single struct. + bool RegisterUserNames(); + // The SPIR-V binary we're parsing std::vector spv_binary_; @@ -112,6 +121,9 @@ class ParserImpl : Reader { FailStream fail_stream_; spvtools::MessageConsumer message_consumer_; + // An object used to store and generate names for SPIR-V objects. + Namer namer_; + // The internal representation of the SPIR-V module and its context. spvtools::Context tools_context_; spvtools::SpirvTools tools_; diff --git a/src/reader/spirv/parser_impl_user_name_test.cc b/src/reader/spirv/parser_impl_user_name_test.cc new file mode 100644 index 0000000000..b20c021696 --- /dev/null +++ b/src/reader/spirv/parser_impl_user_name_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 + +#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::Eq; + +using SpvParseUserNameTest = ::testing::Test; + +TEST_F(SpvParseUserNameTest, RespectOpName) { + ParserImpl p(test::Assemble(R"( + OpName %1 "the_void_type" + %1 = OpTypeVoid + )")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_THAT(p.namer().GetName(1), Eq("the_void_type")); +} + +TEST_F(SpvParseUserNameTest, DistinguishDuplicateSuggestion) { + ParserImpl p(test::Assemble(R"( + OpName %1 "vanilla" + OpName %2 "vanilla" + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + )")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_THAT(p.namer().GetName(1), Eq("vanilla")); + EXPECT_THAT(p.namer().GetName(2), Eq("vanilla_1")); +} + +TEST_F(SpvParseUserNameTest, RespectOpMemberName) { + ParserImpl p(test::Assemble(R"( + OpMemberName %3 0 "strawberry" + OpMemberName %3 1 "vanilla" + OpMemberName %3 2 "chocolate" + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %2 %2 %2 + )")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_THAT(p.namer().GetMemberName(3, 0), Eq("strawberry")); + EXPECT_THAT(p.namer().GetMemberName(3, 1), Eq("vanilla")); + EXPECT_THAT(p.namer().GetMemberName(3, 2), Eq("chocolate")); +} + +TEST_F(SpvParseUserNameTest, SynthesizeMemberNames) { + ParserImpl p(test::Assemble(R"( + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %2 %2 %2 + )")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_THAT(p.namer().GetMemberName(3, 0), Eq("field0")); + EXPECT_THAT(p.namer().GetMemberName(3, 1), Eq("field1")); + EXPECT_THAT(p.namer().GetMemberName(3, 2), Eq("field2")); +} + +TEST_F(SpvParseUserNameTest, MemberNamesMixUserAndSynthesized) { + ParserImpl p(test::Assemble(R"( + OpMemberName %3 1 "vanilla" + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %2 %2 %2 + )")); + EXPECT_TRUE(p.BuildAndParseInternalModule()); + EXPECT_THAT(p.namer().GetMemberName(3, 0), Eq("field0")); + EXPECT_THAT(p.namer().GetMemberName(3, 1), Eq("vanilla")); + EXPECT_THAT(p.namer().GetMemberName(3, 2), Eq("field2")); +} + +} // namespace +} // namespace spirv +} // namespace reader +} // namespace tint