diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 0e88128cda..b72b55a952 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -3143,6 +3143,10 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue( return MakeSimpleSelect(inst); } + if (opcode == SpvOpArrayLength) { + return MakeArrayLength(inst); + } + // builtin readonly function // glsl.std.450 readonly function @@ -4490,6 +4494,45 @@ TypedExpression FunctionEmitter::ToSignedIfUnsigned(TypedExpression value) { 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( + Source{}, ast_module_.RegisterSymbol(field_name), field_name); + auto* member_access = create( + Source{}, MakeExpression(struct_ptr_id).expr, member_ident); + + // Generate the intrinsic function call. + std::string call_ident_str = "arrayLength"; + auto* call_ident = create( + 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(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 c82fa91e9e..c27cde5d35 100644 --- a/src/reader/spirv/function.h +++ b/src/reader/spirv/function.h @@ -834,6 +834,11 @@ class FunctionEmitter { /// @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 733e4db688..37dede222b 100644 --- a/src/reader/spirv/function_memory_test.cc +++ b/src/reader/spirv/function_memory_test.cc @@ -1011,6 +1011,69 @@ TEST_F(SpvParserTest, DISABLED_RemapStorageBuffer_ThroughFunctionParameter) { // 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 ff36925fbc..715e3d0163 100644 --- a/src/reader/spirv/namer.h +++ b/src/reader/spirv/namer.h @@ -78,7 +78,7 @@ class Namer { /// 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.