writer/hlsl: Generate padding for UBO padded structs

Combined with the new PadArrayElements transform, arrays with strides
are now correctly emitted.

Fixed: tint:182
Fixed: tint:895
Change-Id: I26a1be94dee6e4c9d9747c8317a932fc1fb3c810
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54640
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index eded048..8cde6f9 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -14,6 +14,8 @@
 
 #include "src/writer/hlsl/generator_impl.h"
 
+#include <algorithm>
+#include <iomanip>
 #include <utility>
 #include <vector>
 
@@ -1343,7 +1345,11 @@
       type->is_scalar_vector() && expr->values().size() == 1 &&
       TypeOf(expr->values()[0])->is_scalar();
 
-  if (brackets) {
+  auto it = structure_builders_.find(As<sem::Struct>(type));
+  if (it != structure_builders_.end()) {
+    out << it->second << "(";
+    brackets = false;
+  } else if (brackets) {
     out << "{";
   } else {
     if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite,
@@ -1904,7 +1910,13 @@
       }
     }
   } else if (auto* str = type->As<sem::Struct>()) {
-    out << "{";
+    auto it = structure_builders_.find(str);
+    if (it != structure_builders_.end()) {
+      out << it->second << "(";
+    } else {
+      out << "{";
+    }
+
     bool first = true;
     for (auto* member : str->Members()) {
       if (!first) {
@@ -1915,7 +1927,8 @@
         return false;
       }
     }
-    out << "}";
+
+    out << (it != structure_builders_.end() ? ")" : "}");
   } else if (auto* arr = type->As<sem::Array>()) {
     out << "{";
     auto* elem = arr->ElemType();
@@ -2244,23 +2257,75 @@
     return true;
   }
 
-  auto name = builder_.Symbols().NameFor(str->Declaration()->name());
-  out << "struct " << name << " {" << std::endl;
+  bool is_host_shareable = str->IsHostShareable();
+  uint32_t hlsl_offset = 0;
+
+  // Emits a `/* 0xnnnn */` byte offset comment for a struct member.
+  auto add_byte_offset_comment = [&](uint32_t offset) {
+    std::ios_base::fmtflags saved_flag_state(out.flags());
+    out << "/* 0x" << std::hex << std::setfill('0') << std::setw(4) << offset
+        << " */ ";
+    out.flags(saved_flag_state);
+  };
+
+  uint32_t pad_count = 0;
+  auto add_padding = [&](uint32_t size) {
+    if (size & 3) {
+      TINT_ICE(builder_.Diagnostics())
+          << "attempting to pad field with " << size
+          << " bytes, but we require a multiple of 4 bytes";
+      return false;
+    }
+    std::string name;
+    do {
+      name = "tint_pad_" + std::to_string(pad_count++);
+    } while (str->FindMember(builder_.Symbols().Get(name)));
+
+    out << "int " << name << "[" << (size / 4) << "];" << std::endl;
+    return true;
+  };
+
+  auto struct_name = builder_.Symbols().NameFor(str->Declaration()->name());
+  out << "struct " << struct_name << " {" << std::endl;
 
   increment_indent();
   for (auto* mem : str->Members()) {
     make_indent(out);
-    // TODO(dsinclair): Handle [[offset]] annotation on structs
-    // https://bugs.chromium.org/p/tint/issues/detail?id=184
 
-    auto mem_name = builder_.Symbols().NameFor(mem->Declaration()->symbol());
-    if (!EmitType(out, mem->Type(), ast::StorageClass::kNone,
-                  ast::Access::kReadWrite, mem_name)) {
+    auto name = builder_.Symbols().NameFor(mem->Declaration()->symbol());
+    auto wgsl_offset = mem->Offset();
+
+    if (is_host_shareable) {
+      if (wgsl_offset < hlsl_offset) {
+        // Unimplementable layout
+        TINT_ICE(diagnostics_)
+            << "Structure member WGSL offset (" << wgsl_offset
+            << ") is behind HLSL offset (" << hlsl_offset << ")";
+        return false;
+      }
+
+      // Generate padding if required
+      if (auto padding = wgsl_offset - hlsl_offset) {
+        add_byte_offset_comment(hlsl_offset);
+        if (!add_padding(padding)) {
+          return false;
+        }
+        hlsl_offset += padding;
+        make_indent(out);
+      }
+
+      add_byte_offset_comment(hlsl_offset);
+    }
+
+    auto* ty = mem->Type();
+
+    if (!EmitType(out, ty, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                  name)) {
       return false;
     }
     // Array member name will be output with the type
-    if (!mem->Type()->Is<sem::Array>()) {
-      out << " " << mem_name;
+    if (!ty->Is<sem::Array>()) {
+      out << " " << name;
     }
 
     for (auto* deco : mem->Declaration()->decorations()) {
@@ -2295,12 +2360,84 @@
     }
 
     out << ";" << std::endl;
+
+    if (is_host_shareable) {
+      // Calculate new HLSL offset
+      auto size_align = HlslPackedTypeSizeAndAlign(ty);
+      if (hlsl_offset % size_align.align) {
+        TINT_ICE(diagnostics_)
+            << "Misaligned HLSL structure member "
+            << ty->FriendlyName(builder_.Symbols()) << " " << name;
+        return false;
+      }
+      hlsl_offset += size_align.size;
+    }
   }
+
+  if (is_host_shareable && str->Size() != hlsl_offset) {
+    make_indent(out);
+    add_byte_offset_comment(hlsl_offset);
+    if (!add_padding(str->Size() - hlsl_offset)) {
+      return false;
+    }
+  }
+
   decrement_indent();
   make_indent(out);
 
   out << "};" << std::endl;
 
+  // If the structure has padding members, create a helper function for building
+  // the structure.
+  if (pad_count) {
+    auto builder_name = generate_name("make_" + struct_name);
+
+    out << std::endl;
+    out << struct_name << " " << builder_name << "(";
+    uint32_t idx = 0;
+    for (auto* mem : str->Members()) {
+      if (idx > 0) {
+        out << ",";
+        make_indent(out << std::endl);
+        out << std::string(struct_name.length() + builder_name.length() + 2,
+                           ' ');
+      }
+      auto name = "param_" + std::to_string(idx++);
+      auto* ty = mem->Type();
+      if (!EmitType(out, ty, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                    name)) {
+        return false;
+      }
+
+      // Array member name will be output with the type
+      if (!ty->Is<sem::Array>()) {
+        out << " " << name;
+      }
+    }
+    out << ") {";
+    increment_indent();
+    make_indent(out << std::endl);
+
+    out << struct_name << " output;";
+    make_indent(out << std::endl);
+    idx = 0;
+    for (auto* mem : str->Members()) {
+      out << "output."
+          << builder_.Symbols().NameFor(mem->Declaration()->symbol()) << " = "
+          << "param_" + std::to_string(idx++) << ";";
+      make_indent(out << std::endl);
+    }
+    out << "return output;";
+
+    decrement_indent();
+    make_indent(out << std::endl);
+    out << "}";
+
+    make_indent(out << std::endl);
+
+    structure_builders_[str] = builder_name;
+  }
+
   return true;
 }
 
@@ -2469,6 +2606,63 @@
   return true;
 }
 
+// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
+// TODO(crbug.com/tint/898): We need CTS and / or Dawn e2e tests for this logic.
+GeneratorImpl::SizeAndAlign GeneratorImpl::HlslPackedTypeSizeAndAlign(
+    const sem::Type* ty) {
+  if (ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
+    return {4, 4};
+  }
+
+  if (auto* vec = ty->As<sem::Vector>()) {
+    auto num_els = vec->size();
+    auto* el_ty = vec->type();
+    if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
+      return SizeAndAlign{num_els * 4, 4};
+    }
+  }
+
+  if (auto* mat = ty->As<sem::Matrix>()) {
+    auto cols = mat->columns();
+    auto rows = mat->rows();
+    auto* el_ty = mat->type();
+    if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
+      static constexpr SizeAndAlign table[] = {
+          /* float2x2 */ {16, 8},
+          /* float2x3 */ {32, 16},
+          /* float2x4 */ {32, 16},
+          /* float3x2 */ {24, 8},
+          /* float3x3 */ {48, 16},
+          /* float3x4 */ {48, 16},
+          /* float4x2 */ {32, 8},
+          /* float4x3 */ {64, 16},
+          /* float4x4 */ {64, 16},
+      };
+      if (cols >= 2 && cols <= 4 && rows >= 2 && rows <= 4) {
+        return table[(3 * (cols - 2)) + (rows - 2)];
+      }
+    }
+  }
+
+  if (auto* arr = ty->As<sem::Array>()) {
+    auto el_size_align = HlslPackedTypeSizeAndAlign(arr->ElemType());
+    if (!arr->IsStrideImplicit()) {
+      TINT_ICE(diagnostics_) << "arrays with explicit strides should have "
+                                "removed with the PadArrayElements transform";
+      return {};
+    }
+    auto num_els = std::max<uint32_t>(arr->Count(), 1);
+    return SizeAndAlign{el_size_align.size * num_els, el_size_align.align};
+  }
+
+  if (auto* str = ty->As<sem::Struct>()) {
+    return SizeAndAlign{str->Size(), str->Align()};
+  }
+
+  TINT_UNREACHABLE(diagnostics_) << "Unhandled type " << ty->TypeInfo().name;
+  return {};
+}
+
 }  // namespace hlsl
 }  // namespace writer
 }  // namespace tint
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 99dbbba..d14b8a1 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -426,8 +426,19 @@
     return EmitBlockBraces(out, "", std::forward<F>(cb));
   }
 
+  // A pair of byte size and alignment `uint32_t`s.
+  struct SizeAndAlign {
+    uint32_t size;
+    uint32_t align;
+  };
+
+  /// @returns the HLSL packed type size and alignment in bytes for the given
+  /// type.
+  SizeAndAlign HlslPackedTypeSizeAndAlign(const sem::Type* ty);
+
   ProgramBuilder builder_;
   std::function<bool(std::ostream& out)> emit_continuing_;
+  std::unordered_map<const sem::Struct*, std::string> structure_builders_;
 };
 
 }  // namespace hlsl
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index 6add088..dc55611 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -342,7 +342,7 @@
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
   EXPECT_EQ(result(), R"(struct UBO {
-  float4 coord;
+  /* 0x0000 */ float4 coord;
 };
 
 ConstantBuffer<UBO> ubo : register(b0, space1);
@@ -385,7 +385,7 @@
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
   EXPECT_EQ(result(), R"(struct Uniforms {
-  float4 coord;
+  /* 0x0000 */ float4 coord;
 };
 
 ConstantBuffer<Uniforms> uniforms : register(b0, space1);
@@ -584,7 +584,7 @@
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
   EXPECT_EQ(result(), R"(struct S {
-  float x;
+  /* 0x0000 */ float x;
 };
 
 ConstantBuffer<S> coord : register(b0, space1);
diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc
index eb5c150..ac16ffe 100644
--- a/src/writer/hlsl/generator_impl_type_test.cc
+++ b/src/writer/hlsl/generator_impl_type_test.cc
@@ -93,16 +93,29 @@
   EXPECT_EQ(result(), "bool[4]");
 }
 
-TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_RuntimeArray) {
-  auto* arr = ty.array<bool>();
-  Global("G", arr, ast::StorageClass::kPrivate);
+TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayWithStride) {
+  auto* s = Structure("s", {Member("arr", ty.array<f32, 4>(64))},
+                      {create<ast::StructBlockDecoration>()});
+  auto* ubo = Global("ubo", ty.Of(s), ast::StorageClass::kUniform,
+                     ast::DecorationList{
+                         create<ast::GroupDecoration>(1),
+                         create<ast::BindingDecoration>(1),
+                     });
+  WrapInFunction(MemberAccessor(ubo, "arr"));
 
-  GeneratorImpl& gen = Build();
+  GeneratorImpl& gen = SanitizeAndBuild();
 
-  ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::Access::kReadWrite, "ary"))
-      << gen.error();
-  EXPECT_EQ(result(), "bool ary[]");
+  ASSERT_TRUE(gen.Generate(out)) << gen.error();
+  EXPECT_THAT(result(), HasSubstr(R"(struct tint_padded_array_element {
+  /* 0x0000 */ float el;
+  /* 0x0004 */ int tint_pad_0[15];
+};)"));
+  EXPECT_THAT(result(), HasSubstr(R"(struct tint_array_wrapper {
+  /* 0x0000 */ tint_padded_array_element arr[4];
+};)"));
+  EXPECT_THAT(result(), HasSubstr(R"(struct s {
+  /* 0x0000 */ tint_array_wrapper arr;
+};)"));
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Bool) {
@@ -219,31 +232,427 @@
   EXPECT_EQ(result(), "S");
 }
 
-/// TODO(bclayton): Enable this, fix it, add tests for vector, matrix, array and
-/// nested structures.
-TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
-  auto* s = Structure(
-      "S", {
-               Member("a", ty.i32(), {MemberSize(32)}),
-               Member("b", ty.f32()),
-               Member("c", ty.f32(), {MemberAlign(128), MemberSize(128)}),
-           });
-  Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_Layout_NonComposites) {
+  auto* s =
+      Structure("S",
+                {
+                    Member("a", ty.i32(), {MemberSize(32)}),
+                    Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}),
+                    Member("c", ty.vec2<f32>()),
+                    Member("d", ty.u32()),
+                    Member("e", ty.vec3<f32>()),
+                    Member("f", ty.u32()),
+                    Member("g", ty.vec4<f32>()),
+                    Member("h", ty.u32()),
+                    Member("i", ty.mat2x2<f32>()),
+                    Member("j", ty.u32()),
+                    Member("k", ty.mat2x3<f32>()),
+                    Member("l", ty.u32()),
+                    Member("m", ty.mat2x4<f32>()),
+                    Member("n", ty.u32()),
+                    Member("o", ty.mat3x2<f32>()),
+                    Member("p", ty.u32()),
+                    Member("q", ty.mat3x3<f32>()),
+                    Member("r", ty.u32()),
+                    Member("s", ty.mat3x4<f32>()),
+                    Member("t", ty.u32()),
+                    Member("u", ty.mat4x2<f32>()),
+                    Member("v", ty.u32()),
+                    Member("w", ty.mat4x3<f32>()),
+                    Member("x", ty.u32()),
+                    Member("y", ty.mat4x4<f32>()),
+                    Member("z", ty.f32()),
+                },
+                {create<ast::StructBlockDecoration>()});
+
+  Global("G", ty.Of(s), ast::StorageClass::kUniform,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(0),
+             create<ast::GroupDecoration>(0),
+         });
 
   GeneratorImpl& gen = Build();
 
   auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
-  ASSERT_TRUE(gen.EmitType(out, sem_s, ast::StorageClass::kNone,
-                           ast::Access::kReadWrite, ""))
-      << gen.error();
-  EXPECT_EQ(gen.result(), R"(struct S {
-  int a;
-  int8_t pad_0[28];
-  float b;
-  int8_t pad_1[92];
-  float c;
-  int8_t pad_2[124];
+  ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
+
+  auto* expect = R"(struct S {
+  /* 0x0000 */ int a;
+  /* 0x0004 */ int tint_pad_0[31];
+  /* 0x0080 */ float b;
+  /* 0x0084 */ int tint_pad_1[31];
+  /* 0x0100 */ float2 c;
+  /* 0x0108 */ uint d;
+  /* 0x010c */ int tint_pad_2[1];
+  /* 0x0110 */ float3 e;
+  /* 0x011c */ uint f;
+  /* 0x0120 */ float4 g;
+  /* 0x0130 */ uint h;
+  /* 0x0134 */ int tint_pad_3[1];
+  /* 0x0138 */ float2x2 i;
+  /* 0x0148 */ uint j;
+  /* 0x014c */ int tint_pad_4[1];
+  /* 0x0150 */ float2x3 k;
+  /* 0x0170 */ uint l;
+  /* 0x0174 */ int tint_pad_5[3];
+  /* 0x0180 */ float2x4 m;
+  /* 0x01a0 */ uint n;
+  /* 0x01a4 */ int tint_pad_6[1];
+  /* 0x01a8 */ float3x2 o;
+  /* 0x01c0 */ uint p;
+  /* 0x01c4 */ int tint_pad_7[3];
+  /* 0x01d0 */ float3x3 q;
+  /* 0x0200 */ uint r;
+  /* 0x0204 */ int tint_pad_8[3];
+  /* 0x0210 */ float3x4 s;
+  /* 0x0240 */ uint t;
+  /* 0x0244 */ int tint_pad_9[1];
+  /* 0x0248 */ float4x2 u;
+  /* 0x0268 */ uint v;
+  /* 0x026c */ int tint_pad_10[1];
+  /* 0x0270 */ float4x3 w;
+  /* 0x02b0 */ uint x;
+  /* 0x02b4 */ int tint_pad_11[3];
+  /* 0x02c0 */ float4x4 y;
+  /* 0x0300 */ float z;
+  /* 0x0304 */ int tint_pad_12[31];
 };
+
+S make_S(int param_0,
+         float param_1,
+         float2 param_2,
+         uint param_3,
+         float3 param_4,
+         uint param_5,
+         float4 param_6,
+         uint param_7,
+         float2x2 param_8,
+         uint param_9,
+         float2x3 param_10,
+         uint param_11,
+         float2x4 param_12,
+         uint param_13,
+         float3x2 param_14,
+         uint param_15,
+         float3x3 param_16,
+         uint param_17,
+         float3x4 param_18,
+         uint param_19,
+         float4x2 param_20,
+         uint param_21,
+         float4x3 param_22,
+         uint param_23,
+         float4x4 param_24,
+         float param_25) {
+  S output;
+  output.a = param_0;
+  output.b = param_1;
+  output.c = param_2;
+  output.d = param_3;
+  output.e = param_4;
+  output.f = param_5;
+  output.g = param_6;
+  output.h = param_7;
+  output.i = param_8;
+  output.j = param_9;
+  output.k = param_10;
+  output.l = param_11;
+  output.m = param_12;
+  output.n = param_13;
+  output.o = param_14;
+  output.p = param_15;
+  output.q = param_16;
+  output.r = param_17;
+  output.s = param_18;
+  output.t = param_19;
+  output.u = param_20;
+  output.v = param_21;
+  output.w = param_22;
+  output.x = param_23;
+  output.y = param_24;
+  output.z = param_25;
+  return output;
+}
+)";
+
+  EXPECT_EQ(result(), expect);
+}
+
+TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_Layout_Structures) {
+  // inner_x: size(1024), align(512)
+  auto* inner_x =
+      Structure("inner_x", {
+                               Member("a", ty.i32()),
+                               Member("b", ty.f32(), {MemberAlign(512)}),
+                           });
+
+  // inner_y: size(516), align(4)
+  auto* inner_y =
+      Structure("inner_y", {
+                               Member("a", ty.i32(), {MemberSize(512)}),
+                               Member("b", ty.f32()),
+                           });
+
+  auto* s = Structure("S",
+                      {
+                          Member("a", ty.i32()),
+                          Member("b", ty.Of(inner_x)),
+                          Member("c", ty.f32()),
+                          Member("d", ty.Of(inner_y)),
+                          Member("e", ty.f32()),
+                      },
+                      {create<ast::StructBlockDecoration>()});
+
+  Global("G", ty.Of(s), ast::StorageClass::kUniform,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(0),
+             create<ast::GroupDecoration>(0),
+         });
+
+  GeneratorImpl& gen = Build();
+
+  auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
+  ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
+
+  auto* expect = R"(struct S {
+  /* 0x0000 */ int a;
+  /* 0x0004 */ int tint_pad_0[127];
+  /* 0x0200 */ inner_x b;
+  /* 0x0600 */ float c;
+  /* 0x0604 */ inner_y d;
+  /* 0x0808 */ float e;
+  /* 0x080c */ int tint_pad_1[125];
+};
+
+S make_S(int param_0,
+         inner_x param_1,
+         float param_2,
+         inner_y param_3,
+         float param_4) {
+  S output;
+  output.a = param_0;
+  output.b = param_1;
+  output.c = param_2;
+  output.d = param_3;
+  output.e = param_4;
+  return output;
+}
+)";
+  EXPECT_EQ(result(), expect);
+}
+
+TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_Layout_ArrayDefaultStride) {
+  // inner: size(1024), align(512)
+  auto* inner =
+      Structure("inner", {
+                             Member("a", ty.i32()),
+                             Member("b", ty.f32(), {MemberAlign(512)}),
+                         });
+
+  // array_x: size(28), align(4)
+  auto* array_x = ty.array<f32, 7>();
+
+  // array_y: size(4096), align(512)
+  auto* array_y = ty.array(ty.Of(inner), 4);
+
+  // array_z: size(4), align(4)
+  auto* array_z = ty.array<f32, 1>();
+
+  auto* s =
+      Structure("S",
+                {
+                    Member("a", ty.i32()),
+                    Member("b", array_x),
+                    Member("c", ty.f32()),
+                    Member("d", array_y),
+                    Member("e", ty.f32()),
+                    Member("f", array_z),
+                },
+                ast::DecorationList{create<ast::StructBlockDecoration>()});
+
+  Global("G", ty.Of(s), ast::StorageClass::kUniform,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(0),
+             create<ast::GroupDecoration>(0),
+         });
+
+  GeneratorImpl& gen = Build();
+
+  auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
+  ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
+
+  auto* expect = R"(struct S {
+  /* 0x0000 */ int a;
+  /* 0x0004 */ float b[7];
+  /* 0x0020 */ float c;
+  /* 0x0024 */ int tint_pad_0[119];
+  /* 0x0200 */ inner d[4];
+  /* 0x1200 */ float e;
+  /* 0x1204 */ float f[1];
+  /* 0x1208 */ int tint_pad_1[126];
+};
+
+S make_S(int param_0,
+         float param_1[7],
+         float param_2,
+         inner param_3[4],
+         float param_4,
+         float param_5[1]) {
+  S output;
+  output.a = param_0;
+  output.b = param_1;
+  output.c = param_2;
+  output.d = param_3;
+  output.e = param_4;
+  output.f = param_5;
+  return output;
+}
+)";
+
+  EXPECT_EQ(result(), expect);
+}
+
+TEST_F(HlslGeneratorImplTest_Type, AttemptTintPadSymbolCollision) {
+  auto* s = Structure(
+      "S",
+      {
+          // uses symbols tint_pad_[0..9] and tint_pad_[20..35]
+          Member("tint_pad_2", ty.i32(), {MemberSize(32)}),
+          Member("tint_pad_20", ty.f32(), {MemberAlign(128), MemberSize(128)}),
+          Member("tint_pad_33", ty.vec2<f32>()),
+          Member("tint_pad_1", ty.u32()),
+          Member("tint_pad_3", ty.vec3<f32>()),
+          Member("tint_pad_7", ty.u32()),
+          Member("tint_pad_25", ty.vec4<f32>()),
+          Member("tint_pad_5", ty.u32()),
+          Member("tint_pad_27", ty.mat2x2<f32>()),
+          Member("tint_pad_24", ty.u32()),
+          Member("tint_pad_23", ty.mat2x3<f32>()),
+          Member("tint_pad_0", ty.u32()),
+          Member("tint_pad_8", ty.mat2x4<f32>()),
+          Member("tint_pad_26", ty.u32()),
+          Member("tint_pad_29", ty.mat3x2<f32>()),
+          Member("tint_pad_6", ty.u32()),
+          Member("tint_pad_22", ty.mat3x3<f32>()),
+          Member("tint_pad_32", ty.u32()),
+          Member("tint_pad_34", ty.mat3x4<f32>()),
+          Member("tint_pad_35", ty.u32()),
+          Member("tint_pad_30", ty.mat4x2<f32>()),
+          Member("tint_pad_9", ty.u32()),
+          Member("tint_pad_31", ty.mat4x3<f32>()),
+          Member("tint_pad_28", ty.u32()),
+          Member("tint_pad_4", ty.mat4x4<f32>()),
+          Member("tint_pad_21", ty.f32()),
+      },
+      {create<ast::StructBlockDecoration>()});
+
+  Global("G", ty.Of(s), ast::StorageClass::kUniform,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(0),
+             create<ast::GroupDecoration>(0),
+         });
+
+  GeneratorImpl& gen = Build();
+
+  auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
+  ASSERT_TRUE(gen.EmitStructType(out, sem_s)) << gen.error();
+  EXPECT_EQ(result(), R"(struct S {
+  /* 0x0000 */ int tint_pad_2;
+  /* 0x0004 */ int tint_pad_10[31];
+  /* 0x0080 */ float tint_pad_20;
+  /* 0x0084 */ int tint_pad_11[31];
+  /* 0x0100 */ float2 tint_pad_33;
+  /* 0x0108 */ uint tint_pad_1;
+  /* 0x010c */ int tint_pad_12[1];
+  /* 0x0110 */ float3 tint_pad_3;
+  /* 0x011c */ uint tint_pad_7;
+  /* 0x0120 */ float4 tint_pad_25;
+  /* 0x0130 */ uint tint_pad_5;
+  /* 0x0134 */ int tint_pad_13[1];
+  /* 0x0138 */ float2x2 tint_pad_27;
+  /* 0x0148 */ uint tint_pad_24;
+  /* 0x014c */ int tint_pad_14[1];
+  /* 0x0150 */ float2x3 tint_pad_23;
+  /* 0x0170 */ uint tint_pad_0;
+  /* 0x0174 */ int tint_pad_15[3];
+  /* 0x0180 */ float2x4 tint_pad_8;
+  /* 0x01a0 */ uint tint_pad_26;
+  /* 0x01a4 */ int tint_pad_16[1];
+  /* 0x01a8 */ float3x2 tint_pad_29;
+  /* 0x01c0 */ uint tint_pad_6;
+  /* 0x01c4 */ int tint_pad_17[3];
+  /* 0x01d0 */ float3x3 tint_pad_22;
+  /* 0x0200 */ uint tint_pad_32;
+  /* 0x0204 */ int tint_pad_18[3];
+  /* 0x0210 */ float3x4 tint_pad_34;
+  /* 0x0240 */ uint tint_pad_35;
+  /* 0x0244 */ int tint_pad_19[1];
+  /* 0x0248 */ float4x2 tint_pad_30;
+  /* 0x0268 */ uint tint_pad_9;
+  /* 0x026c */ int tint_pad_36[1];
+  /* 0x0270 */ float4x3 tint_pad_31;
+  /* 0x02b0 */ uint tint_pad_28;
+  /* 0x02b4 */ int tint_pad_37[3];
+  /* 0x02c0 */ float4x4 tint_pad_4;
+  /* 0x0300 */ float tint_pad_21;
+  /* 0x0304 */ int tint_pad_38[31];
+};
+
+S make_S(int param_0,
+         float param_1,
+         float2 param_2,
+         uint param_3,
+         float3 param_4,
+         uint param_5,
+         float4 param_6,
+         uint param_7,
+         float2x2 param_8,
+         uint param_9,
+         float2x3 param_10,
+         uint param_11,
+         float2x4 param_12,
+         uint param_13,
+         float3x2 param_14,
+         uint param_15,
+         float3x3 param_16,
+         uint param_17,
+         float3x4 param_18,
+         uint param_19,
+         float4x2 param_20,
+         uint param_21,
+         float4x3 param_22,
+         uint param_23,
+         float4x4 param_24,
+         float param_25) {
+  S output;
+  output.tint_pad_2 = param_0;
+  output.tint_pad_20 = param_1;
+  output.tint_pad_33 = param_2;
+  output.tint_pad_1 = param_3;
+  output.tint_pad_3 = param_4;
+  output.tint_pad_7 = param_5;
+  output.tint_pad_25 = param_6;
+  output.tint_pad_5 = param_7;
+  output.tint_pad_27 = param_8;
+  output.tint_pad_24 = param_9;
+  output.tint_pad_23 = param_10;
+  output.tint_pad_0 = param_11;
+  output.tint_pad_8 = param_12;
+  output.tint_pad_26 = param_13;
+  output.tint_pad_29 = param_14;
+  output.tint_pad_6 = param_15;
+  output.tint_pad_22 = param_16;
+  output.tint_pad_32 = param_17;
+  output.tint_pad_34 = param_18;
+  output.tint_pad_35 = param_19;
+  output.tint_pad_30 = param_20;
+  output.tint_pad_9 = param_21;
+  output.tint_pad_31 = param_22;
+  output.tint_pad_28 = param_23;
+  output.tint_pad_4 = param_24;
+  output.tint_pad_21 = param_25;
+  return output;
+}
 )");
 }
 
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index ecd9205..d6ffed2 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -2410,6 +2410,7 @@
   return true;
 }
 
