diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c4570463f..ba455ac0d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -327,6 +327,7 @@ if(${TINT_BUILD_SPV_READER}) reader/spirv/parser_impl_entry_point_test.cc reader/spirv/parser_impl_get_decorations_test.cc reader/spirv/parser_impl_import_test.cc + reader/spirv/parser_impl_named_types_test.cc reader/spirv/parser_impl_user_name_test.cc reader/spirv/parser_impl_test.cc reader/spirv/parser_test.cc diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index 571bd57a2a..2480a348e0 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -32,6 +32,7 @@ #include "src/ast/struct_member.h" #include "src/ast/struct_member_decoration.h" #include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/type/alias_type.h" #include "src/ast/type/array_type.h" #include "src/ast/type/bool_type.h" #include "src/ast/type/f32_type.h" @@ -77,7 +78,6 @@ ParserImpl::ParserImpl(Context* ctx, const std::vector& spv_binary) // For binary validation errors, we only have the instruction // number. It's not text, so there is no column number. this->Fail() << "line:" << position.index << ": " << message; - this->Fail() << "error: line " << position.index << ": " << message; } }; } @@ -85,23 +85,27 @@ ParserImpl::ParserImpl(Context* ctx, const std::vector& spv_binary) ParserImpl::~ParserImpl() = default; bool ParserImpl::Parse() { + // Set up use of SPIRV-Tools utilities. + spvtools::SpirvTools spv_tools(kTargetEnv); + + // Error messages from SPIRV-Tools are forwarded as failures, including + // setting |success_| to false. + spv_tools.SetMessageConsumer(message_consumer_); + if (!success_) { return false; } - // Set up use of SPIRV-Tools utilities. - spvtools::SpirvTools spv_tools(kTargetEnv); - - // Error messages from SPIRV-Tools are forwarded as failures. - spv_tools.SetMessageConsumer(message_consumer_); - - // Only consider valid modules. - if (success_) { - success_ = spv_tools.Validate(spv_binary_); + // Only consider valid modules. On failure, the message consumer + // will set the error status. + if (!spv_tools.Validate(spv_binary_)) { + return false; } - - if (success_) { - success_ = BuildInternalModule(); + if (!BuildInternalModule()) { + return false; + } + if (!ParseInternalModule()) { + return false; } return success_; @@ -160,6 +164,11 @@ ast::type::Type* ParserImpl::ConvertType(uint32_t type_id) { return save(ConvertType(spirv_type->AsArray())); case spvtools::opt::analysis::Type::kStruct: return save(ConvertType(spirv_type->AsStruct())); + case spvtools::opt::analysis::Type::kFunction: + case spvtools::opt::analysis::Type::kPointer: + // For now, just return null without erroring out. + // TODO(dneto) + return nullptr; default: break; } @@ -228,6 +237,9 @@ ParserImpl::ConvertMemberDecoration(const Decoration& decoration) { } bool ParserImpl::BuildInternalModule() { + if (!success_) { + return false; + } tools_.SetMessageConsumer(message_consumer_); const spv_context& context = tools_context_.CContext(); @@ -243,7 +255,7 @@ bool ParserImpl::BuildInternalModule() { type_mgr_ = ir_context_->get_type_mgr(); deco_mgr_ = ir_context_->get_decoration_mgr(); - return true; + return success_; } void ParserImpl::ResetInternalModule() { @@ -265,12 +277,18 @@ bool ParserImpl::ParseInternalModule() { if (!RegisterExtendedInstructionImports()) { return false; } - if (!RegisterUserNames()) { + if (!RegisterUserAndStructMemberNames()) { return false; } if (!EmitEntryPoints()) { return false; } + if (!RegisterTypes()) { + return false; + } + if (!EmitAliasTypes()) { + return false; + } // TODO(dneto): fill in the rest return true; } @@ -297,7 +315,10 @@ bool ParserImpl::RegisterExtendedInstructionImports() { return true; } -bool ParserImpl::RegisterUserNames() { +bool ParserImpl::RegisterUserAndStructMemberNames() { + if (!success_) { + return false; + } // Register entry point names. An entry point name is the point of contact // between the API and the shader. It has the highest priority for // preservation, so register it first. @@ -490,14 +511,77 @@ ast::type::Type* ParserImpl::ConvertType( // Now make the struct. auto ast_struct = std::make_unique(ast_struct_decoration, std::move(ast_members)); + // The struct type will be assigned a name during EmitAliasTypes. auto ast_struct_type = std::make_unique(std::move(ast_struct)); - // The struct might not have a name yet. Suggest one. + // Set the struct name before registering it. namer_.SuggestSanitizedName(type_id, "S"); ast_struct_type->set_name(namer_.GetName(type_id)); return ctx_.type_mgr().Get(std::move(ast_struct_type)); } +bool ParserImpl::RegisterTypes() { + if (!success_) { + return false; + } + for (auto& type_or_const : module_->types_values()) { + const auto* type = type_mgr_->GetType(type_or_const.result_id()); + if (type == nullptr) { + continue; + } + ConvertType(type_or_const.result_id()); + } + return success_; +} + +bool ParserImpl::EmitAliasTypes() { + if (!success_) { + return false; + } + // The algorithm here emits type definitions in the order presented in + // the SPIR-V module. This is valid because: + // + // - There are no back-references. OpTypeForwarddPointer is not supported + // by the WebGPU shader programming model. + // - Arrays are always sized by an OpConstant of scalar integral type. + // WGSL currently doesn't have specialization constants. + // crbug.com/32 tracks implementation in case they are added. + for (auto& type_or_const : module_->types_values()) { + const auto type_id = type_or_const.result_id(); + // We only care about struct, arrays, and runtime arrays. + switch (type_or_const.opcode()) { + case SpvOpTypeStruct: + // The struct already got a name when the type was first registered. + break; + case SpvOpTypeRuntimeArray: + // Runtime arrays are always decorated with ArrayStride so always get a + // type alias. + namer_.SuggestSanitizedName(type_id, "RTArr"); + break; + case SpvOpTypeArray: + // Only make a type aliase for arrays with decorations. + if (GetDecorationsFor(type_id).empty()) { + continue; + } + namer_.SuggestSanitizedName(type_id, "Arr"); + break; + default: + // Ignore constants, and any other types. + continue; + } + auto* ast_underlying_type = id_to_type_[type_id]; + if (ast_underlying_type == nullptr) { + Fail() << "internal error: no type registered for SPIR-V ID: " << type_id; + return false; + } + const auto name = namer_.GetName(type_id); + auto* ast_type = ctx_.type_mgr().Get( + std::make_unique(name, ast_underlying_type)); + ast_module_.AddAliasType(ast_type->AsAlias()); + } + return success_; +} + } // namespace spirv } // namespace reader } // namespace tint diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index 8a7b974936..a38ddafe7c 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -126,33 +126,54 @@ class ParserImpl : Reader { std::unique_ptr ConvertMemberDecoration( const Decoration& decoration); - private: /// Builds the internal representation of the SPIR-V module. /// Assumes the module is somewhat well-formed. Normally you /// would want to validate the SPIR-V module before attempting /// to build this internal representation. - /// @returns true if successful. + /// This is a no-op if the parser has already failed. + /// @returns true if the parser is still successful. bool BuildInternalModule(); /// Walks the internal representation of the module to populate /// the AST form of the module. - /// @returns true on success + /// This is a no-op if the parser has already failed. + /// @returns true if the parser is still successful. bool ParseInternalModule(); /// Destroys the internal representation of the SPIR-V module. void ResetInternalModule(); /// Registers extended instruction imports. Only "GLSL.std.450" is supported. + /// This is a no-op if the parser has already failed. + /// @returns true if parser is still successful. bool RegisterExtendedInstructionImports(); /// Registers user names for SPIR-V objects, from OpName, and OpMemberName. /// Also synthesizes struct field names. Ensures uniqueness for names for /// SPIR-V IDs, and uniqueness of names of fields within any single struct. - bool RegisterUserNames(); + /// This is a no-op if the parser has already failed. + /// @returns true if parser is still successful. + bool RegisterUserAndStructMemberNames(); /// Emit entry point AST nodes. + /// This is a no-op if the parser has already failed. + /// @returns true if parser is still successful. bool EmitEntryPoints(); + /// Register Tint AST types for SPIR-V types. + /// This is a no-op if the parser has already failed. + /// @returns true if parser is still successful. + bool RegisterTypes(); + + /// Emit type alias declarations for types requiring user-specified names: + /// - struct types + /// - decorated arrays and runtime arrays + /// TODO(dneto): I expect images and samplers to require names as well. + /// This is a no-op if the parser has already failed. + /// @returns true if parser is still successful. + bool EmitAliasTypes(); + + private: /// Converts a specific SPIR-V type to a Tint type. Integer case ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty); /// Converts a specific SPIR-V type to a Tint type. Float case diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc index 7dd24dffa5..f5daed01e8 100644 --- a/src/reader/spirv/parser_impl_convert_type_test.cc +++ b/src/reader/spirv/parser_impl_convert_type_test.cc @@ -53,7 +53,7 @@ TEST_F(SpvParserTest, ConvertType_RequiresInternalRepresntation) { TEST_F(SpvParserTest, ConvertType_NotAnId) { auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\"")); - EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error(); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); EXPECT_EQ(type, nullptr); @@ -63,7 +63,7 @@ TEST_F(SpvParserTest, ConvertType_NotAnId) { TEST_F(SpvParserTest, ConvertType_IdExistsButIsNotAType) { auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\"")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(1); EXPECT_EQ(nullptr, type); @@ -73,7 +73,7 @@ TEST_F(SpvParserTest, ConvertType_IdExistsButIsNotAType) { TEST_F(SpvParserTest, ConvertType_UnhandledType) { // Pipes are an OpenCL type. Tint doesn't support them. auto p = parser(test::Assemble("%70 = OpTypePipe WriteOnly")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(70); EXPECT_EQ(nullptr, type); @@ -82,7 +82,7 @@ TEST_F(SpvParserTest, ConvertType_UnhandledType) { TEST_F(SpvParserTest, ConvertType_Void) { auto p = parser(test::Assemble("%1 = OpTypeVoid")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(1); EXPECT_TRUE(type->IsVoid()); @@ -91,7 +91,7 @@ TEST_F(SpvParserTest, ConvertType_Void) { TEST_F(SpvParserTest, ConvertType_Bool) { auto p = parser(test::Assemble("%100 = OpTypeBool")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(100); EXPECT_TRUE(type->IsBool()); @@ -100,7 +100,7 @@ TEST_F(SpvParserTest, ConvertType_Bool) { TEST_F(SpvParserTest, ConvertType_I32) { auto p = parser(test::Assemble("%2 = OpTypeInt 32 1")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(2); EXPECT_TRUE(type->IsI32()); @@ -109,7 +109,7 @@ TEST_F(SpvParserTest, ConvertType_I32) { TEST_F(SpvParserTest, ConvertType_U32) { auto p = parser(test::Assemble("%3 = OpTypeInt 32 0")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(3); EXPECT_TRUE(type->IsU32()); @@ -118,7 +118,7 @@ TEST_F(SpvParserTest, ConvertType_U32) { TEST_F(SpvParserTest, ConvertType_F32) { auto p = parser(test::Assemble("%4 = OpTypeFloat 32")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(4); EXPECT_TRUE(type->IsF32()); @@ -127,7 +127,7 @@ TEST_F(SpvParserTest, ConvertType_F32) { TEST_F(SpvParserTest, ConvertType_BadIntWidth) { auto p = parser(test::Assemble("%5 = OpTypeInt 17 1")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(5); EXPECT_EQ(type, nullptr); @@ -136,7 +136,7 @@ TEST_F(SpvParserTest, ConvertType_BadIntWidth) { TEST_F(SpvParserTest, ConvertType_BadFloatWidth) { auto p = parser(test::Assemble("%6 = OpTypeFloat 19")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(6); EXPECT_EQ(type, nullptr); @@ -148,7 +148,7 @@ TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidVectorElement) { %5 = OpTypePipe ReadOnly %20 = OpTypeVector %5 2 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(20); EXPECT_EQ(type, nullptr); @@ -162,7 +162,7 @@ TEST_F(SpvParserTest, ConvertType_VecOverF32) { %30 = OpTypeVector %float 3 %40 = OpTypeVector %float 4 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* v2xf32 = p->ConvertType(20); EXPECT_TRUE(v2xf32->IsVector()); @@ -189,7 +189,7 @@ TEST_F(SpvParserTest, ConvertType_VecOverI32) { %30 = OpTypeVector %int 3 %40 = OpTypeVector %int 4 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* v2xi32 = p->ConvertType(20); EXPECT_TRUE(v2xi32->IsVector()); @@ -216,7 +216,7 @@ TEST_F(SpvParserTest, ConvertType_VecOverU32) { %30 = OpTypeVector %uint 3 %40 = OpTypeVector %uint 4 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* v2xu32 = p->ConvertType(20); EXPECT_TRUE(v2xu32->IsVector()); @@ -242,7 +242,7 @@ TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidMatrixElement) { %10 = OpTypeVector %5 2 %20 = OpTypeMatrix %10 2 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(20); EXPECT_EQ(type, nullptr); @@ -268,7 +268,7 @@ TEST_F(SpvParserTest, ConvertType_MatrixOverF32) { %43 = OpTypeMatrix %v4 3 %44 = OpTypeMatrix %v4 4 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* m22 = p->ConvertType(22); EXPECT_TRUE(m22->IsMatrix()); @@ -332,7 +332,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) { %uint = OpTypeInt 32 0 %10 = OpTypeRuntimeArray %uint )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); ASSERT_NE(type, nullptr); @@ -353,7 +353,7 @@ TEST_F(SpvParserTest, ConvertType_Array) { %uint_42 = OpConstant %uint 42 %10 = OpTypeArray %uint %uint_42 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); ASSERT_NE(type, nullptr); @@ -375,7 +375,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantValue) { %uint_42 = OpSpecConstant %uint 42 %10 = OpTypeArray %uint %uint_42 )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); ASSERT_EQ(type, nullptr); @@ -390,7 +390,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantExpr) { %sum = OpSpecConstantOp %uint IAdd %uint_42 %uint_42 %10 = OpTypeArray %uint %sum )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); ASSERT_EQ(type, nullptr); @@ -408,7 +408,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadTooBig) { %uint64_big = OpConstant %uint64 5000000000 %10 = OpTypeArray %uint64 %uint64_big )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); auto* type = p->ConvertType(10); ASSERT_EQ(type, nullptr); @@ -423,10 +423,11 @@ TEST_F(SpvParserTest, ConvertType_StructTwoMembers) { %float = OpTypeFloat 32 %10 = OpTypeStruct %uint %float )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); + EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); auto* type = p->ConvertType(10); - ASSERT_NE(type, nullptr) << p->error(); + ASSERT_NE(type, nullptr); EXPECT_TRUE(type->IsStruct()); std::stringstream ss; type->AsStruct()->impl()->to_str(ss, 0); @@ -443,7 +444,8 @@ TEST_F(SpvParserTest, ConvertType_StructWithBlockDecoration) { %uint = OpTypeInt 32 0 %10 = OpTypeStruct %uint )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); + EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); auto* type = p->ConvertType(10); ASSERT_NE(type, nullptr); @@ -466,10 +468,11 @@ TEST_F(SpvParserTest, ConvertType_StructWithMemberDecorations) { %mat = OpTypeMatrix %vec 2 %10 = OpTypeStruct %float %vec %mat )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildInternalModule()); + EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); auto* type = p->ConvertType(10); - ASSERT_NE(type, nullptr) << p->error(); + ASSERT_NE(type, nullptr); EXPECT_TRUE(type->IsStruct()); std::stringstream ss; type->AsStruct()->impl()->to_str(ss, 0); diff --git a/src/reader/spirv/parser_impl_get_decorations_test.cc b/src/reader/spirv/parser_impl_get_decorations_test.cc index ef5a5f15dd..113a7a79f3 100644 --- a/src/reader/spirv/parser_impl_get_decorations_test.cc +++ b/src/reader/spirv/parser_impl_get_decorations_test.cc @@ -97,7 +97,8 @@ TEST_F(SpvParserTest, GetDecorationsForMember_MemberWithoutDecoration) { EXPECT_TRUE(p->error().empty()); } -TEST_F(SpvParserTest, GetDecorationsForMember_OneDecoration) { +// TODO(dneto): Enable when ArrayStride is handled +TEST_F(SpvParserTest, DISABLED_GetDecorationsForMember_OneDecoration) { auto p = parser(test::Assemble(R"( OpMemberDecorate %10 1 ArrayStride 12 %uint = OpTypeInt 32 0 @@ -105,14 +106,17 @@ TEST_F(SpvParserTest, GetDecorationsForMember_OneDecoration) { %arr = OpTypeArray %uint %uint_2 %10 = OpTypeStruct %uint %arr )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error(); auto decorations = p->GetDecorationsForMember(10, 1); EXPECT_THAT(decorations, UnorderedElementsAre(Decoration{SpvDecorationArrayStride, 12})); EXPECT_TRUE(p->error().empty()); } -TEST_F(SpvParserTest, GetDecorationsForMember_MultiDecoration) { +// TODO(dneto): Enable when ArrayStride, MatrixStride, ColMajor are handled +// crbug.com/tint/30 for ArrayStride +// crbug.com/tint/31 for matrix layout +TEST_F(SpvParserTest, DISABLED_GetDecorationsForMember_MultiDecoration) { auto p = parser(test::Assemble(R"( OpMemberDecorate %50 1 RelaxedPrecision OpMemberDecorate %50 2 ArrayStride 16 @@ -126,7 +130,7 @@ TEST_F(SpvParserTest, GetDecorationsForMember_MultiDecoration) { %arr = OpTypeArray %mat %uint_2 %50 = OpTypeStruct %uint %float %arr )")); - EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error(); EXPECT_TRUE(p->GetDecorationsForMember(50, 0).empty()); EXPECT_THAT(p->GetDecorationsForMember(50, 1), diff --git a/src/reader/spirv/parser_impl_named_types_test.cc b/src/reader/spirv/parser_impl_named_types_test.cc new file mode 100644 index 0000000000..cebfa1049e --- /dev/null +++ b/src/reader/spirv/parser_impl_named_types_test.cc @@ -0,0 +1,96 @@ +// 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 +#include +#include + +#include "gmock/gmock.h" +#include "src/ast/struct.h" +#include "src/ast/type/array_type.h" +#include "src/ast/type/matrix_type.h" +#include "src/ast/type/struct_type.h" +#include "src/ast/type/vector_type.h" +#include "src/reader/spirv/parser_impl.h" +#include "src/reader/spirv/parser_impl_test_helper.h" +#include "src/reader/spirv/spirv_tools_helpers_test.h" +#include "src/type_manager.h" + +namespace tint { +namespace reader { +namespace spirv { +namespace { + +using ::testing::HasSubstr; + +TEST_F(SpvParserTest, NamedTypes_AnonStruct) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %s = OpTypeStruct %uint %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_THAT(p->module().to_str(), HasSubstr("S -> __struct_")); +} + +TEST_F(SpvParserTest, NamedTypes_NamedStruct) { + auto p = parser(test::Assemble(R"( + OpName %s "mystruct" + %uint = OpTypeInt 32 0 + %s = OpTypeStruct %uint %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_THAT(p->module().to_str(), HasSubstr("mystruct -> __struct_")); +} + +// TODO(dneto): Enable this when array types can have ArrayStride +TEST_F(SpvParserTest, DISABLED_NamedTypes_AnonArrayWithDecoration) { + auto p = parser(test::Assemble(R"( + OpDecorate %arr ArrayStride 16 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %arr = OpTypeArray %uint %uint_3 + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_THAT(p->module().to_str(), HasSubstr("Arr -> __array__u32")); +} + +// TODO(dneto): Should we make an alias for an un-decoratrd array with +// an OpName? + +TEST_F(SpvParserTest, NamedTypes_AnonRTArray) { + auto p = parser(test::Assemble(R"( + %uint = OpTypeInt 32 0 + %arr = OpTypeRuntimeArray %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_THAT(p->module().to_str(), HasSubstr("RTArr -> __array__u32")); +} + +TEST_F(SpvParserTest, NamedTypes_NamedRTArray) { + auto p = parser(test::Assemble(R"( + OpName %arr "myrtarr" + %uint = OpTypeInt 32 0 + %arr = OpTypeRuntimeArray %uint + )")); + EXPECT_TRUE(p->BuildAndParseInternalModule()); + EXPECT_THAT(p->module().to_str(), HasSubstr("myrtarr -> __array__u32")); +} + +// TODO(dneto): Handle arrays sized by a spec constant. +// Blocked by crbug.com/tint/32 + +} // namespace +} // namespace spirv +} // namespace reader +} // namespace tint