authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-21 16:50:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-21 22:51:17-07:00
logf550c29c4e76ccfbdbc8ec2159cf90474530a24c
tree66b810de7b303ce71bb7eb538cc8a2b497c11d72
parent460211431f407c9f707e3ac3bbff61610a487926

LLVM: fix lowering of structs with underaligned fields

When lowering a struct type to an LLVM struct type, keep track of whether there are any underaligned fields. If so, then make it a packed llvm struct. This works because we already insert manual padding bytes regardless. We could unconditionally use an LLVM packed struct; the reason we bother checking for underaligned fields is that it is a conservative choice, in case LLVM handles packed structs less optimally. A future improvement could simplify this code by unconditionally using packed LLVM structs and then make sure measure perf is unaffected. closes #12190

4 files changed, 32 insertions(+), 3 deletions(-)

lib/std/os/linux.zig+4-1
......@@ -3236,7 +3236,10 @@ pub const epoll_event = switch (builtin.zig_backend) {
32363236 },
32373237 else => extern struct {
32383238 events: u32,
3239 data: epoll_data align(4),
3239 data: epoll_data align(switch (native_arch) {
3240 .x86_64 => 4,
3241 else => @alignOf(epoll_data),
3242 }),
32403243 },
32413244};
32423245
lib/std/x/os/io.zig-1
......@@ -117,7 +117,6 @@ pub const Reactor = struct {
117117};
118118
119119test "reactor/linux: drive async tcp client/listener pair" {
120 if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest;
121120 if (native_os.tag != .linux) return error.SkipZigTest;
122121
123122 const ip = std.x.net.ip;
src/codegen/llvm.zig+5-1
......@@ -2680,11 +2680,15 @@ pub const DeclGen = struct {
26802680 comptime assert(struct_layout_version == 2);
26812681 var offset: u64 = 0;
26822682 var big_align: u32 = 0;
2683 var any_underaligned_fields = false;
26832684
26842685 for (struct_obj.fields.values()) |field| {
26852686 if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
26862687
26872688 const field_align = field.normalAlignment(target);
2689 const field_ty_align = field.ty.abiAlignment(target);
2690 any_underaligned_fields = any_underaligned_fields or
2691 field_align < field_ty_align;
26882692 big_align = @maximum(big_align, field_align);
26892693 const prev_offset = offset;
26902694 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
......@@ -2712,7 +2716,7 @@ pub const DeclGen = struct {
27122716 llvm_struct_ty.structSetBody(
27132717 llvm_field_types.items.ptr,
27142718 @intCast(c_uint, llvm_field_types.items.len),
2715 .False,
2719 llvm.Bool.fromBool(any_underaligned_fields),
27162720 );
27172721
27182722 return llvm_struct_ty;
test/behavior/struct.zig+23
......@@ -1372,3 +1372,26 @@ test "struct field init value is size of the struct" {
13721372 var s: namespace.S = .{ .blah = 1234 };
13731373 try expect(s.size == 4);
13741374}
1375
1376test "under-aligned struct field" {
1377 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1378 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1379 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1380 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1381 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1382
1383 const U = extern union {
1384 fd: i32,
1385 u32: u32,
1386 u64: u64,
1387 };
1388 const S = extern struct {
1389 events: u32,
1390 data: U align(4),
1391 };
1392 var runtime: usize = 1234;
1393 const ptr = &S{ .events = 0, .data = .{ .u64 = runtime } };
1394 const array = @ptrCast(*const [12]u8, ptr);
1395 const result = std.mem.readIntNative(u64, array[4..12]);
1396 try expect(result == 1234);
1397}