[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:
parent
a8cd18e9e7
commit
288674904d
|
@ -15,6 +15,7 @@
|
||||||
#include "src/reader/spirv/parser_impl.h"
|
#include "src/reader/spirv/parser_impl.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include "source/opt/module.h"
|
#include "source/opt/module.h"
|
||||||
#include "source/opt/type_manager.h"
|
#include "source/opt/type_manager.h"
|
||||||
#include "spirv-tools/libspirv.hpp"
|
#include "spirv-tools/libspirv.hpp"
|
||||||
|
#include "src/ast/type/array_type.h"
|
||||||
#include "src/ast/type/bool_type.h"
|
#include "src/ast/type/bool_type.h"
|
||||||
#include "src/ast/type/f32_type.h"
|
#include "src/ast/type/f32_type.h"
|
||||||
#include "src/ast/type/i32_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)
|
ParserImpl::ParserImpl(Context* ctx, const std::vector<uint32_t>& spv_binary)
|
||||||
: Reader(ctx),
|
: Reader(ctx),
|
||||||
|
ctx_(ctx),
|
||||||
spv_binary_(spv_binary),
|
spv_binary_(spv_binary),
|
||||||
fail_stream_(&success_, &errors_),
|
fail_stream_(&success_, &errors_),
|
||||||
namer_(fail_stream_),
|
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.
|
// In the error case, we'll already have emitted a diagnostic.
|
||||||
break;
|
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:
|
default:
|
||||||
// The error diagnostic will be generated below because result is still
|
// The error diagnostic will be generated below because result is still
|
||||||
// nullptr.
|
// nullptr.
|
||||||
|
|
|
@ -122,6 +122,8 @@ class ParserImpl : Reader {
|
||||||
/// Emit entry point AST nodes.
|
/// Emit entry point AST nodes.
|
||||||
bool EmitEntryPoints();
|
bool EmitEntryPoints();
|
||||||
|
|
||||||
|
// The Tint context
|
||||||
|
Context ctx_;
|
||||||
// The SPIR-V binary we're parsing
|
// The SPIR-V binary we're parsing
|
||||||
std::vector<uint32_t> spv_binary_;
|
std::vector<uint32_t> spv_binary_;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
#include "src/ast/type/array_type.h"
|
||||||
#include "src/ast/type/matrix_type.h"
|
#include "src/ast/type/matrix_type.h"
|
||||||
#include "src/ast/type/vector_type.h"
|
#include "src/ast/type/vector_type.h"
|
||||||
#include "src/reader/spirv/parser_impl.h"
|
#include "src/reader/spirv/parser_impl.h"
|
||||||
|
@ -324,6 +325,96 @@ TEST_F(SpvParserTest, ConvertType_MatrixOverF32) {
|
||||||
EXPECT_TRUE(p->error().empty());
|
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
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
|
@ -42,8 +42,8 @@ class SpvParserTest : public testing::Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the parser from the helper
|
/// Retrieves the parser from the helper
|
||||||
/// @param input the string to parse
|
/// @param input the SPIR-V binary to parse
|
||||||
/// @returns the parser implementation
|
/// @returns a parser for the given binary
|
||||||
ParserImpl* parser(const std::vector<uint32_t>& input) {
|
ParserImpl* parser(const std::vector<uint32_t>& input) {
|
||||||
impl_ = std::make_unique<ParserImpl>(&ctx_, input);
|
impl_ = std::make_unique<ParserImpl>(&ctx_, input);
|
||||||
return impl_.get();
|
return impl_.get();
|
||||||
|
|
Loading…
Reference in New Issue