| author | |
| committer | |
| log | 6e313eb1107d4f5d7b0ada0a67c810ce90e79bf5 |
| tree | 11a0dd0f9dd004e8509321cd8ee757100832d89e |
| parent | c5ba941b77fbdb06841f28142420c6786f2a4d0c |
on x86_64 and similar targets.6 files changed, 70 insertions(+), 28 deletions(-)
lib/std/target.zig+4-3| ... | @@ -1806,9 +1806,9 @@ pub const Target = struct { | ... | @@ -1806,9 +1806,9 @@ pub const Target = struct { |
| 1806 | else => 4, | 1806 | else => 4, |
| 1807 | }, | 1807 | }, |
| 1808 | 1808 | ||
| 1809 | // For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 | 1809 | // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 |
| 1810 | // is a better number for two reasons: | 1810 | // is a relevant number in three cases: |
| 1811 | // 1. Better machine code when loading into SIMD register. | 1811 | // 1. Different machine code instruction when loading into SIMD register. |
| 1812 | // 2. The C ABI wants 16 for extern structs. | 1812 | // 2. The C ABI wants 16 for extern structs. |
| 1813 | // 3. 16-byte cmpxchg needs 16-byte alignment. | 1813 | // 3. 16-byte cmpxchg needs 16-byte alignment. |
| 1814 | // Same logic for riscv64, powerpc64, mips64, sparc64. | 1814 | // Same logic for riscv64, powerpc64, mips64, sparc64. |
| ... | @@ -1819,6 +1819,7 @@ pub const Target = struct { | ... | @@ -1819,6 +1819,7 @@ pub const Target = struct { |
| 1819 | .mips64, | 1819 | .mips64, |
| 1820 | .mips64el, | 1820 | .mips64el, |
| 1821 | .sparc64, | 1821 | .sparc64, |
| 1822 | => 8, | ||
| 1822 | 1823 | ||
| 1823 | // Even LLVMABIAlignmentOfType(i128) agrees on these targets. | 1824 | // Even LLVMABIAlignmentOfType(i128) agrees on these targets. |
| 1824 | .aarch64, | 1825 | .aarch64, |
src/Module.zig+25-5| ... | @@ -935,13 +935,33 @@ pub const Struct = struct { | ... | @@ -935,13 +935,33 @@ pub const Struct = struct { |
| 935 | /// If true then `default_val` is the comptime field value. | 935 | /// If true then `default_val` is the comptime field value. |
| 936 | is_comptime: bool, | 936 | is_comptime: bool, |
| 937 | 937 | ||
| 938 | /// Returns the field alignment, assuming the struct is not packed. | 938 | /// Returns the field alignment. If the struct is packed, returns 0. |
| 939 | pub fn normalAlignment(field: Field, target: Target) u32 { | 939 | pub fn alignment( |
| 940 | if (field.abi_align == 0) { | 940 | field: Field, |
| 941 | return field.ty.abiAlignment(target); | 941 | target: Target, |
| 942 | } else { | 942 | layout: std.builtin.Type.ContainerLayout, |
| 943 | ) u32 { | ||
| 944 | if (field.abi_align != 0) { | ||
| 945 | assert(layout != .Packed); | ||
| 943 | return field.abi_align; | 946 | return field.abi_align; |
| 944 | } | 947 | } |
| 948 | |||
| 949 | switch (layout) { | ||
| 950 | .Packed => return 0, | ||
| 951 | .Auto => return field.ty.abiAlignment(target), | ||
| 952 | .Extern => { | ||
| 953 | // This logic is duplicated in Type.abiAlignmentAdvanced. | ||
| 954 | const ty_abi_align = field.ty.abiAlignment(target); | ||
| 955 | |||
| 956 | if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { | ||
| 957 | // The C ABI requires 128 bit integer fields of structs | ||
| 958 | // to be 16-bytes aligned. | ||
| 959 | return @maximum(ty_abi_align, 16); | ||
| 960 | } | ||
| 961 | |||
| 962 | return ty_abi_align; | ||
| 963 | }, | ||
| 964 | } | ||
| 945 | } | 965 | } |
| 946 | }; | 966 | }; |
| 947 | 967 |
src/Sema.zig+1-4| ... | @@ -14380,10 +14380,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -14380,10 +14380,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 14380 | else | 14380 | else |
| 14381 | field.default_val; | 14381 | field.default_val; |
| 14382 | const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val); | 14382 | const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val); |
| 14383 | const alignment = switch (layout) { | 14383 | const alignment = field.alignment(target, layout); |
| 14384 | .Auto, .Extern => field.normalAlignment(target), | ||
| 14385 | .Packed => 0, | ||
| 14386 | }; | ||
| 14387 | 14384 | ||
| 14388 | struct_field_fields.* = .{ | 14385 | struct_field_fields.* = .{ |
| 14389 | // name: []const u8, | 14386 | // name: []const u8, |
src/codegen/llvm.zig+8-6| ... | @@ -1841,6 +1841,7 @@ pub const Object = struct { | ... | @@ -1841,6 +1841,7 @@ pub const Object = struct { |
| 1841 | } | 1841 | } |
| 1842 | 1842 | ||
| 1843 | const fields = ty.structFields(); | 1843 | const fields = ty.structFields(); |
| 1844 | const layout = ty.containerLayout(); | ||
| 1844 | 1845 | ||
| 1845 | var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; | 1846 | var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; |
| 1846 | defer di_fields.deinit(gpa); | 1847 | defer di_fields.deinit(gpa); |
| ... | @@ -1854,7 +1855,7 @@ pub const Object = struct { | ... | @@ -1854,7 +1855,7 @@ pub const Object = struct { |
| 1854 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; | 1855 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1855 | 1856 | ||
| 1856 | const field_size = field.ty.abiSize(target); | 1857 | const field_size = field.ty.abiSize(target); |
| 1857 | const field_align = field.normalAlignment(target); | 1858 | const field_align = field.alignment(target, layout); |
| 1858 | const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align); | 1859 | const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align); |
| 1859 | offset = field_offset + field_size; | 1860 | offset = field_offset + field_size; |
| 1860 | 1861 | ||
| ... | @@ -2499,7 +2500,7 @@ pub const DeclGen = struct { | ... | @@ -2499,7 +2500,7 @@ pub const DeclGen = struct { |
| 2499 | 2500 | ||
| 2500 | fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { | 2501 | fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { |
| 2501 | const llvm_ty = try lowerTypeInner(dg, t); | 2502 | const llvm_ty = try lowerTypeInner(dg, t); |
| 2502 | if (std.debug.runtime_safety and false) check: { | 2503 | if (std.debug.runtime_safety) check: { |
| 2503 | if (t.zigTypeTag() == .Opaque) break :check; | 2504 | if (t.zigTypeTag() == .Opaque) break :check; |
| 2504 | if (!t.hasRuntimeBits()) break :check; | 2505 | if (!t.hasRuntimeBits()) break :check; |
| 2505 | if (!llvm_ty.isSized().toBool()) break :check; | 2506 | if (!llvm_ty.isSized().toBool()) break :check; |
| ... | @@ -2757,7 +2758,7 @@ pub const DeclGen = struct { | ... | @@ -2757,7 +2758,7 @@ pub const DeclGen = struct { |
| 2757 | for (struct_obj.fields.values()) |field| { | 2758 | for (struct_obj.fields.values()) |field| { |
| 2758 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; | 2759 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2759 | 2760 | ||
| 2760 | const field_align = field.normalAlignment(target); | 2761 | const field_align = field.alignment(target, struct_obj.layout); |
| 2761 | const field_ty_align = field.ty.abiAlignment(target); | 2762 | const field_ty_align = field.ty.abiAlignment(target); |
| 2762 | any_underaligned_fields = any_underaligned_fields or | 2763 | any_underaligned_fields = any_underaligned_fields or |
| 2763 | field_align < field_ty_align; | 2764 | field_align < field_ty_align; |
| ... | @@ -3433,7 +3434,7 @@ pub const DeclGen = struct { | ... | @@ -3433,7 +3434,7 @@ pub const DeclGen = struct { |
| 3433 | for (struct_obj.fields.values()) |field, i| { | 3434 | for (struct_obj.fields.values()) |field, i| { |
| 3434 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; | 3435 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3435 | 3436 | ||
| 3436 | const field_align = field.normalAlignment(target); | 3437 | const field_align = field.alignment(target, struct_obj.layout); |
| 3437 | big_align = @maximum(big_align, field_align); | 3438 | big_align = @maximum(big_align, field_align); |
| 3438 | const prev_offset = offset; | 3439 | const prev_offset = offset; |
| 3439 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | 3440 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); |
| ... | @@ -9376,13 +9377,14 @@ fn llvmFieldIndex( | ... | @@ -9376,13 +9377,14 @@ fn llvmFieldIndex( |
| 9376 | } | 9377 | } |
| 9377 | return null; | 9378 | return null; |
| 9378 | } | 9379 | } |
| 9379 | assert(ty.containerLayout() != .Packed); | 9380 | const layout = ty.containerLayout(); |
| 9381 | assert(layout != .Packed); | ||
| 9380 | 9382 | ||
| 9381 | var llvm_field_index: c_uint = 0; | 9383 | var llvm_field_index: c_uint = 0; |
| 9382 | for (ty.structFields().values()) |field, i| { | 9384 | for (ty.structFields().values()) |field, i| { |
| 9383 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; | 9385 | if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 9384 | 9386 | ||
| 9385 | const field_align = field.normalAlignment(target); | 9387 | const field_align = field.alignment(target, layout); |
| 9386 | big_align = @maximum(big_align, field_align); | 9388 | big_align = @maximum(big_align, field_align); |
| 9387 | const prev_offset = offset; | 9389 | const prev_offset = offset; |
| 9388 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | 9390 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); |
src/type.zig+11-2| ... | @@ -3017,6 +3017,15 @@ pub const Type = extern union { | ... | @@ -3017,6 +3017,15 @@ pub const Type = extern union { |
| 3017 | }, | 3017 | }, |
| 3018 | }; | 3018 | }; |
| 3019 | big_align = @maximum(big_align, field_align); | 3019 | big_align = @maximum(big_align, field_align); |
| 3020 | |||
| 3021 | // This logic is duplicated in Module.Struct.Field.alignment. | ||
| 3022 | if (struct_obj.layout == .Extern) { | ||
| 3023 | if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { | ||
| 3024 | // The C ABI requires 128 bit integer fields of structs | ||
| 3025 | // to be 16-bytes aligned. | ||
| 3026 | big_align = @maximum(big_align, 16); | ||
| 3027 | } | ||
| 3028 | } | ||
| 3020 | } | 3029 | } |
| 3021 | return AbiAlignmentAdvanced{ .scalar = big_align }; | 3030 | return AbiAlignmentAdvanced{ .scalar = big_align }; |
| 3022 | }, | 3031 | }, |
| ... | @@ -5490,7 +5499,7 @@ pub const Type = extern union { | ... | @@ -5490,7 +5499,7 @@ pub const Type = extern union { |
| 5490 | .@"struct" => { | 5499 | .@"struct" => { |
| 5491 | const struct_obj = ty.castTag(.@"struct").?.data; | 5500 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 5492 | assert(struct_obj.layout != .Packed); | 5501 | assert(struct_obj.layout != .Packed); |
| 5493 | return struct_obj.fields.values()[index].normalAlignment(target); | 5502 | return struct_obj.fields.values()[index].alignment(target, struct_obj.layout); |
| 5494 | }, | 5503 | }, |
| 5495 | .@"union", .union_safety_tagged, .union_tagged => { | 5504 | .@"union", .union_safety_tagged, .union_tagged => { |
| 5496 | const union_obj = ty.cast(Payload.Union).?.data; | 5505 | const union_obj = ty.cast(Payload.Union).?.data; |
| ... | @@ -5597,7 +5606,7 @@ pub const Type = extern union { | ... | @@ -5597,7 +5606,7 @@ pub const Type = extern union { |
| 5597 | if (!field.ty.hasRuntimeBits() or field.is_comptime) | 5606 | if (!field.ty.hasRuntimeBits() or field.is_comptime) |
| 5598 | return FieldOffset{ .field = it.field, .offset = it.offset }; | 5607 | return FieldOffset{ .field = it.field, .offset = it.offset }; |
| 5599 | 5608 | ||
| 5600 | const field_align = field.normalAlignment(it.target); | 5609 | const field_align = field.alignment(it.target, it.struct_obj.layout); |
| 5601 | it.big_align = @maximum(it.big_align, field_align); | 5610 | it.big_align = @maximum(it.big_align, field_align); |
| 5602 | it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align); | 5611 | it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align); |
| 5603 | defer it.offset += field.ty.abiSize(it.target); | 5612 | defer it.offset += field.ty.abiSize(it.target); |
test/behavior/align.zig+21-8| ... | @@ -143,6 +143,19 @@ test "alignment and size of structs with 128-bit fields" { | ... | @@ -143,6 +143,19 @@ test "alignment and size of structs with 128-bit fields" { |
| 143 | .riscv64, | 143 | .riscv64, |
| 144 | .sparc64, | 144 | .sparc64, |
| 145 | .x86_64, | 145 | .x86_64, |
| 146 | => .{ | ||
| 147 | .a_align = 8, | ||
| 148 | .a_size = 16, | ||
| 149 | |||
| 150 | .b_align = 16, | ||
| 151 | .b_size = 32, | ||
| 152 | |||
| 153 | .u128_align = 8, | ||
| 154 | .u128_size = 16, | ||
| 155 | .u129_align = 8, | ||
| 156 | .u129_size = 24, | ||
| 157 | }, | ||
| 158 | |||
| 146 | .aarch64, | 159 | .aarch64, |
| 147 | .aarch64_be, | 160 | .aarch64_be, |
| 148 | .aarch64_32, | 161 | .aarch64_32, |
| ... | @@ -166,17 +179,17 @@ test "alignment and size of structs with 128-bit fields" { | ... | @@ -166,17 +179,17 @@ test "alignment and size of structs with 128-bit fields" { |
| 166 | else => return error.SkipZigTest, | 179 | else => return error.SkipZigTest, |
| 167 | }; | 180 | }; |
| 168 | comptime { | 181 | comptime { |
| 169 | std.debug.assert(@alignOf(A) == expected.a_align); | 182 | assert(@alignOf(A) == expected.a_align); |
| 170 | std.debug.assert(@sizeOf(A) == expected.a_size); | 183 | assert(@sizeOf(A) == expected.a_size); |
| 171 | 184 | ||
| 172 | std.debug.assert(@alignOf(B) == expected.b_align); | 185 | assert(@alignOf(B) == expected.b_align); |
| 173 | std.debug.assert(@sizeOf(B) == expected.b_size); | 186 | assert(@sizeOf(B) == expected.b_size); |
| 174 | 187 | ||
| 175 | std.debug.assert(@alignOf(u128) == expected.u128_align); | 188 | assert(@alignOf(u128) == expected.u128_align); |
| 176 | std.debug.assert(@sizeOf(u128) == expected.u128_size); | 189 | assert(@sizeOf(u128) == expected.u128_size); |
| 177 | 190 | ||
| 178 | std.debug.assert(@alignOf(u129) == expected.u129_align); | 191 | assert(@alignOf(u129) == expected.u129_align); |
| 179 | std.debug.assert(@sizeOf(u129) == expected.u129_size); | 192 | assert(@sizeOf(u129) == expected.u129_size); |
| 180 | } | 193 | } |
| 181 | } | 194 | } |
| 182 | 195 |