+// TODO(crbug.com/tint/898): We need CTS and / or Dawn e2e tests for this logic.
 GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign(
     const sem::Type* ty) {
   if (ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
diff --git a/test/array/assign_to_function_var.wgsl.expected.hlsl b/test/array/assign_to_function_var.wgsl.expected.hlsl
index 6722f05..5935a13 100644
--- a/test/array/assign_to_function_var.wgsl.expected.hlsl
+++ b/test/array/assign_to_function_var.wgsl.expected.hlsl
@@ -4,17 +4,24 @@
 }
 
 struct tint_padded_array_element {
-  int el;
+  /* 0x0000 */ int el;
+  /* 0x0004 */ int tint_pad_0[3];
 };
+
+tint_padded_array_element make_tint_padded_array_element(int param_0) {
+  tint_padded_array_element output;
+  output.el = param_0;
+  return output;
+}
 struct tint_array_wrapper {
-  tint_padded_array_element arr[4];
+  /* 0x0000 */ tint_padded_array_element arr[4];
 };
 struct S {
-  tint_array_wrapper arr;
+  /* 0x0000 */ tint_array_wrapper arr;
 };
 
 tint_array_wrapper tint_symbol_2(RWByteAddressBuffer buffer, uint offset) {
-  const tint_array_wrapper tint_symbol_3 = {{{asint(buffer.Load((offset + 0u)))}, {asint(buffer.Load((offset + 16u)))}, {asint(buffer.Load((offset + 32u)))}, {asint(buffer.Load((offset + 48u)))}}};
+  const tint_array_wrapper tint_symbol_3 = {{make_tint_padded_array_element(asint(buffer.Load((offset + 0u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 16u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 32u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 48u))))}};
   return tint_symbol_3;
 }
 
@@ -24,12 +31,12 @@
 RWByteAddressBuffer src_storage : register(u1, space0);
 
 tint_array_wrapper ret_arr() {
-  const tint_array_wrapper tint_symbol_4 = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper tint_symbol_4 = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   return tint_symbol_4;
 }
 
 S ret_struct_arr() {
-  const S tint_symbol_5 = {{{{0}, {0}, {0}, {0}}}};
+  const S tint_symbol_5 = {{{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}}};
   return tint_symbol_5;
 }
 
@@ -44,13 +51,13 @@
 };
 
 void foo(tint_array_wrapper src_param) {
-  tint_array_wrapper src_function = {{{0}, {0}, {0}, {0}}};
-  tint_array_wrapper tint_symbol = {{{0}, {0}, {0}, {0}}};
-  const tint_array_wrapper tint_symbol_6 = {{{1}, {2}, {3}, {3}}};
+  tint_array_wrapper src_function = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
+  tint_array_wrapper tint_symbol = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
+  const tint_array_wrapper tint_symbol_6 = {{make_tint_padded_array_element(1), make_tint_padded_array_element(2), make_tint_padded_array_element(3), make_tint_padded_array_element(3)}};
   tint_symbol = tint_symbol_6;
   tint_symbol = src_param;
   tint_symbol = ret_arr();
-  const tint_array_wrapper src_let = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper src_let = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   tint_symbol = src_let;
   tint_symbol = src_function;
   tint_symbol = src_private;
diff --git a/test/array/assign_to_private_var.wgsl.expected.hlsl b/test/array/assign_to_private_var.wgsl.expected.hlsl
index 8f935d4..cca7ff6 100644
--- a/test/array/assign_to_private_var.wgsl.expected.hlsl
+++ b/test/array/assign_to_private_var.wgsl.expected.hlsl
@@ -4,17 +4,24 @@
 }
 
 struct tint_padded_array_element {
-  int el;
+  /* 0x0000 */ int el;
+  /* 0x0004 */ int tint_pad_0[3];
 };
+
+tint_padded_array_element make_tint_padded_array_element(int param_0) {
+  tint_padded_array_element output;
+  output.el = param_0;
+  return output;
+}
 struct tint_array_wrapper {
-  tint_padded_array_element arr[4];
+  /* 0x0000 */ tint_padded_array_element arr[4];
 };
 struct S {
-  tint_array_wrapper arr;
+  /* 0x0000 */ tint_array_wrapper arr;
 };
 
 tint_array_wrapper tint_symbol_2(RWByteAddressBuffer buffer, uint offset) {
-  const tint_array_wrapper tint_symbol_3 = {{{asint(buffer.Load((offset + 0u)))}, {asint(buffer.Load((offset + 16u)))}, {asint(buffer.Load((offset + 32u)))}, {asint(buffer.Load((offset + 48u)))}}};
+  const tint_array_wrapper tint_symbol_3 = {{make_tint_padded_array_element(asint(buffer.Load((offset + 0u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 16u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 32u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 48u))))}};
   return tint_symbol_3;
 }
 
@@ -37,22 +44,22 @@
 static tint_array_wrapper_1 dst_nested;
 
 tint_array_wrapper ret_arr() {
-  const tint_array_wrapper tint_symbol_4 = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper tint_symbol_4 = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   return tint_symbol_4;
 }
 
 S ret_struct_arr() {
-  const S tint_symbol_5 = {{{{0}, {0}, {0}, {0}}}};
+  const S tint_symbol_5 = {{{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}}};
   return tint_symbol_5;
 }
 
 void foo(tint_array_wrapper src_param) {
-  tint_array_wrapper src_function = {{{0}, {0}, {0}, {0}}};
-  const tint_array_wrapper tint_symbol_6 = {{{1}, {2}, {3}, {3}}};
+  tint_array_wrapper src_function = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
+  const tint_array_wrapper tint_symbol_6 = {{make_tint_padded_array_element(1), make_tint_padded_array_element(2), make_tint_padded_array_element(3), make_tint_padded_array_element(3)}};
   tint_symbol = tint_symbol_6;
   tint_symbol = src_param;
   tint_symbol = ret_arr();
-  const tint_array_wrapper src_let = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper src_let = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   tint_symbol = src_let;
   tint_symbol = src_function;
   tint_symbol = src_private;
diff --git a/test/array/assign_to_storage_var.wgsl.expected.hlsl b/test/array/assign_to_storage_var.wgsl.expected.hlsl
index 6988ce2..6ed5129 100644
--- a/test/array/assign_to_storage_var.wgsl.expected.hlsl
+++ b/test/array/assign_to_storage_var.wgsl.expected.hlsl
@@ -4,17 +4,24 @@
 }
 
 struct tint_padded_array_element {
-  int el;
+  /* 0x0000 */ int el;
+  /* 0x0004 */ int tint_pad_0[3];
 };
+
+tint_padded_array_element make_tint_padded_array_element(int param_0) {
+  tint_padded_array_element output;
+  output.el = param_0;
+  return output;
+}
 struct tint_array_wrapper {
-  tint_padded_array_element arr[4];
+  /* 0x0000 */ tint_padded_array_element arr[4];
 };
 struct S {
-  tint_array_wrapper arr;
+  /* 0x0000 */ tint_array_wrapper arr;
 };
 
 tint_array_wrapper tint_symbol_2(RWByteAddressBuffer buffer, uint offset) {
-  const tint_array_wrapper tint_symbol_9 = {{{asint(buffer.Load((offset + 0u)))}, {asint(buffer.Load((offset + 16u)))}, {asint(buffer.Load((offset + 32u)))}, {asint(buffer.Load((offset + 48u)))}}};
+  const tint_array_wrapper tint_symbol_9 = {{make_tint_padded_array_element(asint(buffer.Load((offset + 0u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 16u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 32u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 48u))))}};
   return tint_symbol_9;
 }
 
@@ -26,13 +33,13 @@
 }
 
 struct tint_array_wrapper_3 {
-  int arr[2];
+  /* 0x0000 */ int arr[2];
 };
 struct tint_array_wrapper_2 {
-  tint_array_wrapper_3 arr[3];
+  /* 0x0000 */ tint_array_wrapper_3 arr[3];
 };
 struct tint_array_wrapper_1 {
-  tint_array_wrapper_2 arr[4];
+  /* 0x0000 */ tint_array_wrapper_2 arr[4];
 };
 
 void tint_symbol_6(RWByteAddressBuffer buffer, uint offset, tint_array_wrapper_3 value) {
@@ -61,22 +68,22 @@
 RWByteAddressBuffer dst_nested : register(u3, space0);
 
 tint_array_wrapper ret_arr() {
-  const tint_array_wrapper tint_symbol_10 = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper tint_symbol_10 = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   return tint_symbol_10;
 }
 
 S ret_struct_arr() {
-  const S tint_symbol_11 = {{{{0}, {0}, {0}, {0}}}};
+  const S tint_symbol_11 = {{{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}}};
   return tint_symbol_11;
 }
 
 void foo(tint_array_wrapper src_param) {
-  tint_array_wrapper src_function = {{{0}, {0}, {0}, {0}}};
-  const tint_array_wrapper tint_symbol_12 = {{{1}, {2}, {3}, {3}}};
+  tint_array_wrapper src_function = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
+  const tint_array_wrapper tint_symbol_12 = {{make_tint_padded_array_element(1), make_tint_padded_array_element(2), make_tint_padded_array_element(3), make_tint_padded_array_element(3)}};
   tint_symbol_4(tint_symbol, 0u, tint_symbol_12);
   tint_symbol_4(tint_symbol, 0u, src_param);
   tint_symbol_4(tint_symbol, 0u, ret_arr());
-  const tint_array_wrapper src_let = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper src_let = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   tint_symbol_4(tint_symbol, 0u, src_let);
   tint_symbol_4(tint_symbol, 0u, src_function);
   tint_symbol_4(tint_symbol, 0u, src_private);
diff --git a/test/array/assign_to_workgroup_var.wgsl.expected.hlsl b/test/array/assign_to_workgroup_var.wgsl.expected.hlsl
index 725566e..64bb639 100644
--- a/test/array/assign_to_workgroup_var.wgsl.expected.hlsl
+++ b/test/array/assign_to_workgroup_var.wgsl.expected.hlsl
@@ -4,17 +4,24 @@
 }
 
 struct tint_padded_array_element {
-  int el;
+  /* 0x0000 */ int el;
+  /* 0x0004 */ int tint_pad_0[3];
 };
+
+tint_padded_array_element make_tint_padded_array_element(int param_0) {
+  tint_padded_array_element output;
+  output.el = param_0;
+  return output;
+}
 struct tint_array_wrapper {
-  tint_padded_array_element arr[4];
+  /* 0x0000 */ tint_padded_array_element arr[4];
 };
 struct S {
-  tint_array_wrapper arr;
+  /* 0x0000 */ tint_array_wrapper arr;
 };
 
 tint_array_wrapper tint_symbol_2(RWByteAddressBuffer buffer, uint offset) {
-  const tint_array_wrapper tint_symbol_3 = {{{asint(buffer.Load((offset + 0u)))}, {asint(buffer.Load((offset + 16u)))}, {asint(buffer.Load((offset + 32u)))}, {asint(buffer.Load((offset + 48u)))}}};
+  const tint_array_wrapper tint_symbol_3 = {{make_tint_padded_array_element(asint(buffer.Load((offset + 0u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 16u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 32u)))), make_tint_padded_array_element(asint(buffer.Load((offset + 48u))))}};
   return tint_symbol_3;
 }
 
@@ -37,22 +44,22 @@
 groupshared tint_array_wrapper_1 dst_nested;
 
 tint_array_wrapper ret_arr() {
-  const tint_array_wrapper tint_symbol_4 = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper tint_symbol_4 = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   return tint_symbol_4;
 }
 
 S ret_struct_arr() {
-  const S tint_symbol_5 = {{{{0}, {0}, {0}, {0}}}};
+  const S tint_symbol_5 = {{{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}}};
   return tint_symbol_5;
 }
 
 void foo(tint_array_wrapper src_param) {
-  tint_array_wrapper src_function = {{{0}, {0}, {0}, {0}}};
-  const tint_array_wrapper tint_symbol_6 = {{{1}, {2}, {3}, {3}}};
+  tint_array_wrapper src_function = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
+  const tint_array_wrapper tint_symbol_6 = {{make_tint_padded_array_element(1), make_tint_padded_array_element(2), make_tint_padded_array_element(3), make_tint_padded_array_element(3)}};
   tint_symbol = tint_symbol_6;
   tint_symbol = src_param;
   tint_symbol = ret_arr();
-  const tint_array_wrapper src_let = {{{0}, {0}, {0}, {0}}};
+  const tint_array_wrapper src_let = {{make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0), make_tint_padded_array_element(0)}};
   tint_symbol = src_let;
   tint_symbol = src_function;
   tint_symbol = src_private;
diff --git a/test/bug/tint/744.wgsl.expected.hlsl b/test/bug/tint/744.wgsl.expected.hlsl
index d684214..7819eb6 100644
--- a/test/bug/tint/744.wgsl.expected.hlsl
+++ b/test/bug/tint/744.wgsl.expected.hlsl
@@ -1,7 +1,7 @@
 struct Uniforms {
-  uint2 aShape;
-  uint2 bShape;
-  uint2 outShape;
+  /* 0x0000 */ uint2 aShape;
+  /* 0x0008 */ uint2 bShape;
+  /* 0x0010 */ uint2 outShape;
 };
 
 ByteAddressBuffer firstMatrix : register(t0, space0);
diff --git a/test/bug/tint/757.wgsl.expected.hlsl b/test/bug/tint/757.wgsl.expected.hlsl
index 63d2e80..e7db592 100644
--- a/test/bug/tint/757.wgsl.expected.hlsl
+++ b/test/bug/tint/757.wgsl.expected.hlsl
@@ -1,5 +1,5 @@
 struct Constants {
-  int level;
+  /* 0x0000 */ int level;
 };
 
 ConstantBuffer<Constants> constants : register(b0, space0);
diff --git a/test/bug/tint/870.spvasm.expected.hlsl b/test/bug/tint/870.spvasm.expected.hlsl
index e465840..56649a2 100644
--- a/test/bug/tint/870.spvasm.expected.hlsl
+++ b/test/bug/tint/870.spvasm.expected.hlsl
@@ -1,5 +1,5 @@
 struct tint_array_wrapper {
-  int arr[6];
+  /* 0x0000 */ int arr[6];
 };
 
 tint_array_wrapper tint_symbol_1(ByteAddressBuffer buffer, uint offset) {
diff --git a/test/ptr_ref/load/local/ptr_uniform.wgsl.expected.hlsl b/test/ptr_ref/load/local/ptr_uniform.wgsl.expected.hlsl
index f0642c7..ac3efc5 100644
--- a/test/ptr_ref/load/local/ptr_uniform.wgsl.expected.hlsl
+++ b/test/ptr_ref/load/local/ptr_uniform.wgsl.expected.hlsl
@@ -1,5 +1,5 @@
 struct S {
-  int a;
+  /* 0x0000 */ int a;
 };
 
 ConstantBuffer<S> v : register(b0, space0);
diff --git a/test/samples/compute_boids.wgsl.expected.hlsl b/test/samples/compute_boids.wgsl.expected.hlsl
index ab102f4..974996a 100644
--- a/test/samples/compute_boids.wgsl.expected.hlsl
+++ b/test/samples/compute_boids.wgsl.expected.hlsl
@@ -27,13 +27,13 @@
 }
 
 struct SimParams {
-  float deltaT;
-  float rule1Distance;
-  float rule2Distance;
-  float rule3Distance;
-  float rule1Scale;
-  float rule2Scale;
-  float rule3Scale;
+  /* 0x0000 */ float deltaT;
+  /* 0x0004 */ float rule1Distance;
+  /* 0x0008 */ float rule2Distance;
+  /* 0x000c */ float rule3Distance;
+  /* 0x0010 */ float rule1Scale;
+  /* 0x0014 */ float rule2Scale;
+  /* 0x0018 */ float rule3Scale;
 };
 
 ConstantBuffer<SimParams> params : register(b0, space0);
diff --git a/test/samples/cube.wgsl.expected.hlsl b/test/samples/cube.wgsl.expected.hlsl
index bae4c7e..05fb49f 100644
--- a/test/samples/cube.wgsl.expected.hlsl
+++ b/test/samples/cube.wgsl.expected.hlsl
@@ -1,5 +1,5 @@
 struct Uniforms {
-  float4x4 modelViewProjectionMatrix;
+  /* 0x0000 */ float4x4 modelViewProjectionMatrix;
 };
 
 ConstantBuffer<Uniforms> uniforms : register(b0, space0);
diff --git a/test/shader_io/shared_struct_storage_buffer.wgsl.expected.hlsl b/test/shader_io/shared_struct_storage_buffer.wgsl.expected.hlsl
index 1c8ad7d..b482205 100644
--- a/test/shader_io/shared_struct_storage_buffer.wgsl.expected.hlsl
+++ b/test/shader_io/shared_struct_storage_buffer.wgsl.expected.hlsl
@@ -1,9 +1,21 @@
 struct S {
-  float f;
-  uint u;
-  float4 v;
+  /* 0x0000 */ float f;
+  /* 0x0004 */ uint u;
+  /* 0x0008 */ int tint_pad_0[30];
+  /* 0x0080 */ float4 v;
+  /* 0x0090 */ int tint_pad_1[28];
 };
 
+S make_S(float param_0,
+         uint param_1,
+         float4 param_2) {
+  S output;
+  output.f = param_0;
+  output.u = param_1;
+  output.v = param_2;
+  return output;
+}
+
 void tint_symbol_5(RWByteAddressBuffer buffer, uint offset, S value) {
   buffer.Store((offset + 0u), asuint(value.f));
   buffer.Store((offset + 4u), asuint(value.u));
@@ -19,7 +31,7 @@
 };
 
 void frag_main(tint_symbol_1 tint_symbol) {
-  const S input = {tint_symbol.f, tint_symbol.u, tint_symbol.v};
+  const S input = make_S(tint_symbol.f, tint_symbol.u, tint_symbol.v);
   const float f = input.f;
   const uint u = input.u;
   const float4 v = input.v;