[spirv-reader] Get decorations
Bug: tint:3
Change-Id: Idfd49c5bfc8629c17f42f954d65c881865548b8a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18140
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index dd2fd63..5813e8b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -324,6 +324,7 @@
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 8679f03..51b94e3 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -21,6 +21,7 @@
#include <utility>
#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 @@
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<uint32_t> 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<uint32_t> 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 a422cb6..0bb6694 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -41,6 +41,13 @@
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<uint32_t>;
+using DecorationList = std::vector<Decoration>;
+
/// Parser implementation for SPIR-V.
class ParserImpl : Reader {
public:
@@ -95,6 +102,22 @@
/// @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 0000000..ef5a5f1
--- /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