[ir][spirv-writer] Rework unit testing Add a `Generate()` method to the base test helper that runs the SPIR-V generator and validates the result with spirv-val. The full disassembled module is then stored to `output_`. Rewrite the `access` tests to use this method, instead of manually invoking the PIMPL methods. Attach friendly names to variables and results to allow the test to only check for a specific instruction. Bug: tint:1906 Change-Id: I679ecbff46598d50777f5663d36c6c1235a3f5ff Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/139480 Reviewed-by: dan sinclair <dsinclair@google.com> Commit-Queue: James Price <jrprice@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc index c4f7557..1fd5db9 100644 --- a/src/tint/writer/spirv/ir/generator_impl_ir.cc +++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -636,6 +636,13 @@ TINT_ICE(Writer, diagnostics_) << "unimplemented instruction: " << inst->TypeInfo().name; }); + + // Set the name for the SPIR-V result ID if provided in the module. + if (inst->Result() && !inst->Is<ir::Var>()) { + if (auto name = ir_->NameOf(inst)) { + module_.PushDebug(spv::Op::OpName, {Value(inst), Operand(name.Name())}); + } + } } if (block->IsEmpty()) {
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_access_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_access_test.cc index a6ed2f1..5b0d3f6 100644 --- a/src/tint/writer/spirv/ir/generator_impl_ir_access_test.cc +++ b/src/tint/writer/spirv/ir/generator_impl_ir_access_test.cc
@@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "gmock/gmock.h" #include "src/tint/writer/spirv/ir/test_helper_ir.h" namespace tint::writer::spirv { @@ -20,454 +21,229 @@ using namespace tint::builtin::fluent_types; // NOLINT using namespace tint::number_suffixes; // NOLINT -using SpvGeneratorImplTest_Access = SpvGeneratorImplTest; - -TEST_F(SpvGeneratorImplTest_Access, Array_Value_ConstantIndex) { - auto* arr_val = b.FunctionParam(ty.array(ty.i32(), 4)); +TEST_F(SpvGeneratorImplTest, Access_Array_Value_ConstantIndex) { + auto* arr_val = b.FunctionParam("arr", ty.array(ty.i32(), 4)); auto* func = b.Function("foo", ty.void_()); func->SetParams({arr_val}); - b.With(func->Block(), [&] { - b.Access(ty.i32(), arr_val, 1_u); + auto* result = b.Access(ty.i32(), arr_val, 1_u); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpDecorate %3 ArrayStride 4 -%2 = OpTypeVoid -%4 = OpTypeInt 32 1 -%6 = OpTypeInt 32 0 -%5 = OpConstant %6 4 -%3 = OpTypeArray %4 %5 -%8 = OpTypeFunction %2 %3 -%1 = OpFunction %2 None %8 -%7 = OpFunctionParameter %3 -%9 = OpLabel -%10 = OpCompositeExtract %4 %7 1 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result = OpCompositeExtract %int %arr 1)")); } -TEST_F(SpvGeneratorImplTest_Access, Array_Pointer_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Array_Pointer_ConstantIndex) { auto* func = b.Function("foo", ty.void_()); - b.With(func->Block(), [&] { - auto* arr_var = b.Var(ty.ptr<function, array<i32, 4>>()); - b.Access(ty.ptr<function, i32>(), arr_var, 1_u); + auto* arr_var = b.Var("arr", ty.ptr<function, array<i32, 4>>()); + auto* result = b.Access(ty.ptr<function, i32>(), arr_var, 1_u); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpDecorate %7 ArrayStride 4 -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%8 = OpTypeInt 32 1 -%10 = OpTypeInt 32 0 -%9 = OpConstant %10 4 -%7 = OpTypeArray %8 %9 -%6 = OpTypePointer Function %7 -%12 = OpTypePointer Function %8 -%13 = OpConstant %10 1 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%11 = OpAccessChain %12 %5 %13 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result = OpAccessChain %_ptr_Function_int %arr %uint_1)")); } -TEST_F(SpvGeneratorImplTest_Access, Array_Pointer_DynamicIndex) { +TEST_F(SpvGeneratorImplTest, Access_Array_Pointer_DynamicIndex) { + auto* idx = b.FunctionParam("idx", ty.i32()); auto* func = b.Function("foo", ty.void_()); - + func->SetParams({idx}); b.With(func->Block(), [&] { - auto* idx_var = b.Var(ty.ptr<function, i32>()); - auto* idx = b.Load(idx_var); - auto* arr_var = b.Var(ty.ptr<function, array<i32, 4>>()); - b.Access(ty.ptr<function, i32>(), arr_var, idx); + auto* arr_var = b.Var("arr", ty.ptr<function, array<i32, 4>>()); + auto* result = b.Access(ty.ptr<function, i32>(), arr_var, idx); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpDecorate %11 ArrayStride 4 -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%7 = OpTypeInt 32 1 -%6 = OpTypePointer Function %7 -%13 = OpTypeInt 32 0 -%12 = OpConstant %13 4 -%11 = OpTypeArray %7 %12 -%10 = OpTypePointer Function %11 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%9 = OpVariable %10 Function -%8 = OpLoad %7 %5 -%14 = OpAccessChain %6 %9 %8 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result = OpAccessChain %_ptr_Function_int %arr %idx)")); } -TEST_F(SpvGeneratorImplTest_Access, Matrix_Value_ConstantIndex) { - auto* mat_val = b.FunctionParam(ty.mat2x2(ty.f32())); +TEST_F(SpvGeneratorImplTest, Access_Matrix_Value_ConstantIndex) { + auto* mat_val = b.FunctionParam("mat", ty.mat2x2(ty.f32())); auto* func = b.Function("foo", ty.void_()); func->SetParams({mat_val}); - b.With(func->Block(), [&] { - b.Access(ty.vec2(ty.f32()), mat_val, 1_u); - b.Access(ty.f32(), mat_val, 1_u, 0_u); + auto* result_vector = b.Access(ty.vec2(ty.f32()), mat_val, 1_u); + auto* result_scalar = b.Access(ty.f32(), mat_val, 1_u, 0_u); b.Return(func); + mod.SetName(result_vector, "result_vector"); + mod.SetName(result_scalar, "result_scalar"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%5 = OpTypeFloat 32 -%4 = OpTypeVector %5 2 -%3 = OpTypeMatrix %4 2 -%7 = OpTypeFunction %2 %3 -%1 = OpFunction %2 None %7 -%6 = OpFunctionParameter %3 -%8 = OpLabel -%9 = OpCompositeExtract %4 %6 1 -%10 = OpCompositeExtract %5 %6 1 0 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result_vector = OpCompositeExtract %v2float %mat 1)")); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result_scalar = OpCompositeExtract %float %mat 1 0)")); } -TEST_F(SpvGeneratorImplTest_Access, Matrix_Pointer_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Matrix_Pointer_ConstantIndex) { auto* func = b.Function("foo", ty.void_()); - b.With(func->Block(), [&] { - auto* mat_var = b.Var(ty.ptr<function, mat2x2<f32>>()); - b.Access(ty.ptr<function, vec2<f32>>(), mat_var, 1_u); - b.Access(ty.ptr<function, f32>(), mat_var, 1_u, 0_u); + auto* mat_var = b.Var("mat", ty.ptr<function, mat2x2<f32>>()); + auto* result_vector = b.Access(ty.ptr<function, vec2<f32>>(), mat_var, 1_u); + auto* result_scalar = b.Access(ty.ptr<function, f32>(), mat_var, 1_u, 0_u); b.Return(func); + mod.SetName(result_vector, "result_vector"); + mod.SetName(result_scalar, "result_scalar"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%9 = OpTypeFloat 32 -%8 = OpTypeVector %9 2 -%7 = OpTypeMatrix %8 2 -%6 = OpTypePointer Function %7 -%11 = OpTypePointer Function %8 -%13 = OpTypeInt 32 0 -%12 = OpConstant %13 1 -%15 = OpTypePointer Function %9 -%16 = OpConstant %13 0 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%10 = OpAccessChain %11 %5 %12 -%14 = OpAccessChain %15 %5 %12 %16 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr( + R"(%result_vector = OpAccessChain %_ptr_Function_v2float %mat %uint_1)")); + EXPECT_THAT(output_, + testing::HasSubstr( + R"(%result_scalar = OpAccessChain %_ptr_Function_float %mat %uint_1 %uint_0)")); } -TEST_F(SpvGeneratorImplTest_Access, Matrix_Pointer_DynamicIndex) { +TEST_F(SpvGeneratorImplTest, Access_Matrix_Pointer_DynamicIndex) { + auto* idx = b.FunctionParam("idx", ty.i32()); auto* func = b.Function("foo", ty.void_()); - + func->SetParams({idx}); b.With(func->Block(), [&] { - auto* idx_var = b.Var(ty.ptr<function, i32>()); - auto* idx = b.Load(idx_var); - auto* mat_var = b.Var(ty.ptr<function, mat2x2<f32>>()); - b.Access(ty.ptr<function, vec2<f32>>(), mat_var, idx); - b.Access(ty.ptr<function, f32>(), mat_var, idx, idx); + auto* mat_var = b.Var("mat", ty.ptr<function, mat2x2<f32>>()); + auto* result_vector = b.Access(ty.ptr<function, vec2<f32>>(), mat_var, idx); + auto* result_scalar = b.Access(ty.ptr<function, f32>(), mat_var, idx, idx); b.Return(func); + mod.SetName(result_vector, "result_vector"); + mod.SetName(result_scalar, "result_scalar"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%7 = OpTypeInt 32 1 -%6 = OpTypePointer Function %7 -%13 = OpTypeFloat 32 -%12 = OpTypeVector %13 2 -%11 = OpTypeMatrix %12 2 -%10 = OpTypePointer Function %11 -%15 = OpTypePointer Function %12 -%17 = OpTypePointer Function %13 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%9 = OpVariable %10 Function -%8 = OpLoad %7 %5 -%14 = OpAccessChain %15 %9 %8 -%16 = OpAccessChain %17 %9 %8 %8 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr( + R"(%result_vector = OpAccessChain %_ptr_Function_v2float %mat %idx)")); + EXPECT_THAT(output_, + testing::HasSubstr( + R"(%result_scalar = OpAccessChain %_ptr_Function_float %mat %idx %idx)")); } -TEST_F(SpvGeneratorImplTest_Access, Vector_Value_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Vector_Value_ConstantIndex) { + auto* vec_val = b.FunctionParam("vec", ty.vec4(ty.i32())); auto* func = b.Function("foo", ty.void_()); - auto* vec_val = b.FunctionParam(ty.vec4(ty.i32())); func->SetParams({vec_val}); - b.With(func->Block(), [&] { - b.Access(ty.i32(), vec_val, 1_u); + auto* result = b.Access(ty.i32(), vec_val, 1_u); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%4 = OpTypeInt 32 1 -%3 = OpTypeVector %4 4 -%6 = OpTypeFunction %2 %3 -%1 = OpFunction %2 None %6 -%5 = OpFunctionParameter %3 -%7 = OpLabel -%8 = OpCompositeExtract %4 %5 1 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result = OpCompositeExtract %int %vec 1)")); } -TEST_F(SpvGeneratorImplTest_Access, Vector_Value_DynamicIndex) { +TEST_F(SpvGeneratorImplTest, Access_Vector_Value_DynamicIndex) { + auto* vec_val = b.FunctionParam("vec", ty.vec4(ty.i32())); + auto* idx = b.FunctionParam("idx", ty.i32()); auto* func = b.Function("foo", ty.void_()); - auto* vec_val = b.FunctionParam(ty.vec4(ty.i32())); - func->SetParams({vec_val}); - + func->SetParams({vec_val, idx}); b.With(func->Block(), [&] { - auto* idx_var = b.Var(ty.ptr<function, i32>()); - auto* idx = b.Load(idx_var); - b.Access(ty.i32(), vec_val, idx); + auto* result = b.Access(ty.i32(), vec_val, idx); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%4 = OpTypeInt 32 1 -%3 = OpTypeVector %4 4 -%6 = OpTypeFunction %2 %3 -%9 = OpTypePointer Function %4 -%1 = OpFunction %2 None %6 -%5 = OpFunctionParameter %3 -%7 = OpLabel -%8 = OpVariable %9 Function -%10 = OpLoad %4 %8 -%11 = OpVectorExtractDynamic %4 %5 %10 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result = OpVectorExtractDynamic %int %vec %idx)")); } -TEST_F(SpvGeneratorImplTest_Access, Vector_Pointer_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Vector_Pointer_ConstantIndex) { auto* func = b.Function("foo", ty.void_()); - b.With(func->Block(), [&] { - auto* vec_var = b.Var(ty.ptr<function, vec4<i32>>()); - b.Access(ty.ptr<function, i32>(), vec_var, 1_u); + auto* vec_var = b.Var("vec", ty.ptr<function, vec4<i32>>()); + auto* result = b.Access(ty.ptr<function, i32>(), vec_var, 1_u); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%8 = OpTypeInt 32 1 -%7 = OpTypeVector %8 4 -%6 = OpTypePointer Function %7 -%10 = OpTypePointer Function %8 -%12 = OpTypeInt 32 0 -%11 = OpConstant %12 1 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%9 = OpAccessChain %10 %5 %11 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result = OpAccessChain %_ptr_Function_int %vec %uint_1)")); } -TEST_F(SpvGeneratorImplTest_Access, Vector_Pointer_DynamicIndex) { +TEST_F(SpvGeneratorImplTest, Access_Vector_Pointer_DynamicIndex) { + auto* idx = b.FunctionParam("idx", ty.i32()); auto* func = b.Function("foo", ty.void_()); - + func->SetParams({idx}); b.With(func->Block(), [&] { - auto* idx_var = b.Var(ty.ptr<function, i32>()); - auto* idx = b.Load(idx_var); - auto* vec_var = b.Var(ty.ptr<function, vec4<i32>>()); - b.Access(ty.ptr<function, i32>(), vec_var, idx); + auto* vec_var = b.Var("vec", ty.ptr<function, vec4<i32>>()); + auto* result = b.Access(ty.ptr<function, i32>(), vec_var, idx); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%7 = OpTypeInt 32 1 -%6 = OpTypePointer Function %7 -%11 = OpTypeVector %7 4 -%10 = OpTypePointer Function %11 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%9 = OpVariable %10 Function -%8 = OpLoad %7 %5 -%12 = OpAccessChain %6 %9 %8 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, + testing::HasSubstr(R"(%result = OpAccessChain %_ptr_Function_int %vec %idx)")); } -TEST_F(SpvGeneratorImplTest_Access, NestedVector_Value_DynamicIndex) { - auto* val = b.FunctionParam(ty.array(ty.array(ty.vec4(ty.i32()), 4), 4)); +TEST_F(SpvGeneratorImplTest, Access_NestedVector_Value_DynamicIndex) { + auto* val = b.FunctionParam("arr", ty.array(ty.array(ty.vec4(ty.i32()), 4), 4)); + auto* idx = b.FunctionParam("idx", ty.i32()); auto* func = b.Function("foo", ty.void_()); - func->SetParams({val}); - + func->SetParams({val, idx}); b.With(func->Block(), [&] { - auto* idx_var = b.Var(ty.ptr<function, i32>()); - auto* idx = b.Load(idx_var); - b.Access(ty.i32(), val, 1_u, 2_u, idx); + auto* result = b.Access(ty.i32(), val, 1_u, 2_u, idx); b.Return(func); + mod.SetName(result, "result"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpDecorate %4 ArrayStride 16 -OpDecorate %3 ArrayStride 64 -%2 = OpTypeVoid -%6 = OpTypeInt 32 1 -%5 = OpTypeVector %6 4 -%8 = OpTypeInt 32 0 -%7 = OpConstant %8 4 -%4 = OpTypeArray %5 %7 -%3 = OpTypeArray %4 %7 -%10 = OpTypeFunction %2 %3 -%13 = OpTypePointer Function %6 -%1 = OpFunction %2 None %10 -%9 = OpFunctionParameter %3 -%11 = OpLabel -%12 = OpVariable %13 Function -%14 = OpLoad %6 %12 -%16 = OpCompositeExtract %5 %9 1 2 -%15 = OpVectorExtractDynamic %6 %16 %14 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr(R"(%14 = OpCompositeExtract %v4int %arr 1 2)")); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result = OpVectorExtractDynamic %int %14 %idx)")); } -TEST_F(SpvGeneratorImplTest_Access, Struct_Value_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Struct_Value_ConstantIndex) { auto* str = ty.Struct(mod.symbols.New("MyStruct"), { {mod.symbols.Register("a"), ty.f32()}, {mod.symbols.Register("b"), ty.vec4<i32>()}, }); - auto* str_val = b.FunctionParam(str); + auto* str_val = b.FunctionParam("str", str); auto* func = b.Function("foo", ty.void_()); func->SetParams({str_val}); - b.With(func->Block(), [&] { - b.Access(ty.f32(), str_val, 0_u); - b.Access(ty.i32(), str_val, 1_u, 2_u); + auto* result_a = b.Access(ty.f32(), str_val, 0_u); + auto* result_b = b.Access(ty.i32(), str_val, 1_u, 2_u); b.Return(func); + mod.SetName(result_a, "result_a"); + mod.SetName(result_b, "result_b"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpMemberName %3 0 "a" -OpMemberName %3 1 "b" -OpName %3 "MyStruct" -OpMemberDecorate %3 0 Offset 0 -OpMemberDecorate %3 1 Offset 16 -%2 = OpTypeVoid -%4 = OpTypeFloat 32 -%6 = OpTypeInt 32 1 -%5 = OpTypeVector %6 4 -%3 = OpTypeStruct %4 %5 -%8 = OpTypeFunction %2 %3 -%1 = OpFunction %2 None %8 -%7 = OpFunctionParameter %3 -%9 = OpLabel -%10 = OpCompositeExtract %4 %7 0 -%11 = OpCompositeExtract %6 %7 1 2 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result_a = OpCompositeExtract %float %str 0)")); + EXPECT_THAT(output_, testing::HasSubstr(R"(%result_b = OpCompositeExtract %int %str 1 2)")); } -TEST_F(SpvGeneratorImplTest_Access, Struct_Pointer_ConstantIndex) { +TEST_F(SpvGeneratorImplTest, Access_Struct_Pointer_ConstantIndex) { auto* str = ty.Struct(mod.symbols.New("MyStruct"), { {mod.symbols.Register("a"), ty.f32()}, {mod.symbols.Register("b"), ty.vec4<i32>()}, }); auto* func = b.Function("foo", ty.void_()); - b.With(func->Block(), [&] { - auto* str_var = b.Var(ty.ptr(function, str, read_write)); - b.Access(ty.ptr<function, f32>(), str_var, 0_u); - b.Access(ty.ptr<function, i32>(), str_var, 1_u, 2_u); + auto* str_var = b.Var("str", ty.ptr(function, str, read_write)); + auto* result_a = b.Access(ty.ptr<function, f32>(), str_var, 0_u); + auto* result_b = b.Access(ty.ptr<function, i32>(), str_var, 1_u, 2_u); b.Return(func); + mod.SetName(result_a, "result_a"); + mod.SetName(result_b, "result_b"); }); - ASSERT_TRUE(IRIsValid()) << Error(); - - generator_.EmitFunction(func); - EXPECT_EQ(DumpModule(generator_.Module()), R"(OpName %1 "foo" -OpMemberName %7 0 "a" -OpMemberName %7 1 "b" -OpName %7 "MyStruct" -OpMemberDecorate %7 0 Offset 0 -OpMemberDecorate %7 1 Offset 16 -%2 = OpTypeVoid -%3 = OpTypeFunction %2 -%8 = OpTypeFloat 32 -%10 = OpTypeInt 32 1 -%9 = OpTypeVector %10 4 -%7 = OpTypeStruct %8 %9 -%6 = OpTypePointer Function %7 -%12 = OpTypePointer Function %8 -%14 = OpTypeInt 32 0 -%13 = OpConstant %14 0 -%16 = OpTypePointer Function %10 -%17 = OpConstant %14 1 -%18 = OpConstant %14 2 -%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpVariable %6 Function -%11 = OpAccessChain %12 %5 %13 -%15 = OpAccessChain %16 %5 %17 %18 -OpReturn -OpFunctionEnd -)"); + ASSERT_TRUE(Generate()) << Error(); + EXPECT_THAT(output_, testing::HasSubstr( + R"(%result_a = OpAccessChain %_ptr_Function_float %str %uint_0)")); + EXPECT_THAT( + output_, + testing::HasSubstr(R"(%result_b = OpAccessChain %_ptr_Function_int %str %uint_1 %uint_2)")); } } // namespace
diff --git a/src/tint/writer/spirv/ir/test_helper_ir.h b/src/tint/writer/spirv/ir/test_helper_ir.h index 08f8246..debe5d8 100644 --- a/src/tint/writer/spirv/ir/test_helper_ir.h +++ b/src/tint/writer/spirv/ir/test_helper_ir.h
@@ -16,8 +16,10 @@ #define SRC_TINT_WRITER_SPIRV_IR_TEST_HELPER_IR_H_ #include <string> +#include <utility> #include "gtest/gtest.h" +#include "spirv-tools/libspirv.hpp" #include "src/tint/ir/builder.h" #include "src/tint/ir/validate.h" #include "src/tint/writer/spirv/ir/generator_impl_ir.h" @@ -54,6 +56,9 @@ /// Validation errors std::string err_; + /// SPIR-V output. + std::string output_; + /// @returns the error string from the validation std::string Error() const { return err_; } @@ -67,6 +72,57 @@ return true; } + /// Run the generator on the IR module and validate the result. + /// @returns true if generation and validation succeeded + bool Generate() { + if (!generator_.Generate()) { + err_ = generator_.Diagnostics().str(); + return false; + } + if (!Validate()) { + return false; + } + + output_ = Disassemble(generator_.Result(), SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return true; + } + + /// Validate the generated SPIR-V using the SPIR-V Tools Validator. + /// @returns true if validation succeeded, false otherwise + bool Validate() { + auto binary = generator_.Result(); + + std::string spv_errors; + auto msg_consumer = [&spv_errors](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + spv_errors += + "error: line " + std::to_string(position.index) + ": " + message + "\n"; + break; + case SPV_MSG_WARNING: + spv_errors += + "warning: line " + std::to_string(position.index) + ": " + message + "\n"; + break; + case SPV_MSG_INFO: + spv_errors += + "info: line " + std::to_string(position.index) + ": " + message + "\n"; + break; + case SPV_MSG_DEBUG: + break; + } + }; + + spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2); + tools.SetMessageConsumer(msg_consumer); + + auto result = tools.Validate(binary); + err_ = std::move(spv_errors); + return result; + } + /// @returns the disassembled types from the generated module. std::string DumpTypes() { return DumpInstructions(generator_.Module().Types()); }
diff --git a/src/tint/writer/spirv/spv_dump.cc b/src/tint/writer/spirv/spv_dump.cc index da24446..6ded6ff 100644 --- a/src/tint/writer/spirv/spv_dump.cc +++ b/src/tint/writer/spirv/spv_dump.cc
@@ -19,7 +19,7 @@ namespace tint::writer::spirv { -std::string Disassemble(const std::vector<uint32_t>& data) { +std::string Disassemble(const std::vector<uint32_t>& data, uint32_t options /* = 0u */) { std::string spv_errors; spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; @@ -49,7 +49,7 @@ tools.SetMessageConsumer(msg_consumer); std::string result; - if (!tools.Disassemble(data, &result, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) { + if (!tools.Disassemble(data, &result, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | options)) { return "*** Invalid SPIR-V ***\n" + spv_errors; } return result;
diff --git a/src/tint/writer/spirv/spv_dump.h b/src/tint/writer/spirv/spv_dump.h index 359f0cb..b9030a4 100644 --- a/src/tint/writer/spirv/spv_dump.h +++ b/src/tint/writer/spirv/spv_dump.h
@@ -24,8 +24,9 @@ /// Disassembles SPIR-V binary data into its textual form. /// @param data the SPIR-V binary data +/// @param options the additional SPIR-V disassembler options to use /// @returns the disassembled SPIR-V string -std::string Disassemble(const std::vector<uint32_t>& data); +std::string Disassemble(const std::vector<uint32_t>& data, uint32_t options = 0u); /// Dumps the given builder to a SPIR-V disassembly string /// @param builder the builder to convert