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(...@@ -617,6 +617,7 @@ pub fn generateSymbol(
617 else => unreachable,617 else => unreachable,
618 },618 },
619 .un => |un| {619 .un => |un| {
620 const union_begin = w.end;
620 const layout = ty.unionGetLayout(zcu);621 const layout = ty.unionGetLayout(zcu);
621622
622 if (layout.payload_size == 0) {623 if (layout.payload_size == 0) {
...@@ -648,11 +649,10 @@ pub fn generateSymbol(...@@ -648,11 +649,10 @@ pub fn generateSymbol(
648649
649 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {650 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
650 try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent);651 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 }
655 }652 }
653
654 const padding: usize = @intCast(union_begin + layout.abi_size - w.end);
655 if (padding > 0) try w.splatByteAll(0, padding);
656 },656 },
657 .bitpack => |bitpack| try generateSymbol(bin_file, pt, .fromInterned(bitpack.backing_int_val), w, reloc_parent),657 .bitpack => |bitpack| try generateSymbol(bin_file, pt, .fromInterned(bitpack.backing_int_val), w, reloc_parent),
658 .memoized_call => unreachable,658 .memoized_call => unreachable,
src/codegen/llvm.zig+21-11
...@@ -2898,8 +2898,8 @@ pub const Object = struct {...@@ -2898,8 +2898,8 @@ pub const Object = struct {
2898 o.gpa,2898 o.gpa,
2899 try o.builder.arrayType(padding_len, .i8),2899 try o.builder.arrayType(padding_len, .i8),
2900 );2900 );
2901 if (@ctz(offset) < max_field_ty_align.toLog2Units()) {2901 if (struct_type.alignment.compareStrict(.lt, max_field_ty_align)) {
2902 struct_kind = .@"packed"; // prevent unexpected trailing padding2902 struct_kind = .@"packed"; // prevent wrong alignment
2903 }2903 }
2904 }2904 }
29052905
...@@ -2978,14 +2978,22 @@ pub const Object = struct {...@@ -2978,14 +2978,22 @@ pub const Object = struct {
2978 const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);2978 const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);
2979 const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr);2979 const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr);
29802980
2981 const payload_ty = ty: {2981 const struct_kind: Builder.Type.Structure.Kind = for (union_obj.field_types.get(ip)) |field_ty_ip| {
2982 if (layout.most_aligned_field_size == layout.payload_size) {2982 const field_ty = Type.fromInterned(field_ty_ip);
2983 break :ty aligned_field_llvm_ty;2983 if (!field_ty.hasRuntimeBits(zcu)) continue;
2984 if (layout.payload_align.compareStrict(.lt, field_ty.abiAlignment(zcu))) {
2985 break .@"packed"; // prevent wrong alignment
2984 }2986 }
2987 } else .normal;
2988
2989 const payload_ty = ty: {
2985 const padding_len = if (layout.tag_size == 0)2990 const padding_len = if (layout.tag_size == 0)
2986 layout.abi_size - layout.most_aligned_field_size2991 layout.abi_size - layout.most_aligned_field_size
2987 else2992 else
2988 layout.payload_size - layout.most_aligned_field_size;2993 layout.payload_size - layout.most_aligned_field_size;
2994 if (padding_len == 0) {
2995 break :ty aligned_field_llvm_ty;
2996 }
2989 break :ty try o.builder.structType(.@"packed", &.{2997 break :ty try o.builder.structType(.@"packed", &.{
2990 aligned_field_llvm_ty,2998 aligned_field_llvm_ty,
2991 try o.builder.arrayType(padding_len, .i8),2999 try o.builder.arrayType(padding_len, .i8),
...@@ -2998,7 +3006,7 @@ pub const Object = struct {...@@ -2998,7 +3006,7 @@ pub const Object = struct {
29983006
2999 o.builder.namedTypeSetBody(3007 o.builder.namedTypeSetBody(
3000 ty,3008 ty,
3001 try o.builder.structType(.normal, &.{payload_ty}),3009 try o.builder.structType(struct_kind, &.{payload_ty}),
3002 );3010 );
3003 return ty;3011 return ty;
3004 }3012 }
...@@ -3026,7 +3034,7 @@ pub const Object = struct {...@@ -3026,7 +3034,7 @@ pub const Object = struct {
30263034
3027 o.builder.namedTypeSetBody(3035 o.builder.namedTypeSetBody(
3028 ty,3036 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]),
3030 );3038 );
3031 return ty;3039 return ty;
3032 },3040 },
...@@ -3579,6 +3587,11 @@ pub const Object = struct {...@@ -3579,6 +3587,11 @@ pub const Object = struct {
3579 const payload = if (un.tag != .none) p: {3587 const payload = if (un.tag != .none) p: {
3580 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;3588 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;
3581 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);3589 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
3583 // Sometimes we must make an unnamed struct because LLVM does3596 // Sometimes we must make an unnamed struct because LLVM does
3584 // not support bitcasting our payload struct to the true union payload type.3597 // not support bitcasting our payload struct to the true union payload type.
...@@ -3587,7 +3600,6 @@ pub const Object = struct {...@@ -3587,7 +3600,6 @@ pub const Object = struct {
3587 need_unnamed = layout.most_aligned_field != field_index;3600 need_unnamed = layout.most_aligned_field != field_index;
35883601
3589 if (!field_ty.hasRuntimeBits(zcu)) {3602 if (!field_ty.hasRuntimeBits(zcu)) {
3590 const padding_len = layout.payload_size;
3591 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));3603 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));
3592 }3604 }
3593 const payload = try o.lowerValue(un.val, repr);3605 const payload = try o.lowerValue(un.val, repr);
...@@ -3595,9 +3607,7 @@ pub const Object = struct {...@@ -3595,9 +3607,7 @@ pub const Object = struct {
3595 if (payload_ty != union_ty.structFields(&o.builder)[3607 if (payload_ty != union_ty.structFields(&o.builder)[
3596 @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align))3608 @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align))
3597 ]) need_unnamed = true;3609 ]) need_unnamed = true;
3598 const field_size = field_ty.abiSize(zcu);3610 if (padding_len == 0) break :p payload;
3599 if (field_size == layout.payload_size) break :p payload;
3600 const padding_len = layout.payload_size - field_size;
3601 const padding_ty = try o.builder.arrayType(padding_len, .i8);3611 const padding_ty = try o.builder.arrayType(padding_len, .i8);
3602 break :p try o.builder.structConst(3612 break :p try o.builder.structConst(
3603 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),3613 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
test/behavior/align.zig+36
...@@ -495,6 +495,42 @@ test "struct field explicit alignment" {...@@ -495,6 +495,42 @@ test "struct field explicit alignment" {
495 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);495 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
496}496}
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
498test "align(N) on functions" {534test "align(N) on functions" {
499 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO535 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO536 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" {...@@ -1162,6 +1162,22 @@ test "extern union most-aligned field is smaller" {
1162 try expect(a != null);1162 try expect(a != null);
1163}1163}
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
1165test "return an extern union from C calling convention" {1181test "return an extern union from C calling convention" {
1166 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1182 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1167 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1183 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO