diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd2fd63370..5813e8b19b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -324,6 +324,7 @@ if(${TINT_BUILD_SPV_READER}) reader/spirv/namer_test.cc reader/spirv/parser_impl_convert_type_test.cc reader/spirv/parser_impl_entry_point_test.cc + reader/spirv/parser_impl_get_decorations_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 8679f03158..51b94e33e2 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -21,6 +21,7 @@ #include #include "source/opt/build_module.h" +#include "source/opt/decoration_manager.h" #include "source/opt/instruction.h" #include "source/opt/module.h" #include "source/opt/type_manager.h" @@ -252,6 +253,42 @@ ast::type::Type* ParserImpl::ConvertType(uint32_t type_id) { return result; } +DecorationList ParserImpl::GetDecorationsFor(uint32_t id) const { + DecorationList result; + const auto& decorations = deco_mgr_->GetDecorationsFor(id, true); + for (const auto* inst : decorations) { + if (inst->opcode() != SpvOpDecorate) { + continue; + } + // Example: OpDecorate %struct_id Block + // Example: OpDecorate %array_ty ArrayStride 16 + std::vector inst_as_words; + inst->ToBinaryWithoutAttachedDebugInsts(&inst_as_words); + Decoration d(inst_as_words.begin() + 2, inst_as_words.end()); + result.push_back(d); + } + return result; +} + +DecorationList ParserImpl::GetDecorationsForMember( + uint32_t id, + uint32_t member_index) const { + DecorationList result; + const auto& decorations = deco_mgr_->GetDecorationsFor(id, true); + for (const auto* inst : decorations) { + if ((inst->opcode() != SpvOpMemberDecorate) || + (inst->GetSingleWordInOperand(1) != member_index)) { + continue; + } + // Example: OpMemberDecorate %struct_id 2 Offset 24 + std::vector inst_as_words; + inst->ToBinaryWithoutAttachedDebugInsts(&inst_as_words); + Decoration d(inst_as_words.begin() + 3, inst_as_words.end()); + result.push_back(d); + } + return result; +} + bool ParserImpl::BuildInternalModule() { tools_.SetMessageConsumer(message_consumer_); diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index a422cb6f7a..0bb6694e3f 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -41,6 +41,13 @@ namespace tint { namespace reader { namespace spirv { +/// The binary representation of a SPIR-V decoration enum followed by its +/// operands, if any. +/// Example: { SpvDecorationBlock } +/// Example: { SpvDecorationArrayStride, 16 } +using Decoration = std::vector; +using DecorationList = std::vector; + /// Parser implementation for SPIR-V. class ParserImpl : Reader { public: @@ -95,6 +102,22 @@ class ParserImpl : Reader { /// @returns the namer object Namer& namer() { return namer_; } + /// Gets the list of decorations for a SPIR-V result ID. Returns an empty + /// vector if the ID is not a result ID, or if no decorations target that ID. + /// The internal representation must have already been built. + /// @param id SPIR-V ID + /// @returns the list of decorations on the given ID + DecorationList GetDecorationsFor(uint32_t id) const; + /// Gets the list of decorations for the member of a struct. Returns an empty + /// list if the |id| is not the ID of a struct, or if the member index is out + /// of range, or if the target member has no decorations. + /// The internal representation must have already been built. + /// @param id SPIR-V ID of a struct + /// @param member_index the member within the struct + /// @returns the list of decorations on the member + DecorationList GetDecorationsForMember(uint32_t id, + uint32_t member_index) const; + private: /// Builds the internal representation of the SPIR-V module. /// Assumes the module is somewhat well-formed. Normally you diff --git a/src/reader/spirv/parser_impl_get_decorations_test.cc b/src/reader/spirv/parser_impl_get_decorations_test.cc new file mode 100644 index 0000000000..ef5a5f15dd --- /dev/null +++ b/src/reader/spirv/parser_impl_get_decorations_test.cc @@ -0,0 +1,144 @@ +// 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 "gmock/gmock.h" +#include "spirv/unified1/spirv.h" +#include "src/reader/spirv/parser_impl.h" +#include "src/reader/spirv/parser_impl_test_helper.h" +#include "src/reader/spirv/spirv_tools_helpers_test.h" + +namespace tint { +namespace reader { +namespace spirv { +namespace { + +using ::testing::Eq; +using ::testing::UnorderedElementsAre; + +TEST_F(SpvParserTest, GetDecorationsFor_NotAnId) { + auto p = parser(test::Assemble("")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsFor(42); + EXPECT_TRUE(decorations.empty()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsFor_NoDecorations) { + auto p = parser(test::Assemble("%1 = OpTypeVoid")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsFor(1); + EXPECT_TRUE(decorations.empty()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsFor_OneDecoration) { + auto p = parser(test::Assemble(R"( + OpDecorate %10 Block + %float = OpTypeFloat 32 + %10 = OpTypeStruct %float + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsFor(10); + EXPECT_THAT(decorations, + UnorderedElementsAre(Decoration{SpvDecorationBlock})); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsFor_MultiDecoration) { + auto p = parser(test::Assemble(R"( + OpDecorate %5 RelaxedPrecision + OpDecorate %5 Location 7 ; Invalid case made up for test + %float = OpTypeFloat 32 + %5 = OpConstant %float 3.14 + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsFor(5); + EXPECT_THAT(decorations, + UnorderedElementsAre(Decoration{SpvDecorationRelaxedPrecision}, + Decoration{SpvDecorationLocation, 7})); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsForMember_NotAnId) { + auto p = parser(test::Assemble("")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsForMember(42, 9); + EXPECT_TRUE(decorations.empty()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsForMember_NotAStruct) { + auto p = parser(test::Assemble("%1 = OpTypeVoid")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsFor(1); + EXPECT_TRUE(decorations.empty()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsForMember_MemberWithoutDecoration) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %10 = OpTypeStruct %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsForMember(10, 0); + EXPECT_TRUE(decorations.empty()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsForMember_OneDecoration) { + auto p = parser(test::Assemble(R"( + OpMemberDecorate %10 1 ArrayStride 12 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %arr = OpTypeArray %uint %uint_2 + %10 = OpTypeStruct %uint %arr + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + auto decorations = p->GetDecorationsForMember(10, 1); + EXPECT_THAT(decorations, + UnorderedElementsAre(Decoration{SpvDecorationArrayStride, 12})); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, GetDecorationsForMember_MultiDecoration) { + auto p = parser(test::Assemble(R"( + OpMemberDecorate %50 1 RelaxedPrecision + OpMemberDecorate %50 2 ArrayStride 16 + OpMemberDecorate %50 2 MatrixStride 8 + OpMemberDecorate %50 2 ColMajor + %float = OpTypeFloat 32 + %vec = OpTypeVector %float 2 + %mat = OpTypeMatrix %vec 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %arr = OpTypeArray %mat %uint_2 + %50 = OpTypeStruct %uint %float %arr + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + EXPECT_TRUE(p->GetDecorationsForMember(50, 0).empty()); + EXPECT_THAT(p->GetDecorationsForMember(50, 1), + UnorderedElementsAre(Decoration{SpvDecorationRelaxedPrecision})); + EXPECT_THAT(p->GetDecorationsForMember(50, 2), + UnorderedElementsAre(Decoration{SpvDecorationColMajor}, + Decoration{SpvDecorationMatrixStride, 8}, + Decoration{SpvDecorationArrayStride, 16})); + EXPECT_TRUE(p->error().empty()); +} + +} // namespace +} // namespace spirv +} // namespace reader +} // namespace tint