[spirv-writer] Generate pointer types

This CL adds the type generation for OpTypePointer.

Bug: tint:5
Change-Id: I9cfe6c4fbd9173df925421f278abd1afbda50fc9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17840
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index fca9232..5820bf2 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -24,6 +24,7 @@
 #include "src/ast/struct_member_offset_decoration.h"
 #include "src/ast/type/array_type.h"
 #include "src/ast/type/matrix_type.h"
+#include "src/ast/type/pointer_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/u32_type.h"
 #include "src/ast/type/vector_type.h"
@@ -282,6 +283,10 @@
     if (!GenerateMatrixType(type->AsMatrix(), result)) {
       return 0;
     }
+  } else if (type->IsPointer()) {
+    if (!GeneratePointerType(type->AsPointer(), result)) {
+      return 0;
+    }
   } else if (type->IsStruct()) {
     if (!GenerateStructType(type->AsStruct(), result)) {
       return 0;
@@ -339,6 +344,24 @@
   return true;
 }
 
+bool Builder::GeneratePointerType(ast::type::PointerType* ptr,
+                                  const Operand& result) {
+  auto pointee_id = GenerateTypeIfNeeded(ptr->type());
+  if (pointee_id == 0) {
+    return false;
+  }
+
+  auto stg_class = ConvertStorageClass(ptr->storage_class());
+  if (stg_class == SpvStorageClassMax) {
+    return false;
+  }
+
+  push_type(spv::Op::OpTypePointer,
+            {result, Operand::Int(stg_class), Operand::Int(pointee_id)});
+
+  return true;
+}
+
 bool Builder::GenerateStructType(ast::type::StructType* struct_type,
                                  const Operand& result) {
   auto struct_id = result.to_i();
@@ -409,6 +432,34 @@
   return true;
 }
 
+SpvStorageClass Builder::ConvertStorageClass(ast::StorageClass klass) const {
+  switch (klass) {
+    case ast::StorageClass::kInput:
+      return SpvStorageClassInput;
+    case ast::StorageClass::kOutput:
+      return SpvStorageClassOutput;
+    case ast::StorageClass::kUniform:
+      return SpvStorageClassUniform;
+    case ast::StorageClass::kWorkgroup:
+      return SpvStorageClassWorkgroup;
+    case ast::StorageClass::kUniformConstant:
+      return SpvStorageClassUniformConstant;
+    case ast::StorageClass::kStorageBuffer:
+      return SpvStorageClassStorageBuffer;
+    case ast::StorageClass::kImage:
+      return SpvStorageClassImage;
+    case ast::StorageClass::kPushConstant:
+      return SpvStorageClassPushConstant;
+    case ast::StorageClass::kPrivate:
+      return SpvStorageClassPrivate;
+    case ast::StorageClass::kFunction:
+      return SpvStorageClassFunction;
+    case ast::StorageClass::kNone:
+      break;
+  }
+  return SpvStorageClassMax;
+}
+
 }  // namespace spirv
 }  // namespace writer
 }  // namespace tint
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 727fbab..8aefe56 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -20,6 +20,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "spirv/unified1/spirv.h"
 #include "src/ast/literal.h"
 #include "src/ast/module.h"
 #include "src/ast/struct_member.h"
@@ -122,6 +123,11 @@
   /// @returns the annotations
   const std::vector<Instruction>& annots() const { return annotations_; }
 
+  /// Converts a storage class to a SPIR-V storage class.
+  /// @param klass the storage class to convert
+  /// @returns the SPIR-V storage class or SpvStorageClassMax on error.
+  SpvStorageClass ConvertStorageClass(ast::StorageClass klass) const;
+
   /// Generates an entry point instruction
   /// @param ep the entry point
   /// @returns true if the instruction was generated, false otherwise
@@ -155,6 +161,11 @@
   /// @param result the result operand
   /// @returns true if the matrix was successfully generated
   bool GenerateMatrixType(ast::type::MatrixType* mat, const Operand& result);
+  /// Generates a pointer type declaration
+  /// @param ptr the pointer type to generate
+  /// @param result the result operand
+  /// @returns true if the pointer was successfully generated
+  bool GeneratePointerType(ast::type::PointerType* ptr, const Operand& result);
   /// Generates a vector type declaration
   /// @param struct_type the vector to generate
   /// @param result the result operand
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index da753f7..064ce92 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -236,6 +236,29 @@
   ASSERT_FALSE(b.has_error()) << b.error();
 }
 
+TEST_F(BuilderTest_Type, GeneratePtr) {
+  ast::type::I32Type i32;
+  ast::type::PointerType ptr(&i32, ast::StorageClass::kOutput);
+
+  Builder b;
+  auto id = b.GenerateTypeIfNeeded(&ptr);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(1, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%1 = OpTypePointer Output %2
+)");
+}
+
+TEST_F(BuilderTest_Type, ReturnsGeneratedPtr) {
+  ast::type::I32Type i32;
+  ast::type::PointerType ptr(&i32, ast::StorageClass::kOutput);
+
+  Builder b;
+  EXPECT_EQ(b.GenerateTypeIfNeeded(&ptr), 1);
+  EXPECT_EQ(b.GenerateTypeIfNeeded(&ptr), 1);
+}
+
 TEST_F(BuilderTest_Type, GenerateStruct_Empty) {
   auto s = std::make_unique<ast::Struct>();
   ast::type::StructType s_type(std::move(s));
@@ -419,6 +442,39 @@
   ASSERT_FALSE(b.has_error()) << b.error();
 }
 
+struct PtrData {
+  ast::StorageClass ast_class;
+  SpvStorageClass result;
+};
+inline std::ostream& operator<<(std::ostream& out, PtrData data) {
+  out << data.ast_class;
+  return out;
+}
+using PtrDataTest = testing::TestWithParam<PtrData>;
+TEST_P(PtrDataTest, ConvertStorageClass) {
+  auto params = GetParam();
+
+  Builder b;
+  EXPECT_EQ(b.ConvertStorageClass(params.ast_class), params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+    BuilderTest_Type,
+    PtrDataTest,
+    testing::Values(
+        PtrData{ast::StorageClass::kNone, SpvStorageClassMax},
+        PtrData{ast::StorageClass::kInput, SpvStorageClassInput},
+        PtrData{ast::StorageClass::kOutput, SpvStorageClassOutput},
+        PtrData{ast::StorageClass::kUniform, SpvStorageClassUniform},
+        PtrData{ast::StorageClass::kWorkgroup, SpvStorageClassWorkgroup},
+        PtrData{ast::StorageClass::kUniformConstant,
+                SpvStorageClassUniformConstant},
+        PtrData{ast::StorageClass::kStorageBuffer,
+                SpvStorageClassStorageBuffer},
+        PtrData{ast::StorageClass::kImage, SpvStorageClassImage},
+        PtrData{ast::StorageClass::kPushConstant, SpvStorageClassPushConstant},
+        PtrData{ast::StorageClass::kPrivate, SpvStorageClassPrivate},
+        PtrData{ast::StorageClass::kFunction, SpvStorageClassFunction}));
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer