authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-24 23:21:24+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-25 01:48:24+02:00
log43a091081f1fad7e4d1f2bf2d2b229fb82b65c11
treefd389e0b543f203fd36643c9d101c10388e4b54f
parent12584fac6c6798acfd4ff783a43ee37cbbaf9ef0
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge pull request 'llvm: fix bugs related on padding of struct and unions' (#37347) from pavelverigo/zig:i37335 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37347 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

4 files changed, 77 insertions(+), 15 deletions(-)

src/codegen.zig+4-4
......@@ -617,6 +617,7 @@ pub fn generateSymbol(
617617 else => unreachable,
618618 },
619619 .un => |un| {
620 const union_begin = w.end;
620621 const layout = ty.unionGetLayout(zcu);
621622
622623 if (layout.payload_size == 0) {
......@@ -648,11 +649,10 @@ pub fn generateSymbol(
648649
649650 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
650651 try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent);
651
652 if (layout.padding > 0) {
653 try w.splatByteAll(0, layout.padding);
654 }
655652 }
653
654 const padding: usize = @intCast(union_begin + layout.abi_size - w.end);
655 if (padding > 0) try w.splatByteAll(0, padding);
656656 },
657657 .bitpack => |bitpack| try generateSymbol(bin_file, pt, .fromInterned(bitpack.backing_int_val), w, reloc_parent),
658658 .memoized_call => unreachable,
src/codegen/llvm.zig+21-11
......@@ -2898,8 +2898,8 @@ pub const Object = struct {
28982898 o.gpa,
28992899 try o.builder.arrayType(padding_len, .i8),
29002900 );
2901 if (@ctz(offset) < max_field_ty_align.toLog2Units()) {
2902 struct_kind = .@"packed"; // prevent unexpected trailing padding
2901 if (struct_type.alignment.compareStrict(.lt, max_field_ty_align)) {
2902 struct_kind = .@"packed"; // prevent wrong alignment
29032903 }
29042904 }
29052905
......@@ -2978,14 +2978,22 @@ pub const Object = struct {
29782978 const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);
29792979 const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr);
29802980
2981 const payload_ty = ty: {
2982 if (layout.most_aligned_field_size == layout.payload_size) {
2983 break :ty aligned_field_llvm_ty;
2981 const struct_kind: Builder.Type.Structure.Kind = for (union_obj.field_types.get(ip)) |field_ty_ip| {
2982 const field_ty = Type.fromInterned(field_ty_ip);
2983 if (!field_ty.hasRuntimeBits(zcu)) continue;
2984 if (layout.payload_align.compareStrict(.lt, field_ty.abiAlignment(zcu))) {
2985 break .@"packed"; // prevent wrong alignment
29842986 }
2987 } else .normal;
2988
2989 const payload_ty = ty: {
29852990 const padding_len = if (layout.tag_size == 0)
29862991 layout.abi_size - layout.most_aligned_field_size
29872992 else
29882993 layout.payload_size - layout.most_aligned_field_size;
2994 if (padding_len == 0) {
2995 break :ty aligned_field_llvm_ty;
2996 }
29892997 break :ty try o.builder.structType(.@"packed", &.{
29902998 aligned_field_llvm_ty,
29912999 try o.builder.arrayType(padding_len, .i8),
......@@ -2998,7 +3006,7 @@ pub const Object = struct {
29983006
29993007 o.builder.namedTypeSetBody(
30003008 ty,
3001 try o.builder.structType(.normal, &.{payload_ty}),
3009 try o.builder.structType(struct_kind, &.{payload_ty}),
30023010 );
30033011 return ty;
30043012 }
......@@ -3026,7 +3034,7 @@ pub const Object = struct {
30263034
30273035 o.builder.namedTypeSetBody(
30283036 ty,
3029 try o.builder.structType(.normal, llvm_fields[0..llvm_fields_len]),
3037 try o.builder.structType(struct_kind, llvm_fields[0..llvm_fields_len]),
30303038 );
30313039 return ty;
30323040 },
......@@ -3579,6 +3587,11 @@ pub const Object = struct {
35793587 const payload = if (un.tag != .none) p: {
35803588 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;
35813589 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
3590 const field_size = field_ty.abiSize(zcu);
3591 const padding_len = if (layout.tag_size == 0)
3592 layout.abi_size - field_size
3593 else
3594 layout.payload_size - field_size;
35823595
35833596 // Sometimes we must make an unnamed struct because LLVM does
35843597 // not support bitcasting our payload struct to the true union payload type.
......@@ -3587,7 +3600,6 @@ pub const Object = struct {
35873600 need_unnamed = layout.most_aligned_field != field_index;
35883601
35893602 if (!field_ty.hasRuntimeBits(zcu)) {
3590 const padding_len = layout.payload_size;
35913603 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));
35923604 }
35933605 const payload = try o.lowerValue(un.val, repr);
......@@ -3595,9 +3607,7 @@ pub const Object = struct {
35953607 if (payload_ty != union_ty.structFields(&o.builder)[
35963608 @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align))
35973609 ]) need_unnamed = true;
3598 const field_size = field_ty.abiSize(zcu);
3599 if (field_size == layout.payload_size) break :p payload;
3600 const padding_len = layout.payload_size - field_size;
3610 if (padding_len == 0) break :p payload;
36013611 const padding_ty = try o.builder.arrayType(padding_len, .i8);
36023612 break :p try o.builder.structConst(
36033613 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
test/behavior/align.zig+36
......@@ -495,6 +495,42 @@ test "struct field explicit alignment" {
495495 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
496496}
497497
498test "nested struct field alignment" {
499 const S = struct {
500 fn doTheTest() !void {
501 const T = struct {
502 a: u8,
503 b: struct { c: u16 align(1) },
504 };
505 const init: T = .{ .a = 0, .b = .{ .c = ~@as(u16, 0) } };
506 var t: T = init; // copy constant aggregate
507 _ = &t;
508 try expect(t.b.c == ~@as(u16, 0));
509 }
510 };
511
512 try S.doTheTest();
513 try comptime S.doTheTest();
514}
515
516test "nested union field alignment" {
517 const S = struct {
518 fn doTheTest() !void {
519 const T = struct {
520 a: u8,
521 b: union { c: u16 align(1) },
522 };
523 const init: T = .{ .a = 0, .b = .{ .c = ~@as(u16, 0) } };
524 var t: T = init; // copy constant aggregate
525 _ = &t;
526 try expect(t.b.c == ~@as(u16, 0));
527 }
528 };
529
530 try S.doTheTest();
531 try comptime S.doTheTest();
532}
533
498534test "align(N) on functions" {
499535 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
500536 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/union.zig+16
......@@ -1162,6 +1162,22 @@ test "extern union most-aligned field is smaller" {
11621162 try expect(a != null);
11631163}
11641164
1165test "union constant trailing padding" {
1166 const S = struct {
1167 fn doTheTest() !void {
1168 const U = extern union { x: u32, y: [5]u8 };
1169 var u = [2]U{ .{ .x = 0 }, .{ .y = @splat(0xff) } };
1170 _ = &u;
1171 try expect(u[0].x == 0);
1172 try expect(u[1].y[0] == 0xff);
1173 try expect(u[1].y[4] == 0xff);
1174 }
1175 };
1176
1177 try S.doTheTest();
1178 try comptime S.doTheTest();
1179}
1180
11651181test "return an extern union from C calling convention" {
11661182 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11671183 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO