authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-10-20 22:03:13+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-21 00:47:14+02:00
logf356324325dc3637b93859d7b53f98f4e66009f5
treeb108392beeb8578f0075c9bad76f81ee88aabfb5
parent427ddef0e84384c075dd01e8f1b512922c814a1c

codegen: fix union padding


3 files changed, 29 insertions(+), 11 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+9-7
...@@ -2982,13 +2982,13 @@ pub const Object = struct {...@@ -2982,13 +2982,13 @@ pub const Object = struct {
2982 } else .normal;2982 } else .normal;
29832983
2984 const payload_ty = ty: {2984 const payload_ty = ty: {
2985 if (layout.most_aligned_field_size == layout.payload_size) {
2986 break :ty aligned_field_llvm_ty;
2987 }
2988 const padding_len = if (layout.tag_size == 0)2985 const padding_len = if (layout.tag_size == 0)
2989 layout.abi_size - layout.most_aligned_field_size2986 layout.abi_size - layout.most_aligned_field_size
2990 else2987 else
2991 layout.payload_size - layout.most_aligned_field_size;2988 layout.payload_size - layout.most_aligned_field_size;
2989 if (padding_len == 0) {
2990 break :ty aligned_field_llvm_ty;
2991 }
2992 break :ty try o.builder.structType(.@"packed", &.{2992 break :ty try o.builder.structType(.@"packed", &.{
2993 aligned_field_llvm_ty,2993 aligned_field_llvm_ty,
2994 try o.builder.arrayType(padding_len, .i8),2994 try o.builder.arrayType(padding_len, .i8),
...@@ -3582,6 +3582,11 @@ pub const Object = struct {...@@ -3582,6 +3582,11 @@ pub const Object = struct {
3582 const payload = if (un.tag != .none) p: {3582 const payload = if (un.tag != .none) p: {
3583 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;3583 const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?;
3584 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);3584 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
3585 const field_size = field_ty.abiSize(zcu);
3586 const padding_len = if (layout.tag_size == 0)
3587 layout.abi_size - field_size
3588 else
3589 layout.payload_size - field_size;
35853590
3586 // Sometimes we must make an unnamed struct because LLVM does3591 // Sometimes we must make an unnamed struct because LLVM does
3587 // not support bitcasting our payload struct to the true union payload type.3592 // not support bitcasting our payload struct to the true union payload type.
...@@ -3590,7 +3595,6 @@ pub const Object = struct {...@@ -3590,7 +3595,6 @@ pub const Object = struct {
3590 need_unnamed = layout.most_aligned_field != field_index;3595 need_unnamed = layout.most_aligned_field != field_index;
35913596
3592 if (!field_ty.hasRuntimeBits(zcu)) {3597 if (!field_ty.hasRuntimeBits(zcu)) {
3593 const padding_len = layout.payload_size;
3594 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));3598 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));
3595 }3599 }
3596 const payload = try o.lowerValue(un.val, repr);3600 const payload = try o.lowerValue(un.val, repr);
...@@ -3598,9 +3602,7 @@ pub const Object = struct {...@@ -3598,9 +3602,7 @@ pub const Object = struct {
3598 if (payload_ty != union_ty.structFields(&o.builder)[3602 if (payload_ty != union_ty.structFields(&o.builder)[
3599 @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align))3603 @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align))
3600 ]) need_unnamed = true;3604 ]) need_unnamed = true;
3601 const field_size = field_ty.abiSize(zcu);3605 if (padding_len == 0) break :p payload;
3602 if (field_size == layout.payload_size) break :p payload;
3603 const padding_len = layout.payload_size - field_size;
3604 const padding_ty = try o.builder.arrayType(padding_len, .i8);3606 const padding_ty = try o.builder.arrayType(padding_len, .i8);
3605 break :p try o.builder.structConst(3607 break :p try o.builder.structConst(
3606 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),3608 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
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