spirv-reader: support OpArrayLength
Fixed: tint:431
Change-Id: I727ca8200118e0b93b42c5f7d9e97bc4afd97830
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/36460
Commit-Queue: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 0e88128..b72b55a 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3143,6 +3143,10 @@
return MakeSimpleSelect(inst);
}
+ if (opcode == SpvOpArrayLength) {
+ return MakeArrayLength(inst);
+ }
+
// builtin readonly function
// glsl.std.450 readonly function
@@ -4490,6 +4494,45 @@
return ToI32(value);
}
+TypedExpression FunctionEmitter::MakeArrayLength(
+ const spvtools::opt::Instruction& inst) {
+ if (inst.NumInOperands() != 2) {
+ // Binary parsing will fail on this anyway.
+ Fail() << "invalid array length: requires 2 operands: "
+ << inst.PrettyPrint();
+ return {};
+ }
+ const auto struct_ptr_id = inst.GetSingleWordInOperand(0);
+ const auto field_index = inst.GetSingleWordInOperand(1);
+ const auto struct_ptr_type_id =
+ def_use_mgr_->GetDef(struct_ptr_id)->type_id();
+ // Trace through the pointer type to get to the struct type.
+ const auto struct_type_id =
+ def_use_mgr_->GetDef(struct_ptr_type_id)->GetSingleWordInOperand(1);
+ const auto field_name = namer_.GetMemberName(struct_type_id, field_index);
+ if (field_name.empty()) {
+ Fail() << "struct index out of bounds for array length: "
+ << inst.PrettyPrint();
+ }
+
+ auto* member_ident = create<ast::IdentifierExpression>(
+ Source{}, ast_module_.RegisterSymbol(field_name), field_name);
+ auto* member_access = create<ast::MemberAccessorExpression>(
+ Source{}, MakeExpression(struct_ptr_id).expr, member_ident);
+
+ // Generate the intrinsic function call.
+ std::string call_ident_str = "arrayLength";
+ auto* call_ident = create<ast::IdentifierExpression>(
+ Source{}, ast_module_.RegisterSymbol(call_ident_str), call_ident_str);
+ call_ident->set_intrinsic(ast::Intrinsic::kArrayLength);
+
+ ast::ExpressionList params{member_access};
+ auto* call_expr =
+ create<ast::CallExpression>(Source{}, call_ident, std::move(params));
+
+ return {parser_impl_.ConvertType(inst.type_id()), call_expr};
+}
+
FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default;
FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default;
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index c82fa91..c27cde5 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -834,6 +834,11 @@
/// @returns an expression
TypedExpression MakeIntrinsicCall(const spvtools::opt::Instruction& inst);
+ /// Returns an expression for a SPIR-V OpArrayLength instruction.
+ /// @param inst the SPIR-V instruction
+ /// @returns an expression
+ TypedExpression MakeArrayLength(const spvtools::opt::Instruction& inst);
+
/// Emits a texture builtin function call for a SPIR-V instruction that
/// accesses an image or sampled image.
/// @param inst the SPIR-V instruction
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index 733e4db..37dede2 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -1011,6 +1011,69 @@
// TODO(dneto): Blocked on OpFunctionCall support.
}
+std::string RuntimeArrayPreamble() {
+ return R"(
+ OpName %myvar "myvar"
+ OpMemberName %struct 0 "first"
+ OpMemberName %struct 1 "rtarr"
+
+ OpDecorate %struct Block
+ OpMemberDecorate %struct 0 Offset 0
+ OpMemberDecorate %struct 1 Offset 4
+ OpDecorate %arr ArrayStride 4
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+
+ %uint_0 = OpConstant %uint 0
+ %uint_1 = OpConstant %uint 1
+
+ %arr = OpTypeRuntimeArray %uint
+ %struct = OpTypeStruct %uint %arr
+ %ptr_struct = OpTypePointer StorageBuffer %struct
+ %ptr_uint = OpTypePointer StorageBuffer %uint
+
+ %myvar = OpVariable %ptr_struct StorageBuffer
+ )";
+}
+
+TEST_F(SpvParserTest, ArrayLength) {
+ const auto assembly = RuntimeArrayPreamble() + R"(
+
+ %100 = OpFunction %void None %voidfn
+
+ %entry = OpLabel
+ %1 = OpArrayLength %uint %myvar 1
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+ FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ const auto body_str = ToString(p->get_module(), fe.ast_body());
+ EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
+ VariableConst{
+ x_1
+ none
+ __u32
+ {
+ Call[not set]{
+ Identifier[not set]{arrayLength}
+ (
+ MemberAccessor[not set]{
+ Identifier[not set]{myvar}
+ Identifier[not set]{rtarr}
+ }
+ )
+ }
+ }
+ }
+}
+)")) << body_str;
+}
+
} // namespace
} // namespace spirv
} // namespace reader
diff --git a/src/reader/spirv/namer.h b/src/reader/spirv/namer.h
index ff36925..715e3d0 100644
--- a/src/reader/spirv/namer.h
+++ b/src/reader/spirv/namer.h
@@ -78,7 +78,7 @@
/// Gets the registered name for a struct member. If no name has
/// been registered for this member, then returns the empty string.
/// member index is in bounds.
- /// @param id the SPIR-V ID of the struct
+ /// @param id the SPIR-V ID of the struct type
/// @param member_index the index of the member, counting from 0
/// @returns the registered name for the ID, or an empty string if
/// nothing has been registered.