[spirv-reader] Support DescriptorSet and Binding

Bug: tint:3
Change-Id: I92d44cc59883d3834aedd2529ab5e20b49d6eb31
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/25200
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 9c1a158..3cd3d7e 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -35,12 +35,14 @@
 #include "spirv-tools/libspirv.hpp"
 #include "src/ast/as_expression.h"
 #include "src/ast/binary_expression.h"
+#include "src/ast/binding_decoration.h"
 #include "src/ast/bool_literal.h"
 #include "src/ast/builtin.h"
 #include "src/ast/builtin_decoration.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/float_literal.h"
 #include "src/ast/scalar_constructor_expression.h"
+#include "src/ast/set_decoration.h"
 #include "src/ast/sint_literal.h"
 #include "src/ast/struct.h"
 #include "src/ast/struct_decoration.h"
@@ -985,6 +987,24 @@
       ast_decorations.emplace_back(
           std::make_unique<ast::LocationDecoration>(deco[1]));
     }
+    if (deco[0] == SpvDecorationDescriptorSet) {
+      if (deco.size() == 1) {
+        Fail() << "malformed DescriptorSet decoration on ID " << id
+               << ": has no operand";
+        return nullptr;
+      }
+      ast_decorations.emplace_back(
+          std::make_unique<ast::SetDecoration>(deco[1]));
+    }
+    if (deco[0] == SpvDecorationBinding) {
+      if (deco.size() == 1) {
+        Fail() << "malformed Binding decoration on ID " << id
+               << ": has no operand";
+        return nullptr;
+      }
+      ast_decorations.emplace_back(
+          std::make_unique<ast::BindingDecoration>(deco[1]));
+    }
   }
   if (!ast_decorations.empty()) {
     auto decorated_var =
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index 83895af..dac8467 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -1184,6 +1184,114 @@
          "instruction, found '4'."));
 }
 
+TEST_F(SpvParserTest, ModuleScopeVar_DescriptorSetDecoration_Valid) {
+  auto* p = parser(test::Assemble(R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar DescriptorSet 3
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(
+  DecoratedVariable{
+    Decorations{
+      SetDecoration{3}
+    }
+    myvar
+    storage_buffer
+    __alias_S__struct_S
+  })"))
+      << module_str;
+}
+
+TEST_F(SpvParserTest,
+       ModuleScopeVar_DescriptorSetDecoration_MissingOperandWontAssemble) {
+  const auto assembly = R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar DescriptorSet
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )";
+  EXPECT_THAT(test::AssembleFailure(assembly),
+              Eq("3:5: Expected operand, found next instruction instead."));
+}
+
+TEST_F(SpvParserTest,
+       ModuleScopeVar_DescriptorSetDecoration_TwoOperandsWontAssemble) {
+  const auto assembly = R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar DescriptorSet 3 4
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )";
+  EXPECT_THAT(
+      test::AssembleFailure(assembly),
+      Eq("2:39: Expected <opcode> or <result-id> at the beginning of an "
+         "instruction, found '4'."));
+}
+
+TEST_F(SpvParserTest, ModuleScopeVar_BindingDecoration_Valid) {
+  auto* p = parser(test::Assemble(R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar Binding 3
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(
+  DecoratedVariable{
+    Decorations{
+      BindingDecoration{3}
+    }
+    myvar
+    storage_buffer
+    __alias_S__struct_S
+  })"))
+      << module_str;
+}
+
+TEST_F(SpvParserTest,
+       ModuleScopeVar_BindingDecoration_MissingOperandWontAssemble) {
+  const auto assembly = R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar Binding
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )";
+  EXPECT_THAT(test::AssembleFailure(assembly),
+              Eq("3:5: Expected operand, found next instruction instead."));
+}
+
+TEST_F(SpvParserTest,
+       ModuleScopeVar_BindingDecoration_TwoOperandsWontAssemble) {
+  const auto assembly = R"(
+     OpName %myvar "myvar"
+     OpDecorate %myvar Binding 3 4
+     OpDecorate %strct Block
+)" + CommonTypes() + R"(
+     %ptr_sb_strct = OpTypePointer StorageBuffer %strct
+     %myvar = OpVariable %ptr_sb_strct StorageBuffer
+  )";
+  EXPECT_THAT(
+      test::AssembleFailure(assembly),
+      Eq("2:33: Expected <opcode> or <result-id> at the beginning of an "
+         "instruction, found '4'."));
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader