diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 535c50299b..d1c2cfcfa4 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -3135,6 +3135,10 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) { // Synthesize a vector insertion in its own statements. return MakeVectorInsertDynamic(inst); + case SpvOpCompositeInsert: + // Synthesize a composite insertion in its own statements. + return MakeCompositeInsert(inst); + case SpvOpFunctionCall: return EmitFunctionCall(inst); @@ -3307,7 +3311,6 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue( // OpGenericCastToPtrExplicit // Not in Vulkan // // OpArrayLength - // OpCompositeInsert return {}; } @@ -3577,35 +3580,68 @@ TypedExpression FunctionEmitter::MakeCompositeExtract( // This is structurally similar to creating an access chain, but // the SPIR-V instruction has literal indices instead of IDs for indices. - // A SPIR-V composite extract is a single instruction with multiple - // literal indices walking down into composites. The Tint AST represents - // this as ever-deeper nested indexing expressions. Start off with an - // expression for the composite, and then bury that inside nested indexing - // expressions. - auto source = GetSourceForInst(inst); - TypedExpression current_expr(MakeOperand(inst, 0)); + auto composite_index = 0; + auto first_index_position = 1; + TypedExpression current_expr(MakeOperand(inst, composite_index)); + const auto composite_id = inst.GetSingleWordInOperand(composite_index); + auto current_type_id = def_use_mgr_->GetDef(composite_id)->type_id(); - auto make_index = [this, source](uint32_t literal) { + return MakeCompositeValueDecomposition(inst, current_expr, current_type_id, + first_index_position); +} + +TypedExpression FunctionEmitter::MakeCompositeValueDecomposition( + const spvtools::opt::Instruction& inst, + TypedExpression composite, + uint32_t composite_type_id, + int index_start) { + // This is structurally similar to creating an access chain, but + // the SPIR-V instruction has literal indices instead of IDs for indices. + + // A SPIR-V composite extract is a single instruction with multiple + // literal indices walking down into composites. + // A SPIR-V composite insert is similar but also tells you what component + // to inject. This function is respnosible for the the walking-into part + // of composite-insert. + // + // The Tint AST represents this as ever-deeper nested indexing expressions. + // Start off with an expression for the composite, and then bury that inside + // nested indexing expressions. + + auto current_expr = composite; + auto current_type_id = composite_type_id; + + auto make_index = [this](uint32_t literal) { return create( - source, create(source, u32_, literal)); + Source{}, create(Source{}, u32_, literal)); }; - const auto composite = inst.GetSingleWordInOperand(0); - auto current_type_id = def_use_mgr_->GetDef(composite)->type_id(); - // Build up a nested expression for the access chain by walking down the type + // Build up a nested expression for the decomposition by walking down the type // hierarchy, maintaining |current_type_id| as the SPIR-V ID of the type of // the object pointed to after processing the previous indices. const auto num_in_operands = inst.NumInOperands(); - for (uint32_t index = 1; index < num_in_operands; ++index) { + for (uint32_t index = index_start; index < num_in_operands; ++index) { const uint32_t index_val = inst.GetSingleWordInOperand(index); const auto* current_type_inst = def_use_mgr_->GetDef(current_type_id); if (!current_type_inst) { Fail() << "composite type %" << current_type_id - << " is invalid after following " << (index - 1) + << " is invalid after following " << (index - index_start) << " indices: " << inst.PrettyPrint(); return {}; } + const char* operation_name = nullptr; + switch (inst.opcode()) { + case SpvOpCompositeExtract: + operation_name = "OpCompositeExtract"; + break; + case SpvOpCompositeInsert: + operation_name = "OpCompositeInsert"; + break; + default: + Fail() << "internal error: unhandled " << inst.PrettyPrint(); + return {}; + } ast::Expression* next_expr = nullptr; switch (current_type_inst->opcode()) { case SpvOpTypeVector: { @@ -3613,8 +3649,9 @@ TypedExpression FunctionEmitter::MakeCompositeExtract( // like "foo.z", which is more idiomatic than "foo[2]". const auto num_elems = current_type_inst->GetSingleWordInOperand(1); if (num_elems <= index_val) { - Fail() << "CompositeExtract %" << inst.result_id() << " index value " - << index_val << " is out of bounds for vector of " << num_elems + Fail() << operation_name << " %" << inst.result_id() + << " index value " << index_val + << " is out of bounds for vector of " << num_elems << " elements"; return {}; } @@ -3632,8 +3669,9 @@ TypedExpression FunctionEmitter::MakeCompositeExtract( // Check bounds const auto num_elems = current_type_inst->GetSingleWordInOperand(1); if (num_elems <= index_val) { - Fail() << "CompositeExtract %" << inst.result_id() << " index value " - << index_val << " is out of bounds for matrix of " << num_elems + Fail() << operation_name << " %" << inst.result_id() + << " index value " << index_val + << " is out of bounds for matrix of " << num_elems << " elements"; return {}; } @@ -3657,14 +3695,16 @@ TypedExpression FunctionEmitter::MakeCompositeExtract( current_type_id = current_type_inst->GetSingleWordInOperand(0); break; case SpvOpTypeRuntimeArray: - Fail() << "can't do OpCompositeExtract on a runtime array"; + Fail() << "can't do " << operation_name + << " on a runtime array: " << inst.PrettyPrint(); return {}; case SpvOpTypeStruct: { const auto num_members = current_type_inst->NumInOperands(); if (num_members <= index_val) { - Fail() << "CompositeExtract %" << inst.result_id() << " index value " - << index_val << " is out of bounds for structure %" - << current_type_id << " having " << num_members << " members"; + Fail() << operation_name << " %" << inst.result_id() + << " index value " << index_val + << " is out of bounds for structure %" << current_type_id + << " having " << num_members << " members"; return {}; } auto name = namer_.GetMemberName(current_type_id, uint32_t(index_val)); @@ -3677,8 +3717,8 @@ TypedExpression FunctionEmitter::MakeCompositeExtract( break; } default: - Fail() << "CompositeExtract with bad type %" << current_type_id << ": " - << current_type_inst->PrettyPrint(); + Fail() << operation_name << " with bad type %" << current_type_id + << ": " << current_type_inst->PrettyPrint(); return {}; } current_expr = @@ -4909,6 +4949,59 @@ bool FunctionEmitter::MakeVectorInsertDynamic( {ast_type, create(registered_temp_name)}); } +bool FunctionEmitter::MakeCompositeInsert( + const spvtools::opt::Instruction& inst) { + // For + // %result = OpCompositeInsert %type %object %composite 1 2 3 ... + // generate statements like this: + // + // var temp : type = composite; + // temp[index].x = object; + // const result : type = temp; + // + // Then use result everywhere the original SPIR-V id is used. Using a const + // like this avoids constantly reloading the value many times. + // + // This technique is a combination of: + // - making a temporary variable and constant declaration, like what we do + // for VectorInsertDynamic, and + // - building up an access-chain like access like for CompositeExtract, but + // on the left-hand side of the assignment. + + auto* ast_type = parser_impl_.ConvertType(inst.type_id()); + auto component = MakeOperand(inst, 0); + auto src_composite = MakeOperand(inst, 1); + + // Synthesize the temporary variable. + // It doesn't correspond to a SPIR-V ID, so we don't use the ordinary + // API in parser_impl_. + auto result_name = namer_.Name(inst.result_id()); + auto temp_name = namer_.MakeDerivedName(result_name); + auto registered_temp_name = builder_.Symbols().Register(temp_name); + + auto* temp_var = create( + Source{}, registered_temp_name, ast::StorageClass::kFunction, ast_type, + false, src_composite.expr, ast::VariableDecorationList{}); + AddStatement(create(Source{}, temp_var)); + + TypedExpression seed_expr{ast_type, create( + Source{}, registered_temp_name)}; + + // The left-hand side of the assignment *looks* like a decomposition. + TypedExpression lhs = + MakeCompositeValueDecomposition(inst, seed_expr, inst.type_id(), 2); + if (!lhs.expr) { + return false; + } + + AddStatement( + create(Source{}, lhs.expr, component.expr)); + + return EmitConstDefinition( + inst, + {ast_type, create(registered_temp_name)}); +} + FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default; FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default; diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h index b562968df9..5ac476b70e 100644 --- a/src/reader/spirv/function.h +++ b/src/reader/spirv/function.h @@ -729,6 +729,21 @@ class FunctionEmitter { /// @returns an AST expression for the instruction, or nullptr. TypedExpression MakeCompositeExtract(const spvtools::opt::Instruction& inst); + /// Creates an expression for indexing into a composite value. The literal + /// indices that step into the value start at instruction input operand + /// `start_index` and run to the end of the instruction. + /// @param inst the original instruction + /// @param composite the typed expression for the composite + /// @param composite_type_id the SPIR-V type ID for the composite + /// @param index_start the index of the first operand in `inst` that is an + /// index into the composite type + /// @returns an AST expression for the decomposed composite, or {} on error + TypedExpression MakeCompositeValueDecomposition( + const spvtools::opt::Instruction& inst, + TypedExpression composite, + uint32_t composite_type_id, + int index_start); + /// Creates an expression for OpVectorShuffle /// @param inst an OpVectorShuffle instruction. /// @returns an AST expression for the instruction, or nullptr. @@ -911,6 +926,12 @@ class FunctionEmitter { /// @returns an expression bool MakeVectorInsertDynamic(const spvtools::opt::Instruction& inst); + /// Generates statements for a SPIR-V OpComposite instruction. + /// Registers a const declaration for the result. + /// @param inst the SPIR-V instruction + /// @returns an expression + bool MakeCompositeInsert(const spvtools::opt::Instruction& inst); + /// Get the SPIR-V instruction for the image memory object declaration for /// the image operand to the given instruction. /// @param inst the SPIR-V instruction diff --git a/src/reader/spirv/function_composite_test.cc b/src/reader/spirv/function_composite_test.cc index adff130cce..1517c8abc1 100644 --- a/src/reader/spirv/function_composite_test.cc +++ b/src/reader/spirv/function_composite_test.cc @@ -287,7 +287,7 @@ TEST_F(SpvParserTest_CompositeExtract, Vector_IndexTooBigError) { ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); EXPECT_FALSE(fe.EmitBody()); - EXPECT_THAT(p->error(), Eq("CompositeExtract %1 index value 900 is out of " + EXPECT_THAT(p->error(), Eq("OpCompositeExtract %1 index value 900 is out of " "bounds for vector of 2 elements")); } @@ -338,7 +338,7 @@ TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) { ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); EXPECT_FALSE(fe.EmitBody()) << p->error(); - EXPECT_THAT(p->error(), Eq("CompositeExtract %2 index value 3 is out of " + EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 3 is out of " "bounds for matrix of 3 elements")); } @@ -424,7 +424,8 @@ TEST_F(SpvParserTest_CompositeExtract, RuntimeArray_IsError) { ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); EXPECT_FALSE(fe.EmitBody()) << p->error(); - EXPECT_THAT(p->error(), Eq("can't do OpCompositeExtract on a runtime array")); + EXPECT_THAT(p->error(), + HasSubstr("can't do OpCompositeExtract on a runtime array: ")); } TEST_F(SpvParserTest_CompositeExtract, Struct) { @@ -530,7 +531,7 @@ TEST_F(SpvParserTest_CompositeExtract, Struct_IndexTooBigError) { ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); EXPECT_FALSE(fe.EmitBody()); - EXPECT_THAT(p->error(), Eq("CompositeExtract %2 index value 40 is out of " + EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 40 is out of " "bounds for structure %26 having 3 members")); } @@ -576,6 +577,465 @@ TEST_F(SpvParserTest_CompositeExtract, Struct_Array_Matrix_Vector) { << ToString(p->builder(), fe.ast_body()); } +using SpvParserTest_CompositeInsert = SpvParserTest; + +TEST_F(SpvParserTest_CompositeInsert, Vector) { + const auto assembly = Preamble() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_1_1 + function + __vec_2__f32 + { + TypeConstructor[not set]{ + __vec_2__f32 + ScalarConstructor[not set]{50.000000} + ScalarConstructor[not set]{60.000000} + } + } + } +} +Assignment{ + MemberAccessor[not set]{ + Identifier[not set]{x_1_1} + Identifier[not set]{y} + } + ScalarConstructor[not set]{70.000000} +} +VariableDeclStatement{ + VariableConst{ + x_1 + none + __vec_2__f32 + { + Identifier[not set]{x_1_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, Vector_IndexTooBigError) { + const auto assembly = Preamble() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 900 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_FALSE(fe.EmitBody()); + EXPECT_THAT(p->error(), Eq("OpCompositeInsert %1 index value 900 is out of " + "bounds for vector of 2 elements")); +} + +TEST_F(SpvParserTest_CompositeInsert, Matrix) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %m3v2float + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %m3v2float %var + %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __mat_2_3__f32 + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + ArrayAccessor[not set]{ + Identifier[not set]{x_2_1} + ScalarConstructor[not set]{2} + } + TypeConstructor[not set]{ + __vec_2__f32 + ScalarConstructor[not set]{50.000000} + ScalarConstructor[not set]{60.000000} + } +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __mat_2_3__f32 + { + Identifier[not set]{x_2_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, Matrix_IndexTooBigError) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %m3v2float + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %m3v2float %var + %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 3 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_FALSE(fe.EmitBody()) << p->error(); + EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 3 is out of " + "bounds for matrix of 3 elements")); +} + +TEST_F(SpvParserTest_CompositeInsert, Matrix_Vector) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %m3v2float + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %m3v2float %var + %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __mat_2_3__f32 + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + ArrayAccessor[not set]{ + Identifier[not set]{x_2_1} + ScalarConstructor[not set]{2} + } + TypeConstructor[not set]{ + __vec_2__f32 + ScalarConstructor[not set]{50.000000} + ScalarConstructor[not set]{60.000000} + } +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __mat_2_3__f32 + { + Identifier[not set]{x_2_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, Array) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %a_u_5 + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %a_u_5 %var + %2 = OpCompositeInsert %a_u_5 %uint_20 %1 3 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __array__u32_5 + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + ArrayAccessor[not set]{ + Identifier[not set]{x_2_1} + ScalarConstructor[not set]{3} + } + ScalarConstructor[not set]{20} +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __array__u32_5 + { + Identifier[not set]{x_2_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, RuntimeArray_IsError) { + const auto assembly = Preamble() + R"( + %rtarr = OpTypeRuntimeArray %uint + %ptr = OpTypePointer Function %rtarr + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %rtarr %var + %2 = OpCompositeInsert %rtarr %uint_20 %1 3 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_FALSE(fe.EmitBody()) << p->error(); + EXPECT_THAT(p->error(), + HasSubstr("can't do OpCompositeInsert on a runtime array: ")); +} + +TEST_F(SpvParserTest_CompositeInsert, Struct) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %s_v2f_u_i + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %s_v2f_u_i %var + %2 = OpCompositeInsert %s_v2f_u_i %int_30 %1 2 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __struct_S + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + MemberAccessor[not set]{ + Identifier[not set]{x_2_1} + Identifier[not set]{field2} + } + ScalarConstructor[not set]{30} +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __struct_S + { + Identifier[not set]{x_2_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, Struct_DifferOnlyInMemberName) { + const auto assembly = + R"( + OpMemberName %s0 0 "algo" + OpMemberName %s1 0 "rithm" +)" + Preamble() + + R"( + %s0 = OpTypeStruct %uint + %s1 = OpTypeStruct %uint + %ptr0 = OpTypePointer Function %s0 + %ptr1 = OpTypePointer Function %s1 + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var0 = OpVariable %ptr0 Function + %var1 = OpVariable %ptr1 Function + %1 = OpLoad %s0 %var0 + %2 = OpCompositeInsert %s0 %uint_10 %1 0 + %3 = OpLoad %s1 %var1 + %4 = OpCompositeInsert %s1 %uint_10 %3 0 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __struct_S_1 + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + MemberAccessor[not set]{ + Identifier[not set]{x_2_1} + Identifier[not set]{algo} + } + ScalarConstructor[not set]{10} +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __struct_S_1 + { + Identifier[not set]{x_2_1} + } + } +} +)")) << body_str; + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_4_1 + function + __struct_S_2 + { + Identifier[not set]{x_3} + } + } +} +Assignment{ + MemberAccessor[not set]{ + Identifier[not set]{x_4_1} + Identifier[not set]{rithm} + } + ScalarConstructor[not set]{10} +} +VariableDeclStatement{ + VariableConst{ + x_4 + none + __struct_S_2 + { + Identifier[not set]{x_4_1} + } + } +})")) << body_str; +} + +TEST_F(SpvParserTest_CompositeInsert, Struct_IndexTooBigError) { + const auto assembly = Preamble() + R"( + %ptr = OpTypePointer Function %s_v2f_u_i + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %s_v2f_u_i %var + %2 = OpCompositeInsert %s_v2f_u_i %uint_10 %1 40 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_FALSE(fe.EmitBody()); + EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 40 is out of " + "bounds for structure %26 having 3 members")); +} + +TEST_F(SpvParserTest_CompositeInsert, Struct_Array_Matrix_Vector) { + const auto assembly = Preamble() + R"( + %a_mat = OpTypeArray %m3v2float %uint_3 + %s = OpTypeStruct %uint %a_mat + %ptr = OpTypePointer Function %s + + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %var = OpVariable %ptr Function + %1 = OpLoad %s %var + %2 = OpCompositeInsert %s %float_70 %1 1 2 0 1 + OpReturn + OpFunctionEnd + )"; + auto p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body_str = ToString(p->builder(), fe.ast_body()); + EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{ + Variable{ + x_2_1 + function + __struct_S_1 + { + Identifier[not set]{x_1} + } + } +} +Assignment{ + MemberAccessor[not set]{ + ArrayAccessor[not set]{ + ArrayAccessor[not set]{ + MemberAccessor[not set]{ + Identifier[not set]{x_2_1} + Identifier[not set]{field1} + } + ScalarConstructor[not set]{2} + } + ScalarConstructor[not set]{0} + } + Identifier[not set]{y} + } + ScalarConstructor[not set]{70.000000} +} +VariableDeclStatement{ + VariableConst{ + x_2 + none + __struct_S_1 + { + Identifier[not set]{x_2_1} + } + } +})")) << body_str; +} + using SpvParserTest_CopyObject = SpvParserTest; TEST_F(SpvParserTest_CopyObject, Scalar) {