Commit f550c29c4e

Andrew Kelley <andrew@ziglang.org>
2022-07-22 01:50:06
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
1 parent 4602114
Changed files (4)
lib
std
src
codegen
test
behavior
lib/std/os/linux.zig
@@ -3236,7 +3236,10 @@ pub const epoll_event = switch (builtin.zig_backend) {
     },
     else => extern struct {
         events: u32,
-        data: epoll_data align(4),
+        data: epoll_data align(switch (native_arch) {
+            .x86_64 => 4,
+            else => @alignOf(epoll_data),
+        }),
     },
 };
 
lib/std/x/os/io.zig
@@ -117,7 +117,6 @@ pub const Reactor = struct {
 };
 
 test "reactor/linux: drive async tcp client/listener pair" {
-    if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest;
     if (native_os.tag != .linux) return error.SkipZigTest;
 
     const ip = std.x.net.ip;
src/codegen/llvm.zig
@@ -2680,11 +2680,15 @@ pub const DeclGen = struct {
                 comptime assert(struct_layout_version == 2);
                 var offset: u64 = 0;
                 var big_align: u32 = 0;
+                var any_underaligned_fields = false;
 
                 for (struct_obj.fields.values()) |field| {
                     if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue;
 
                     const field_align = field.normalAlignment(target);
+                    const field_ty_align = field.ty.abiAlignment(target);
+                    any_underaligned_fields = any_underaligned_fields or
+                        field_align < field_ty_align;
                     big_align = @maximum(big_align, field_align);
                     const prev_offset = offset;
                     offset = std.mem.alignForwardGeneric(u64, offset, field_align);
@@ -2712,7 +2716,7 @@ pub const DeclGen = struct {
                 llvm_struct_ty.structSetBody(
                     llvm_field_types.items.ptr,
                     @intCast(c_uint, llvm_field_types.items.len),
-                    .False,
+                    llvm.Bool.fromBool(any_underaligned_fields),
                 );
 
                 return llvm_struct_ty;
test/behavior/struct.zig
@@ -1372,3 +1372,26 @@ test "struct field init value is size of the struct" {
     var s: namespace.S = .{ .blah = 1234 };
     try expect(s.size == 4);
 }
+
+test "under-aligned struct field" {
+    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const U = extern union {
+        fd: i32,
+        u32: u32,
+        u64: u64,
+    };
+    const S = extern struct {
+        events: u32,
+        data: U align(4),
+    };
+    var runtime: usize = 1234;
+    const ptr = &S{ .events = 0, .data = .{ .u64 = runtime } };
+    const array = @ptrCast(*const [12]u8, ptr);
+    const result = std.mem.readIntNative(u64, array[4..12]);
+    try expect(result == 1234);
+}