authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-10-20 06:00:00+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-21 00:47:14+02:00
log427ddef0e84384c075dd01e8f1b512922c814a1c
tree554d2309ba5436d392bd25a2245bf015d3a5eae1
parentc8bb44618a9c3c7bfff4a9f672a451fdeda1bb02

llvm: fix lowerType bug on struct/union with under aligned field


2 files changed, 48 insertions(+), 4 deletions(-)

src/codegen/llvm.zig+12-4
...@@ -2893,8 +2893,8 @@ pub const Object = struct {...@@ -2893,8 +2893,8 @@ pub const Object = struct {
2893 o.gpa,2893 o.gpa,
2894 try o.builder.arrayType(padding_len, .i8),2894 try o.builder.arrayType(padding_len, .i8),
2895 );2895 );
2896 if (@ctz(offset) < max_field_ty_align.toLog2Units()) {2896 if (struct_type.alignment.compareStrict(.lt, max_field_ty_align)) {
2897 struct_kind = .@"packed"; // prevent unexpected trailing padding2897 struct_kind = .@"packed"; // prevent wrong alignment
2898 }2898 }
2899 }2899 }
29002900
...@@ -2973,6 +2973,14 @@ pub const Object = struct {...@@ -2973,6 +2973,14 @@ pub const Object = struct {
2973 const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);2973 const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);
2974 const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr);2974 const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr);
29752975
2976 const struct_kind: Builder.Type.Structure.Kind = for (union_obj.field_types.get(ip)) |field_ty_ip| {
2977 const field_ty = Type.fromInterned(field_ty_ip);
2978 if (!field_ty.hasRuntimeBits(zcu)) continue;
2979 if (layout.payload_align.compareStrict(.lt, field_ty.abiAlignment(zcu))) {
2980 break .@"packed"; // prevent wrong alignment
2981 }
2982 } else .normal;
2983
2976 const payload_ty = ty: {2984 const payload_ty = ty: {
2977 if (layout.most_aligned_field_size == layout.payload_size) {2985 if (layout.most_aligned_field_size == layout.payload_size) {
2978 break :ty aligned_field_llvm_ty;2986 break :ty aligned_field_llvm_ty;
...@@ -2993,7 +3001,7 @@ pub const Object = struct {...@@ -2993,7 +3001,7 @@ pub const Object = struct {
29933001
2994 o.builder.namedTypeSetBody(3002 o.builder.namedTypeSetBody(
2995 ty,3003 ty,
2996 try o.builder.structType(.normal, &.{payload_ty}),3004 try o.builder.structType(struct_kind, &.{payload_ty}),
2997 );3005 );
2998 return ty;3006 return ty;
2999 }3007 }
...@@ -3021,7 +3029,7 @@ pub const Object = struct {...@@ -3021,7 +3029,7 @@ pub const Object = struct {
30213029
3022 o.builder.namedTypeSetBody(3030 o.builder.namedTypeSetBody(
3023 ty,3031 ty,
3024 try o.builder.structType(.normal, llvm_fields[0..llvm_fields_len]),3032 try o.builder.structType(struct_kind, llvm_fields[0..llvm_fields_len]),
3025 );3033 );
3026 return ty;3034 return ty;
3027 },3035 },
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