Commit 6b8e33d14c

Andrew Kelley <andrew@ziglang.org>
2021-12-24 10:37:54
stage2: LLVM: fix lowering of packed structs
* ensure enough capacity when building the LLVM type and value. * add explicit padding field and populate it to ensure proper alignment.
1 parent 5b171f4
Changed files (2)
src
src/codegen/llvm/bindings.zig
@@ -257,6 +257,9 @@ pub const Type = opaque {
 
     pub const getElementType = LLVMGetElementType;
     extern fn LLVMGetElementType(Ty: *const Type) *const Type;
+
+    pub const countStructElementTypes = LLVMCountStructElementTypes;
+    extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;
 };
 
 pub const Module = opaque {
src/codegen/llvm.zig
@@ -845,9 +845,11 @@ pub const DeclGen = struct {
                 defer llvm_field_types.deinit(gpa);
 
                 if (struct_obj.layout == .Packed) {
+                    try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2);
                     const target = dg.module.getTarget();
                     comptime assert(Type.packed_struct_layout_version == 1);
                     var offset: u64 = 0;
+                    var big_align: u32 = 0;
                     var running_bits: u16 = 0;
                     for (struct_obj.fields.values()) |field| {
                         if (!field.ty.hasCodeGenBits()) continue;
@@ -863,6 +865,7 @@ pub const DeclGen = struct {
                                 };
                                 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
                                 const int_align = int_ty.abiAlignment(target);
+                                big_align = @maximum(big_align, int_align);
                                 const llvm_int_ty = try dg.llvmType(int_ty);
                                 const prev_offset = offset;
                                 offset = std.mem.alignForwardGeneric(u64, offset, int_align);
@@ -875,6 +878,7 @@ pub const DeclGen = struct {
                                 offset += int_ty.abiSize(target);
                                 running_bits = 0;
                             }
+                            big_align = @maximum(big_align, field_align);
                             const prev_offset = offset;
                             offset = std.mem.alignForwardGeneric(u64, offset, field_align);
                             const padding_bytes = @intCast(c_uint, offset - prev_offset);
@@ -894,6 +898,7 @@ pub const DeclGen = struct {
                         };
                         const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
                         const int_align = int_ty.abiAlignment(target);
+                        big_align = @maximum(big_align, int_align);
                         const prev_offset = offset;
                         offset = std.mem.alignForwardGeneric(u64, offset, int_align);
                         const padding_bytes = @intCast(c_uint, offset - prev_offset);
@@ -904,6 +909,14 @@ pub const DeclGen = struct {
                         const llvm_int_ty = try dg.llvmType(int_ty);
                         llvm_field_types.appendAssumeCapacity(llvm_int_ty);
                     }
+
+                    const prev_offset = offset;
+                    offset = std.mem.alignForwardGeneric(u64, offset, big_align);
+                    const padding_bytes = @intCast(c_uint, offset - prev_offset);
+                    if (padding_bytes != 0) {
+                        const padding = dg.context.intType(8).arrayType(padding_bytes);
+                        llvm_field_types.appendAssumeCapacity(padding);
+                    }
                 } else {
                     for (struct_obj.fields.values()) |field| {
                         if (!field.ty.hasCodeGenBits()) continue;
@@ -1319,8 +1332,9 @@ pub const DeclGen = struct {
                 const llvm_struct_ty = try dg.llvmType(tv.ty);
                 const field_vals = tv.val.castTag(.@"struct").?.data;
                 const gpa = dg.gpa;
+                const llvm_field_count = llvm_struct_ty.countStructElementTypes();
 
-                var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, field_vals.len);
+                var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);
                 defer llvm_fields.deinit(gpa);
 
                 const struct_obj = tv.ty.castTag(.@"struct").?.data;
@@ -1329,6 +1343,7 @@ pub const DeclGen = struct {
                     const fields = struct_obj.fields.values();
                     comptime assert(Type.packed_struct_layout_version == 1);
                     var offset: u64 = 0;
+                    var big_align: u32 = 0;
                     var running_bits: u16 = 0;
                     var running_int: *const llvm.Value = llvm_struct_ty.structGetTypeAtIndex(0).constNull();
                     for (field_vals) |field_val, i| {
@@ -1349,6 +1364,7 @@ pub const DeclGen = struct {
                             running_int = running_int.constOr(shifted);
                             running_bits += ty_bit_size;
                         } else {
+                            big_align = @maximum(big_align, field_align);
                             if (running_bits != 0) {
                                 var int_payload: Type.Payload.Bits = .{
                                     .base = .{ .tag = .int_unsigned },
@@ -1356,6 +1372,7 @@ pub const DeclGen = struct {
                                 };
                                 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
                                 const int_align = int_ty.abiAlignment(target);
+                                big_align = @maximum(big_align, int_align);
                                 const prev_offset = offset;
                                 offset = std.mem.alignForwardGeneric(u64, offset, int_align);
                                 const padding_bytes = @intCast(c_uint, offset - prev_offset);
@@ -1382,6 +1399,32 @@ pub const DeclGen = struct {
                             offset += field.ty.abiSize(target);
                         }
                     }
+                    if (running_bits != 0) {
+                        var int_payload: Type.Payload.Bits = .{
+                            .base = .{ .tag = .int_unsigned },
+                            .data = running_bits,
+                        };
+                        const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
+                        const int_align = int_ty.abiAlignment(target);
+                        big_align = @maximum(big_align, int_align);
+                        const prev_offset = offset;
+                        offset = std.mem.alignForwardGeneric(u64, offset, int_align);
+                        const padding_bytes = @intCast(c_uint, offset - prev_offset);
+                        if (padding_bytes != 0) {
+                            const padding = dg.context.intType(8).arrayType(padding_bytes);
+                            llvm_fields.appendAssumeCapacity(padding.getUndef());
+                        }
+                        llvm_fields.appendAssumeCapacity(running_int);
+                        offset += int_ty.abiSize(target);
+                    }
+
+                    const prev_offset = offset;
+                    offset = std.mem.alignForwardGeneric(u64, offset, big_align);
+                    const padding_bytes = @intCast(c_uint, offset - prev_offset);
+                    if (padding_bytes != 0) {
+                        const padding = dg.context.intType(8).arrayType(padding_bytes);
+                        llvm_fields.appendAssumeCapacity(padding.getUndef());
+                    }
                 } else {
                     for (field_vals) |field_val, i| {
                         const field_ty = tv.ty.structFieldType(i);