[ir][spirv-writer] Emit struct types
Emit offset decorations for each struct member. If a struct member is
a matrix type (or contains one via nested array elements), emit matrix
layout decorations as well.
Bug: tint:1906
Change-Id: I838bb25977435bb9e901c7d9bcdef4c87eac3e27
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/135581
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: James Price <jrprice@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 5b12094..89e6a1f 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -47,6 +47,7 @@
#include "src/tint/type/i32.h"
#include "src/tint/type/matrix.h"
#include "src/tint/type/pointer.h"
+#include "src/tint/type/struct.h"
#include "src/tint/type/type.h"
#include "src/tint/type/u32.h"
#include "src/tint/type/vector.h"
@@ -225,6 +226,7 @@
spv::Op::OpTypePointer,
{id, U32Operand(StorageClass(ptr->AddressSpace())), Type(ptr->StoreType())});
},
+ [&](const type::Struct* str) { EmitStructType(id, str); },
[&](Default) {
TINT_ICE(Writer, diagnostics_) << "unhandled type: " << ty->FriendlyName();
});
@@ -245,6 +247,47 @@
return block_labels_.GetOrCreate(block, [&]() { return module_.NextId(); });
}
+void GeneratorImplIr::EmitStructType(uint32_t id, const type::Struct* str) {
+ // Helper to return `type` or a potentially nested array element type within `type` as a matrix
+ // type, or nullptr if no such matrix type is present.
+ auto get_nested_matrix_type = [&](const type::Type* type) {
+ while (auto* arr = type->As<type::Array>()) {
+ type = arr->ElemType();
+ }
+ return type->As<type::Matrix>();
+ };
+
+ OperandList operands = {id};
+ for (auto* member : str->Members()) {
+ operands.push_back(Type(member->Type()));
+
+ // Generate struct member offset decoration.
+ module_.PushAnnot(
+ spv::Op::OpMemberDecorate,
+ {operands[0], member->Index(), U32Operand(SpvDecorationOffset), member->Offset()});
+
+ // Emit matrix layout decorations if necessary.
+ if (auto* matrix_type = get_nested_matrix_type(member->Type())) {
+ const uint32_t effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
+ module_.PushAnnot(spv::Op::OpMemberDecorate,
+ {id, member->Index(), U32Operand(SpvDecorationColMajor)});
+ module_.PushAnnot(spv::Op::OpMemberDecorate,
+ {id, member->Index(), U32Operand(SpvDecorationMatrixStride),
+ Operand(effective_row_count * matrix_type->type()->Size())});
+ }
+
+ if (member->Name().IsValid()) {
+ module_.PushDebug(spv::Op::OpMemberName,
+ {operands[0], member->Index(), Operand(member->Name().Name())});
+ }
+ }
+ module_.PushType(spv::Op::OpTypeStruct, std::move(operands));
+
+ if (str->Name().IsValid()) {
+ module_.PushDebug(spv::Op::OpName, {operands[0], Operand(str->Name().Name())});
+ }
+}
+
void GeneratorImplIr::EmitFunction(const ir::Function* func) {
auto id = Value(func);
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.h b/src/tint/writer/spirv/ir/generator_impl_ir.h
index 5ce5838..7c3105b 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.h
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.h
@@ -46,6 +46,7 @@
class Var;
} // namespace tint::ir
namespace tint::type {
+class Struct;
class Type;
} // namespace tint::type
@@ -97,6 +98,11 @@
/// @returns the ID of the block's label
uint32_t Label(const ir::Block* block);
+ /// Emit a struct type.
+ /// @param id the result ID to use
+ /// @param str the struct type to emit
+ void EmitStructType(uint32_t id, const type::Struct* str);
+
/// Emit a function.
/// @param func the function to emit
void EmitFunction(const ir::Function* func);
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
index 284d421..d6c654f 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
@@ -185,6 +185,76 @@
EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), "OpDecorate %1 ArrayStride 16\n");
}
+TEST_F(SpvGeneratorImplTest, Type_Struct) {
+ auto* str = mod.Types().Get<type::Struct>(
+ mod.symbols.Register("MyStruct"),
+ utils::Vector{
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("a"), mod.Types().f32(), 0u,
+ 0u, 4u, 4u, type::StructMemberAttributes{}),
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("b"),
+ mod.Types().vec4(mod.Types().i32()), 1u, 16u, 16u,
+ 16u, type::StructMemberAttributes{}),
+ },
+ 16u, 32u, 32u);
+ auto id = generator_.Type(str);
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), R"(%2 = OpTypeFloat 32
+%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 4
+%1 = OpTypeStruct %2 %3
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), R"(OpMemberDecorate %1 0 Offset 0
+OpMemberDecorate %1 1 Offset 16
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Debug()), R"(OpMemberName %1 0 "a"
+OpMemberName %1 1 "b"
+OpName %1 "MyStruct"
+)");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_Struct_MatrixLayout) {
+ auto* str = mod.Types().Get<type::Struct>(
+ mod.symbols.Register("MyStruct"),
+ utils::Vector{
+ mod.Types().Get<type::StructMember>(mod.symbols.Register("m"),
+ mod.Types().mat3x3(mod.Types().f32()), 0u, 0u, 16u,
+ 48u, type::StructMemberAttributes{}),
+ // Matrices nested inside arrays need layout decorations on the struct member too.
+ mod.Types().Get<type::StructMember>(
+ mod.symbols.Register("arr"),
+ mod.Types().array(mod.Types().array(mod.Types().mat2x4(mod.Types().f16()), 4), 4),
+ 1u, 64u, 8u, 64u, type::StructMemberAttributes{}),
+ },
+ 16u, 128u, 128u);
+ auto id = generator_.Type(str);
+ EXPECT_EQ(id, 1u);
+ EXPECT_EQ(DumpTypes(), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypeMatrix %3 3
+%9 = OpTypeFloat 16
+%8 = OpTypeVector %9 4
+%7 = OpTypeMatrix %8 2
+%11 = OpTypeInt 32 0
+%10 = OpConstant %11 4
+%6 = OpTypeArray %7 %10
+%5 = OpTypeArray %6 %10
+%1 = OpTypeStruct %2 %5
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Annots()), R"(OpMemberDecorate %1 0 Offset 0
+OpMemberDecorate %1 0 ColMajor
+OpMemberDecorate %1 0 MatrixStride 16
+OpDecorate %6 ArrayStride 16
+OpDecorate %5 ArrayStride 64
+OpMemberDecorate %1 1 Offset 64
+OpMemberDecorate %1 1 ColMajor
+OpMemberDecorate %1 1 MatrixStride 8
+)");
+ EXPECT_EQ(DumpInstructions(generator_.Module().Debug()), R"(OpMemberName %1 0 "m"
+OpMemberName %1 1 "arr"
+OpName %1 "MyStruct"
+)");
+}
+
// Test that we can emit multiple types.
// Includes types with the same opcode but different parameters.
TEST_F(SpvGeneratorImplTest, Type_Multiple) {