From 288674904d095934de84f7f850303e030cdfce33 Mon Sep 17 00:00:00 2001 From: David Neto Date: Fri, 27 Mar 2020 00:49:03 +0000 Subject: [PATCH] [reader-spirv] Convert array, runtime array types Bug: tint:3 Change-Id: If0d7d38cc777bce3d86dfd83669c1572331d4ed6 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17800 Reviewed-by: dan sinclair --- src/reader/spirv/parser_impl.cc | 53 +++++++++++ src/reader/spirv/parser_impl.h | 2 + .../spirv/parser_impl_convert_type_test.cc | 91 +++++++++++++++++++ src/reader/spirv/parser_impl_test_helper.h | 4 +- 4 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index 1f6b6d49a9..f0fe50fad1 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -15,6 +15,7 @@ #include "src/reader/spirv/parser_impl.h" #include +#include #include #include #include @@ -24,6 +25,7 @@ #include "source/opt/module.h" #include "source/opt/type_manager.h" #include "spirv-tools/libspirv.hpp" +#include "src/ast/type/array_type.h" #include "src/ast/type/bool_type.h" #include "src/ast/type/f32_type.h" #include "src/ast/type/i32_type.h" @@ -46,6 +48,7 @@ const spv_target_env kTargetEnv = SPV_ENV_WEBGPU_0; ParserImpl::ParserImpl(Context* ctx, const std::vector& spv_binary) : Reader(ctx), + ctx_(ctx), spv_binary_(spv_binary), fail_stream_(&success_, &errors_), namer_(fail_stream_), @@ -180,6 +183,56 @@ ast::type::Type* ParserImpl::ConvertType(uint32_t type_id) { // In the error case, we'll already have emitted a diagnostic. break; } + case spvtools::opt::analysis::Type::kRuntimeArray: { + const auto* rtarr_ty = spirv_type->AsRuntimeArray(); + auto* ast_elem_ty = + ConvertType(type_mgr_->GetId(rtarr_ty->element_type())); + if (ast_elem_ty != nullptr) { + result = ctx_.type_mgr->Get( + std::make_unique(ast_elem_ty)); + } + // In the error case, we'll already have emitted a diagnostic. + break; + } + case spvtools::opt::analysis::Type::kArray: { + const auto* arr_ty = spirv_type->AsArray(); + auto* ast_elem_ty = ConvertType(type_mgr_->GetId(arr_ty->element_type())); + if (ast_elem_ty == nullptr) { + // In the error case, we'll already have emitted a diagnostic. + break; + } + const auto& length_info = arr_ty->length_info(); + if (length_info.words.empty()) { + // The internal representation is invalid. The discriminant vector + // is mal-formed. + Fail() << "internal error: Array length info is invalid"; + return nullptr; + } + if (length_info.words[0] != + spvtools::opt::analysis::Array::LengthInfo::kConstant) { + Fail() << "Array type " << type_id + << " length is a specialization constant"; + return nullptr; + } + const auto* constant = + constant_mgr_->FindDeclaredConstant(length_info.id); + if (constant == nullptr) { + Fail() << "Array type " << type_id << " length ID " << length_info.id + << " does not name an OpConstant"; + return nullptr; + } + const uint64_t num_elem = constant->GetZeroExtendedValue(); + // For now, limit to only 32bits. + if (num_elem > std::numeric_limits::max()) { + Fail() << "Array type " << type_id + << " has too many elements (more than can fit in 32 bits): " + << num_elem; + return nullptr; + } + result = ctx_.type_mgr->Get(std::make_unique( + ast_elem_ty, static_cast(num_elem))); + break; + } default: // The error diagnostic will be generated below because result is still // nullptr. diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index a422cb6f7a..9c4fa58c2a 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -122,6 +122,8 @@ class ParserImpl : Reader { /// Emit entry point AST nodes. bool EmitEntryPoints(); + // The Tint context + Context ctx_; // The SPIR-V binary we're parsing std::vector 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 4a0dfdff79..72c6fc38ff 100644 --- a/src/reader/spirv/parser_impl_convert_type_test.cc +++ b/src/reader/spirv/parser_impl_convert_type_test.cc @@ -17,6 +17,7 @@ #include #include "gmock/gmock.h" +#include "src/ast/type/array_type.h" #include "src/ast/type/matrix_type.h" #include "src/ast/type/vector_type.h" #include "src/reader/spirv/parser_impl.h" @@ -324,6 +325,96 @@ TEST_F(SpvParserTest, ConvertType_MatrixOverF32) { EXPECT_TRUE(p->error().empty()); } +TEST_F(SpvParserTest, ConvertType_RuntimeArray) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %10 = OpTypeRuntimeArray %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + auto* type = p->ConvertType(10); + ASSERT_NE(type, nullptr); + EXPECT_TRUE(type->IsArray()); + auto* arr_type = type->AsArray(); + EXPECT_TRUE(arr_type->IsRuntimeArray()); + ASSERT_NE(arr_type, nullptr); + ASSERT_EQ(arr_type->size(), 0u); + auto* elem_type = arr_type->type(); + ASSERT_NE(elem_type, nullptr); + EXPECT_TRUE(elem_type->IsU32()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, ConvertType_Array) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %uint_42 = OpConstant %uint 42 + %10 = OpTypeArray %uint %uint_42 + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + auto* type = p->ConvertType(10); + ASSERT_NE(type, nullptr); + EXPECT_TRUE(type->IsArray()); + auto* arr_type = type->AsArray(); + EXPECT_FALSE(arr_type->IsRuntimeArray()); + ASSERT_NE(arr_type, nullptr); + ASSERT_EQ(arr_type->size(), 42u); + auto* elem_type = arr_type->type(); + ASSERT_NE(elem_type, nullptr); + EXPECT_TRUE(elem_type->IsU32()); + EXPECT_TRUE(p->error().empty()); +} + +TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantValue) { + auto p = parser(test::Assemble(R"( + OpDecorate %uint_42 SpecId 12 + %uint = OpTypeInt 32 0 + %uint_42 = OpSpecConstant %uint 42 + %10 = OpTypeArray %uint %uint_42 + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + auto* type = p->ConvertType(10); + ASSERT_EQ(type, nullptr); + EXPECT_THAT(p->error(), + Eq("Array type 10 length is a specialization constant")); +} + +TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantExpr) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %uint_42 = OpConstant %uint 42 + %sum = OpSpecConstantOp %uint IAdd %uint_42 %uint_42 + %10 = OpTypeArray %uint %sum + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + auto* type = p->ConvertType(10); + ASSERT_EQ(type, nullptr); + EXPECT_THAT(p->error(), + Eq("Array type 10 length is a specialization constant")); +} + +// TODO(dneto): Maybe add a test where the length operand is not a constant. +// E.g. it's the ID of a type. That won't validate, and the SPIRV-Tools +// optimizer representation doesn't handle it and asserts out instead. + +TEST_F(SpvParserTest, ConvertType_ArrayBadTooBig) { + auto p = parser(test::Assemble(R"( + %uint64 = OpTypeInt 64 0 + %uint64_big = OpConstant %uint64 5000000000 + %10 = OpTypeArray %uint64 %uint64_big + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + + auto* type = p->ConvertType(10); + ASSERT_EQ(type, nullptr); + // TODO(dneto): Right now it's rejected earlier in the flow because + // we can't even utter the uint64 type. + EXPECT_THAT(p->error(), Eq("unhandled integer width: 64")); +} + } // namespace } // namespace spirv } // namespace reader diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h index 97ac096521..8c40a2b9b0 100644 --- a/src/reader/spirv/parser_impl_test_helper.h +++ b/src/reader/spirv/parser_impl_test_helper.h @@ -42,8 +42,8 @@ class SpvParserTest : public testing::Test { } /// Retrieves the parser from the helper - /// @param input the string to parse - /// @returns the parser implementation + /// @param input the SPIR-V binary to parse + /// @returns a parser for the given binary ParserImpl* parser(const std::vector& input) { impl_ = std::make_unique(&ctx_, input); return impl_.get();