[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