[spir-reader] Emit alias types (named types)
Bug: tint:3
Change-Id: I4882160d9fe533d956f29ca15e65bf99eb80f5df
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/18360
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2c45704..ba455ac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -327,6 +327,7 @@
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 571bd57..2480a34 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 @@
// 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() = 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 @@
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 @@
}
bool ParserImpl::BuildInternalModule() {
+ if (!success_) {
+ return false;
+ }
tools_.SetMessageConsumer(message_consumer_);
const spv_context& context = tools_context_.CContext();
@@ -243,7 +255,7 @@
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 @@
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 @@
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 @@
// Now make the struct.
auto ast_struct = std::make_unique<ast::Struct>(ast_struct_decoration,
std::move(ast_members));
+ // The struct type will be assigned a name during EmitAliasTypes.
auto ast_struct_type =
std::make_unique<ast::type::StructType>(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<ast::type::AliasType>(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 8a7b974..a38ddaf 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -126,33 +126,54 @@
std::unique_ptr<ast::StructMemberDecoration> 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 7dd24df..f5daed0 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_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_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_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_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_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_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_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_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_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_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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 @@
%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 ef5a5f1..113a7a7 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 @@
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 @@
%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 @@
%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 0000000..cebfa10
--- /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 <cstdint>
+#include <string>
+#include <vector>
+
+#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