[ir] Fix offset of struct members
When a struct member has an offset that is not automatically satisfied
by its alignment, we need to add a @size() attribute to the previous
member.
Change-Id: Ibb9588f5bbe15666c852e83f618c7c1914823599
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/249275
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
index 298247c..ea5516c 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.cc
@@ -1076,15 +1076,37 @@
}
auto n = structs_.GetOrAdd(s, [&] {
- auto members = tint::Transform<8>(s->Members(), [&](const core::type::StructMember* m) {
+ uint32_t current_offset = 0;
+ TINT_ASSERT(s->Members().Length() > 0 && s->Members()[0]->Offset() == 0);
+
+ Vector<const ast::StructMember*, 8> members;
+ for (const auto* m : s->Members()) {
auto ty = Type(m->Type());
const auto& ir_attrs = m->Attributes();
Vector<const ast::Attribute*, 4> ast_attrs;
+
+ TINT_ASSERT(current_offset == m->Offset());
+
+ // If the next member requires an offset that is not automatically satisfied by
+ // its required alignment, we will need to increase the size of this member.
+ uint32_t size = m->Size();
+ if (m->Index() < s->Members().Length() - 1) {
+ auto* next_member = s->Members()[m->Index() + 1];
+ auto next_offset = tint::RoundUp(next_member->Align(), current_offset + size);
+ auto next_member_required_offset = next_member->Offset();
+ if (next_offset < next_member_required_offset) {
+ uint32_t new_size = next_member_required_offset - current_offset;
+ TINT_ASSERT(new_size > size);
+ size = new_size;
+ }
+ current_offset = next_member_required_offset;
+ }
+
if (m->Type()->Align() != m->Align()) {
ast_attrs.Push(b.MemberAlign(u32(m->Align())));
}
- if (m->Type()->Size() != m->Size()) {
- ast_attrs.Push(b.MemberSize(u32(m->Size())));
+ if (m->Type()->Size() != size) {
+ ast_attrs.Push(b.MemberSize(u32(size)));
}
if (auto location = ir_attrs.location) {
ast_attrs.Push(b.Location(u32(*location)));
@@ -1111,8 +1133,8 @@
if (ir_attrs.invariant) {
ast_attrs.Push(b.Invariant());
}
- return b.Member(m->Name().NameView(), ty, std::move(ast_attrs));
- });
+ members.Push(b.Member(m->Name().NameView(), ty, std::move(ast_attrs)));
+ }
// TODO(crbug.com/tint/1902): Emit structure attributes
Vector<const ast::Attribute*, 2> attrs;
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
index 0afbc3b..bbb5f7e 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/ir_to_program_test.cc
@@ -3414,6 +3414,36 @@
)");
}
+TEST_F(IRToProgramTest, StructMemberOffset) {
+ auto* S = ty.Struct(mod.symbols.New("S"),
+ Vector{
+ ty.Get<core::type::StructMember>(mod.symbols.New("a"), ty.i32(), 0u, 0u,
+ 4u, 4u, core::IOAttributes{}),
+ ty.Get<core::type::StructMember>(mod.symbols.New("b"), ty.u32(), 1u,
+ 64u, 4u, 4u, core::IOAttributes{}),
+ ty.Get<core::type::StructMember>(mod.symbols.New("c"), ty.f32(), 2u,
+ 76u, 4u, 4u, core::IOAttributes{}),
+ });
+
+ auto* fn = b.Function("f", ty.void_());
+ auto* x = b.FunctionParam("x", S);
+ fn->SetParams({x});
+ b.Append(fn->Block(), [&] { b.Return(fn); });
+
+ EXPECT_WGSL(R"(
+struct S {
+ @size(64u)
+ a : i32,
+ @size(12u)
+ b : u32,
+ c : f32,
+}
+
+fn f(x : S) {
+}
+)");
+}
+
////////////////////////////////////////////////////////////////////////////////
// chromium_internal_graphite
////////////////////////////////////////////////////////////////////////////////