[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