[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 <dsinclair@google.com>
This commit is contained in:
David Neto 2020-03-27 00:49:03 +00:00 committed by dan sinclair
parent a8cd18e9e7
commit 288674904d
4 changed files with 148 additions and 2 deletions

View File

@ -15,6 +15,7 @@
#include "src/reader/spirv/parser_impl.h"
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <utility>
@ -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<uint32_t>& 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::type::ArrayType>(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<uint32_t>::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::type::ArrayType>(
ast_elem_ty, static_cast<uint32_t>(num_elem)));
break;
}
default:
// The error diagnostic will be generated below because result is still
// nullptr.

View File

@ -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<uint32_t> spv_binary_;

View File

@ -17,6 +17,7 @@
#include <vector>
#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

View File

@ -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<uint32_t>& input) {
impl_ = std::make_unique<ParserImpl>(&ctx_, input);
return impl_.get();