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