[spirv-reader] Convert pointer type

Bug: tint:3
Change-Id: Ibba1472a1aa3f1399e9596ee6662a29121d88eca
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18420
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 2480a34..efa2d3e 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -38,6 +38,7 @@
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/i32_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/type.h"
 #include "src/ast/type/u32_type.h"
@@ -164,10 +165,10 @@
       return save(ConvertType(spirv_type->AsArray()));
     case spvtools::opt::analysis::Type::kStruct:
       return save(ConvertType(spirv_type->AsStruct()));
-    case spvtools::opt::analysis::Type::kFunction:
     case spvtools::opt::analysis::Type::kPointer:
-      // For now, just return null without erroring out.
-      // TODO(dneto)
+      return save(ConvertType(spirv_type->AsPointer()));
+    case spvtools::opt::analysis::Type::kFunction:
+      // TODO(dneto). For now return null without erroring out.
       return nullptr;
     default:
       break;
@@ -520,6 +521,27 @@
   return ctx_.type_mgr().Get(std::move(ast_struct_type));
 }
 
+ast::type::Type* ParserImpl::ConvertType(
+    const spvtools::opt::analysis::Pointer* ptr_ty) {
+  auto* ast_elem_ty = ConvertType(type_mgr_->GetId(ptr_ty->pointee_type()));
+  if (ast_elem_ty == nullptr) {
+    Fail() << "SPIR-V pointer type with ID " << type_mgr_->GetId(ptr_ty)
+           << " has invalid pointee type "
+           << type_mgr_->GetId(ptr_ty->pointee_type());
+    return nullptr;
+  }
+  auto ast_storage_class =
+      enum_converter_.ToStorageClass(ptr_ty->storage_class());
+  if (ast_storage_class == ast::StorageClass::kNone) {
+    Fail() << "SPIR-V pointer type with ID " << type_mgr_->GetId(ptr_ty)
+           << " has invalid storage class "
+           << static_cast<uint32_t>(ptr_ty->storage_class());
+    return nullptr;
+  }
+  return ctx_.type_mgr().Get(
+      std::make_unique<ast::type::PointerType>(ast_elem_ty, ast_storage_class));
+}
+
 bool ParserImpl::RegisterTypes() {
   if (!success_) {
     return false;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index a38ddaf..4ddcf19 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -190,6 +190,8 @@
   /// Converts a specific SPIR-V type to a Tint type. Struct case
   ast::type::Type* ConvertType(
       const spvtools::opt::analysis::Struct* struct_ty);
+  /// Converts a specific SPIR-V type to a Tint type. Pointer case
+  ast::type::Type* ConvertType(const spvtools::opt::analysis::Pointer* ptr_ty);
 
   // The SPIR-V binary we're parsing
   std::vector<uint32_t> spv_binary_;
diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc
index f5daed0..aa0fe09 100644
--- a/src/reader/spirv/parser_impl_convert_type_test.cc
+++ b/src/reader/spirv/parser_impl_convert_type_test.cc
@@ -20,7 +20,9 @@
 #include "src/ast/struct.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/type.h"
 #include "src/ast/type/vector_type.h"
 #include "src/reader/spirv/parser_impl.h"
 #include "src/reader/spirv/parser_impl_test_helper.h"
@@ -484,11 +486,222 @@
 )"));
 }
 
-// TODO(dneto): Demonstrate other member deocrations. Blocked on
+// TODO(dneto): Demonstrate other member decorations. Blocked on
 // crbug.com/tint/30
 // TODO(dneto): Demonstrate multiple member deocrations. Blocked on
 // crbug.com/tint/30
 
+TEST_F(SpvParserTest, ConvertType_InvalidPointeetype) {
+  // Disallow pointer-to-function
+  auto p = parser(test::Assemble(R"(
+  %void = OpTypeVoid
+  %42 = OpTypeFunction %void
+  %3 = OpTypePointer Input %42
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule()) << p->error();
+
+  auto* type = p->ConvertType(3);
+  EXPECT_EQ(type, nullptr);
+  EXPECT_THAT(p->error(),
+              Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42"));
+}
+
+TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidStorageClass) {
+  // Disallow invalid storage class
+  auto p = parser(test::Assemble(R"(
+  %1 = OpTypeFloat 32
+  %3 = OpTypePointer !999 %1   ; Special syntax to inject 999 as the storage class
+  )"));
+  // TODO(dneto): I can't get it past module building.
+  EXPECT_FALSE(p->BuildInternalModule()) << p->error();
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerInput) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Input %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerOutput) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Output %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kOutput);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerUniform) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Uniform %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniform);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerWorkgroup) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Workgroup %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kWorkgroup);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerUniformConstant) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer UniformConstant %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniformConstant);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerStorageBuffer) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer StorageBuffer %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kStorageBuffer);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerImage) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Image %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kImage);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerPushConstant) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer PushConstant %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kPushConstant);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerPrivate) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Private %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kPrivate);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerFunction) {
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %3 = OpTypePointer Function %float
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_TRUE(type->IsPointer());
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_TRUE(ptr_ty->type()->IsF32());
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kFunction);
+  EXPECT_TRUE(p->error().empty());
+}
+
+TEST_F(SpvParserTest, ConvertType_PointerToPointer) {
+  // FYI:  The reader suports pointer-to-pointer even while WebGPU does not.
+  auto p = parser(test::Assemble(R"(
+  %float = OpTypeFloat 32
+  %42 = OpTypePointer Output %float
+  %3 = OpTypePointer Input %42
+  )"));
+  EXPECT_TRUE(p->BuildInternalModule());
+
+  auto* type = p->ConvertType(3);
+  EXPECT_NE(type, nullptr);
+  EXPECT_TRUE(type->IsPointer());
+
+  auto* ptr_ty = type->AsPointer();
+  EXPECT_NE(ptr_ty, nullptr);
+  EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
+  EXPECT_TRUE(ptr_ty->type()->IsPointer());
+
+  auto* ptr_ptr_ty = ptr_ty->type()->AsPointer();
+  EXPECT_NE(ptr_ptr_ty, nullptr);
+  EXPECT_EQ(ptr_ptr_ty->storage_class(), ast::StorageClass::kOutput);
+  EXPECT_TRUE(ptr_ptr_ty->type()->IsF32());
+
+  EXPECT_TRUE(p->error().empty());
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader