Add reader::spirv::ParserImpl::ConvertType

For now, it only handles scalar types

Bug: tint:3
Change-Id: Ic20e18a4f80790e6cd10d4c06dd2abfd8f67a304
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17700
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 72eb55e..6a4ed6e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -319,6 +319,7 @@
     reader/spirv/enum_converter_test.cc
     reader/spirv/fail_stream_test.cc
     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_import_test.cc
     reader/spirv/parser_impl_user_name_test.cc
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 429ceba..d09f239 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -15,13 +15,22 @@
 #include "src/reader/spirv/parser_impl.h"
 
 #include <cstring>
+#include <memory>
 #include <string>
 #include <utility>
 
 #include "source/opt/build_module.h"
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
+#include "source/opt/type_manager.h"
 #include "spirv-tools/libspirv.hpp"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/type/void_type.h"
+#include "src/type_manager.h"
 
 namespace tint {
 namespace reader {
@@ -92,6 +101,77 @@
   return std::move(ast_module_);
 }
 
+const ast::type::Type* ParserImpl::ConvertType(uint32_t type_id) {
+  if (!success_) {
+    return nullptr;
+  }
+
+  if (type_mgr_ == nullptr) {
+    Fail() << "ConvertType called when the internal module has not been built.";
+    return nullptr;
+  }
+
+  auto where = id_to_type_.find(type_id);
+  if (where != id_to_type_.end()) {
+    return where->second;
+  }
+
+  auto* spirv_type = type_mgr_->GetType(type_id);
+  if (spirv_type == nullptr) {
+    Fail() << "ID is not a SPIR-V type: " << type_id;
+    return nullptr;
+  }
+
+  const ast::type::Type* result = nullptr;
+  TypeManager* tint_tm = TypeManager::Instance();
+
+  switch (spirv_type->kind()) {
+    case spvtools::opt::analysis::Type::kVoid:
+      result = tint_tm->Get(std::make_unique<ast::type::VoidType>());
+      break;
+    case spvtools::opt::analysis::Type::kBool:
+      result = tint_tm->Get(std::make_unique<ast::type::BoolType>());
+      break;
+    case spvtools::opt::analysis::Type::kInteger: {
+      const auto* int_ty = spirv_type->AsInteger();
+      if (int_ty->width() == 32) {
+        if (int_ty->IsSigned()) {
+          result = tint_tm->Get(std::make_unique<ast::type::I32Type>());
+        } else {
+          result = tint_tm->Get(std::make_unique<ast::type::U32Type>());
+        }
+      } else {
+        Fail() << "unhandled integer width: " << int_ty->width();
+      }
+      break;
+    }
+    case spvtools::opt::analysis::Type::kFloat: {
+      const auto* float_ty = spirv_type->AsFloat();
+      if (float_ty->width() == 32) {
+        result = tint_tm->Get(std::make_unique<ast::type::F32Type>());
+      } else {
+        Fail() << "unhandled float width: " << float_ty->width();
+      }
+      break;
+    }
+    default:
+      // The error diagnostic will be generated below because result is still
+      // nullptr.
+      break;
+  }
+
+  if (result == nullptr) {
+    if (success_) {
+      // Only emit a new diagnostic if we haven't already emitted a more
+      // specific one.
+      Fail() << "unknown SPIR-V type: " << type_id;
+    }
+  } else {
+    id_to_type_[type_id] = result;
+  }
+  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 67f1d25..b6f91ce 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -31,6 +31,7 @@
 #include "spirv-tools/libspirv.hpp"
 #include "src/ast/import.h"
 #include "src/ast/module.h"
+#include "src/ast/type/type.h"
 #include "src/reader/reader.h"
 #include "src/reader/spirv/enum_converter.h"
 #include "src/reader/spirv/fail_stream.h"
@@ -82,6 +83,14 @@
     return glsl_std_450_imports_;
   }
 
+  /// Converts a SPIR-V type to a Tint type.
+  /// On failure, logs an error and returns null.
+  /// This should only be called after the internal
+  /// representation of the module has been built.
+  /// @param type_id the SPIR-V ID of a type.
+  /// @returns a Tint type, or nullptr
+  const ast::type::Type* ConvertType(uint32_t type_id);
+
   /// @returns the namer object
   Namer& namer() { return namer_; }
 
@@ -147,6 +156,9 @@
   // The set of IDs that are imports of the GLSL.std.450 extended instruction
   // sets.
   std::unordered_set<uint32_t> glsl_std_450_imports_;
+
+  // Maps a SPIR-V type ID to a Tint type.
+  std::unordered_map<uint32_t, const ast::type::Type*> id_to_type_;
 };
 
 }  // namespace spirv
diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc
new file mode 100644
index 0000000..44acf9a
--- /dev/null
+++ b/src/reader/spirv/parser_impl_convert_type_test.cc
@@ -0,0 +1,136 @@
+// 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 "src/reader/spirv/parser_impl.h"
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "src/reader/spirv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spirv {
+namespace {
+
+using ::testing::Eq;
+
+using SpvParserTest_ConvertType = ::testing::Test;
+
+TEST_F(SpvParserTest_ConvertType, PreservesExistingFailure) {
+  ParserImpl p(std::vector<uint32_t>{});
+  p.Fail() << "boing";
+  const auto* type = p.ConvertType(10);
+  EXPECT_EQ(type, nullptr);
+  EXPECT_THAT(p.error(), Eq("boing"));
+}
+
+TEST_F(SpvParserTest_ConvertType, NotAnId) {
+  ParserImpl p(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
+  EXPECT_TRUE(p.BuildAndParseInternalModule()) << p.error();
+
+  const auto* type = p.ConvertType(10);
+  EXPECT_EQ(type, nullptr);
+  EXPECT_EQ(nullptr, type);
+  EXPECT_THAT(p.error(), Eq("ID is not a SPIR-V type: 10"));
+}
+
+TEST_F(SpvParserTest_ConvertType, IdExistsButIsNotAType) {
+  ParserImpl p(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(1);
+  EXPECT_EQ(nullptr, type);
+  EXPECT_THAT(p.error(), Eq("ID is not a SPIR-V type: 1"));
+}
+
+TEST_F(SpvParserTest_ConvertType, UnhandledType) {
+  // Pipes are an OpenCL type. Tint doesn't support them.
+  ParserImpl p(test::Assemble("%70 = OpTypePipe WriteOnly"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(70);
+  EXPECT_EQ(nullptr, type);
+  EXPECT_THAT(p.error(), Eq("unknown SPIR-V type: 70"));
+}
+
+TEST_F(SpvParserTest_ConvertType, Void) {
+  ParserImpl p(test::Assemble("%1 = OpTypeVoid"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(1);
+  EXPECT_TRUE(type->IsVoid());
+  EXPECT_TRUE(p.error().empty());
+}
+
+TEST_F(SpvParserTest_ConvertType, Bool) {
+  ParserImpl p(test::Assemble("%100 = OpTypeBool"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(100);
+  EXPECT_TRUE(type->IsBool());
+  EXPECT_TRUE(p.error().empty());
+}
+
+TEST_F(SpvParserTest_ConvertType, I32) {
+  ParserImpl p(test::Assemble("%2 = OpTypeInt 32 1"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(2);
+  EXPECT_TRUE(type->IsI32());
+  EXPECT_TRUE(p.error().empty());
+}
+
+TEST_F(SpvParserTest_ConvertType, U32) {
+  ParserImpl p(test::Assemble("%3 = OpTypeInt 32 0"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(3);
+  EXPECT_TRUE(type->IsU32());
+  EXPECT_TRUE(p.error().empty());
+}
+
+TEST_F(SpvParserTest_ConvertType, F32) {
+  ParserImpl p(test::Assemble("%4 = OpTypeFloat 32"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(4);
+  EXPECT_TRUE(type->IsF32());
+  EXPECT_TRUE(p.error().empty());
+}
+
+TEST_F(SpvParserTest_ConvertType, BadIntWidth) {
+  ParserImpl p(test::Assemble("%5 = OpTypeInt 17 1"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(5);
+  EXPECT_EQ(type, nullptr);
+  EXPECT_THAT(p.error(), Eq("unhandled integer width: 17"));
+}
+
+TEST_F(SpvParserTest_ConvertType, BadFloatWidth) {
+  ParserImpl p(test::Assemble("%6 = OpTypeFloat 19"));
+  EXPECT_TRUE(p.BuildAndParseInternalModule());
+
+  const auto* type = p.ConvertType(6);
+  EXPECT_EQ(type, nullptr);
+  EXPECT_THAT(p.error(), Eq("unhandled float width: 19"));
+}
+
+}  // namespace
+}  // namespace spirv
+}  // namespace reader
+}  // namespace tint