authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-03 23:44:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 17:34:16-07:00
log259f784241fb44e0a1b570daaf31ba2b9f164106
treeff024852310df27dfad242c12af8d8a77a290988
parent080e870a717124204cbfa58a1f29061a701b0362

stage2: improve `@sizeOf` and `@alignOf` integers

Prior to this commit, the logic for ABI size and ABI alignment for integers was naive and incorrect. This results in wasted hardware as well as undefined behavior in the LLVM backend when we memset an incorrect number of bytes to 0xaa due to disagreeing with LLVM about the ABI size of integers. This commit introduces a "max int align" value which is different per Target. This value is used to derive the ABI size and alignment of all integers. This commit makes an interesting change from stage1, which treats 128-bit integers as 16-bytes aligned for x86_64-linux. stage1 is incorrect. The maximum integer alignment on this system is only 8 bytes. This change breaks the behavior test called "128-bit cmpxchg" because on that target, 128-bit cmpxchg does require a 16-bytes aligned pointer to a 128 bit integer. However, this alignment property does not belong on *all* 128 bit integers - only on the pointer type in the `@cmpxchg` builtin function prototype. The user can then use an alignment override annotation on a 128-bit integer variable or struct field to obtain such a pointer.

6 files changed, 212 insertions(+), 61 deletions(-)

lib/std/target.zig+77
...@@ -1773,6 +1773,83 @@ pub const Target = struct {...@@ -1773,6 +1773,83 @@ pub const Target = struct {
1773 else => false,1773 else => false,
1774 };1774 };
1775 }1775 }
1776
1777 pub inline fn maxIntAlignment(target: Target) u16 {
1778 return switch (target.cpu.arch) {
1779 .avr => 1,
1780 .msp430 => 2,
1781 .xcore => 4,
1782
1783 .arm,
1784 .armeb,
1785 .thumb,
1786 .thumbeb,
1787 .x86_64,
1788 .hexagon,
1789 .mips,
1790 .mipsel,
1791 .mips64,
1792 .mips64el,
1793 .powerpc,
1794 .powerpcle,
1795 .powerpc64,
1796 .powerpc64le,
1797 .r600,
1798 .amdgcn,
1799 .riscv32,
1800 .riscv64,
1801 .sparc,
1802 .sparcv9,
1803 .sparcel,
1804 .s390x,
1805 .lanai,
1806 .wasm32,
1807 .wasm64,
1808 => 8,
1809
1810 .i386 => return switch (target.os.tag) {
1811 .windows => 8,
1812 else => 4,
1813 },
1814 .aarch64,
1815 .aarch64_be,
1816 .aarch64_32,
1817 .bpfel,
1818 .bpfeb,
1819 .nvptx,
1820 .nvptx64,
1821 => 16,
1822
1823 // Below this comment are unverified and I have chosen a number
1824 // based on ptrBitWidth.
1825
1826 .spu_2 => 2,
1827
1828 .csky,
1829 .arc,
1830 .m68k,
1831 .tce,
1832 .tcele,
1833 .le32,
1834 .amdil,
1835 .hsail,
1836 .spir,
1837 .kalimba,
1838 .renderscript32,
1839 .spirv32,
1840 .shave,
1841 => 4,
1842
1843 .le64,
1844 .amdil64,
1845 .hsail64,
1846 .spir64,
1847 .renderscript64,
1848 .ve,
1849 .spirv64,
1850 => 8,
1851 };
1852 }
1776};1853};
17771854
1778test {1855test {
src/codegen/llvm.zig+1-1
...@@ -7583,7 +7583,7 @@ pub const FuncGen = struct {...@@ -7583,7 +7583,7 @@ pub const FuncGen = struct {
7583 const size_bytes = elem_ty.abiSize(target);7583 const size_bytes = elem_ty.abiSize(target);
7584 _ = self.builder.buildMemCpy(7584 _ = self.builder.buildMemCpy(
7585 self.builder.buildBitCast(ptr, llvm_ptr_u8, ""),7585 self.builder.buildBitCast(ptr, llvm_ptr_u8, ""),
7586 ptr_ty.ptrAlignment(target),7586 ptr_alignment,
7587 self.builder.buildBitCast(elem, llvm_ptr_u8, ""),7587 self.builder.buildBitCast(elem, llvm_ptr_u8, ""),
7588 elem_ty.abiAlignment(target),7588 elem_ty.abiAlignment(target),
7589 self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False),7589 self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False),
src/type.zig+23-15
...@@ -2788,11 +2788,6 @@ pub const Type = extern union {...@@ -2788,11 +2788,6 @@ pub const Type = extern union {
2788 return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) };2788 return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) };
2789 },2789 },
27902790
2791 .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = 2 },
2792 .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = 4 },
2793 .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = 8 },
2794 .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = 16 },
2795
2796 .isize,2791 .isize,
2797 .usize,2792 .usize,
2798 .single_const_pointer_to_comptime_int,2793 .single_const_pointer_to_comptime_int,
...@@ -2865,14 +2860,15 @@ pub const Type = extern union {...@@ -2865,14 +2860,15 @@ pub const Type = extern union {
2865 // ABI alignment of vectors?2860 // ABI alignment of vectors?
2866 .vector => return AbiAlignmentAdvanced{ .scalar = 16 },2861 .vector => return AbiAlignmentAdvanced{ .scalar = 16 },
28672862
2863 .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(16, target) },
2864 .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(32, target) },
2865 .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(64, target) },
2866 .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(128, target) },
2867
2868 .int_signed, .int_unsigned => {2868 .int_signed, .int_unsigned => {
2869 const bits: u16 = ty.cast(Payload.Bits).?.data;2869 const bits: u16 = ty.cast(Payload.Bits).?.data;
2870 if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 };2870 if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 };
2871 if (bits <= 8) return AbiAlignmentAdvanced{ .scalar = 1 };2871 return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(bits, target) };
2872 if (bits <= 16) return AbiAlignmentAdvanced{ .scalar = 2 };
2873 if (bits <= 32) return AbiAlignmentAdvanced{ .scalar = 4 };
2874 if (bits <= 64) return AbiAlignmentAdvanced{ .scalar = 8 };
2875 return AbiAlignmentAdvanced{ .scalar = 16 };
2876 },2872 },
28772873
2878 .optional => {2874 .optional => {
...@@ -3113,10 +3109,6 @@ pub const Type = extern union {...@@ -3113,10 +3109,6 @@ pub const Type = extern union {
3113 assert(elem_size >= payload.elem_type.abiAlignment(target));3109 assert(elem_size >= payload.elem_type.abiAlignment(target));
3114 return (payload.len + 1) * elem_size;3110 return (payload.len + 1) * elem_size;
3115 },3111 },
3116 .i16, .u16 => return 2,
3117 .i32, .u32 => return 4,
3118 .i64, .u64 => return 8,
3119 .u128, .i128 => return 16,
31203112
3121 .isize,3113 .isize,
3122 .usize,3114 .usize,
...@@ -3189,10 +3181,14 @@ pub const Type = extern union {...@@ -3189,10 +3181,14 @@ pub const Type = extern union {
3189 .error_set_merged,3181 .error_set_merged,
3190 => return 2, // TODO revisit this when we have the concept of the error tag type3182 => return 2, // TODO revisit this when we have the concept of the error tag type
31913183
3184 .i16, .u16 => return intAbiSize(16, target),
3185 .i32, .u32 => return intAbiSize(32, target),
3186 .i64, .u64 => return intAbiSize(64, target),
3187 .u128, .i128 => return intAbiSize(128, target),
3192 .int_signed, .int_unsigned => {3188 .int_signed, .int_unsigned => {
3193 const bits: u16 = self.cast(Payload.Bits).?.data;3189 const bits: u16 = self.cast(Payload.Bits).?.data;
3194 if (bits == 0) return 0;3190 if (bits == 0) return 0;
3195 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);3191 return intAbiSize(bits, target);
3196 },3192 },
31973193
3198 .optional => {3194 .optional => {
...@@ -3234,6 +3230,18 @@ pub const Type = extern union {...@@ -3234,6 +3230,18 @@ pub const Type = extern union {
3234 };3230 };
3235 }3231 }
32363232
3233 fn intAbiSize(bits: u16, target: Target) u64 {
3234 const alignment = intAbiAlignment(bits, target);
3235 return std.mem.alignForwardGeneric(u64, (bits + 7) / 8, alignment);
3236 }
3237
3238 fn intAbiAlignment(bits: u16, target: Target) u32 {
3239 return @minimum(
3240 std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8),
3241 target.maxIntAlignment(),
3242 );
3243 }
3244
3237 /// Asserts the type has the bit size already resolved.3245 /// Asserts the type has the bit size already resolved.
3238 pub fn bitSize(ty: Type, target: Target) u64 {3246 pub fn bitSize(ty: Type, target: Target) u64 {
3239 return switch (ty.tag()) {3247 return switch (ty.tag()) {
test/behavior/align.zig+105-27
...@@ -47,41 +47,121 @@ fn expects4(x: *align(4) u32) void {...@@ -47,41 +47,121 @@ fn expects4(x: *align(4) u32) void {
47 x.* += 1;47 x.* += 1;
48}48}
4949
50test "alignment of structs" {50test "alignment of struct with pointer has same alignment as usize" {
51 try expect(@alignOf(struct {51 try expect(@alignOf(struct {
52 a: i32,52 a: i32,
53 b: *i32,53 b: *i32,
54 }) == @alignOf(usize));54 }) == @alignOf(usize));
55}55}
5656
57test "alignment of >= 128-bit integer type" {57test "alignment and size of structs with 128-bit fields" {
58 try expect(@alignOf(u128) == 16);58 const A = struct {
59 try expect(@alignOf(u129) == 16);
60}
61
62test "alignment of struct with 128-bit field" {
63 try expect(@alignOf(struct {
64 x: u128,59 x: u128,
65 }) == 16);60 };
6661 const B = extern struct {
67 comptime {
68 try expect(@alignOf(struct {
69 x: u128,
70 }) == 16);
71 }
72}
73
74test "size of extern struct with 128-bit field" {
75 try expect(@sizeOf(extern struct {
76 x: u128,62 x: u128,
77 y: u8,63 y: u8,
78 }) == 32);64 };
7965 const expected = switch (builtin.cpu.arch) {
66 .arm,
67 .armeb,
68 .thumb,
69 .thumbeb,
70 .x86_64,
71 .hexagon,
72 .mips,
73 .mipsel,
74 .mips64,
75 .mips64el,
76 .powerpc,
77 .powerpcle,
78 .powerpc64,
79 .powerpc64le,
80 .r600,
81 .amdgcn,
82 .riscv32,
83 .riscv64,
84 .sparc,
85 .sparcv9,
86 .sparcel,
87 .s390x,
88 .lanai,
89 .wasm32,
90 .wasm64,
91 => .{
92 .a_align = 8,
93 .a_size = 16,
94
95 .b_align = 8,
96 .b_size = 24,
97
98 .u128_align = 8,
99 .u128_size = 16,
100 .u129_align = 8,
101 .u129_size = 24,
102 },
103
104 .i386 => switch (builtin.os.tag) {
105 .windows => .{
106 .a_align = 8,
107 .a_size = 16,
108
109 .b_align = 8,
110 .b_size = 24,
111
112 .u128_align = 8,
113 .u128_size = 16,
114 .u129_align = 8,
115 .u129_size = 24,
116 },
117 else => .{
118 .a_align = 4,
119 .a_size = 16,
120
121 .b_align = 4,
122 .b_size = 20,
123
124 .u128_align = 4,
125 .u128_size = 16,
126 .u129_align = 4,
127 .u129_size = 20,
128 },
129 },
130
131 .aarch64,
132 .aarch64_be,
133 .aarch64_32,
134 .bpfel,
135 .bpfeb,
136 .nvptx,
137 .nvptx64,
138 => .{
139 .a_align = 16,
140 .a_size = 16,
141
142 .b_align = 16,
143 .b_size = 32,
144
145 .u128_align = 16,
146 .u128_size = 16,
147 .u129_align = 16,
148 .u129_size = 32,
149 },
150
151 else => return error.SkipZigTest,
152 };
80 comptime {153 comptime {
81 try expect(@sizeOf(extern struct {154 std.debug.assert(@alignOf(A) == expected.a_align);
82 x: u128,155 std.debug.assert(@sizeOf(A) == expected.a_size);
83 y: u8,156
84 }) == 32);157 std.debug.assert(@alignOf(B) == expected.b_align);
158 std.debug.assert(@sizeOf(B) == expected.b_size);
159
160 std.debug.assert(@alignOf(u128) == expected.u128_align);
161 std.debug.assert(@sizeOf(u128) == expected.u128_size);
162
163 std.debug.assert(@alignOf(u129) == expected.u129_align);
164 std.debug.assert(@sizeOf(u129) == expected.u129_size);
85 }165 }
86}166}
87167
...@@ -328,7 +408,6 @@ test "read 128-bit field from default aligned struct in stack memory" {...@@ -328,7 +408,6 @@ test "read 128-bit field from default aligned struct in stack memory" {
328 .nevermind = 1,408 .nevermind = 1,
329 .badguy = 12,409 .badguy = 12,
330 };410 };
331 try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
332 try expect(12 == default_aligned.badguy);411 try expect(12 == default_aligned.badguy);
333}412}
334413
...@@ -345,7 +424,6 @@ test "read 128-bit field from default aligned struct in global memory" {...@@ -345,7 +424,6 @@ test "read 128-bit field from default aligned struct in global memory" {
345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;424 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
346 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;425 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
347426
348 try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
349 try expect(12 == default_aligned_global.badguy);427 try expect(12 == default_aligned_global.badguy);
350}428}
351429
test/behavior/bitcast.zig+3-12
...@@ -138,18 +138,9 @@ test "@bitCast packed structs at runtime and comptime" {...@@ -138,18 +138,9 @@ test "@bitCast packed structs at runtime and comptime" {
138 fn doTheTest() !void {138 fn doTheTest() !void {
139 var full = Full{ .number = 0x1234 };139 var full = Full{ .number = 0x1234 };
140 var two_halves = @bitCast(Divided, full);140 var two_halves = @bitCast(Divided, full);
141 switch (native_endian) {141 try expect(two_halves.half1 == 0x34);
142 .Big => {142 try expect(two_halves.quarter3 == 0x2);
143 try expect(two_halves.half1 == 0x12);143 try expect(two_halves.quarter4 == 0x1);
144 try expect(two_halves.quarter3 == 0x3);
145 try expect(two_halves.quarter4 == 0x4);
146 },
147 .Little => {
148 try expect(two_halves.half1 == 0x34);
149 try expect(two_halves.quarter3 == 0x2);
150 try expect(two_halves.quarter4 == 0x1);
151 },
152 }
153 }144 }
154 };145 };
155 try S.doTheTest();146 try S.doTheTest();
test/behavior/struct.zig+3-6
...@@ -499,17 +499,14 @@ const Bitfields = packed struct {...@@ -499,17 +499,14 @@ const Bitfields = packed struct {
499 f7: u8,499 f7: u8,
500};500};
501501
502test "native bit field understands endianness" {502test "packed struct fields are ordered from LSB to MSB" {
503 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;503 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
504 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO504 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
505 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO505 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
506 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO506 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO507 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
508508
509 var all: u64 = if (native_endian != .Little)509 var all: u64 = 0x7765443322221111;
510 0x1111222233445677
511 else
512 0x7765443322221111;
513 var bytes: [8]u8 = undefined;510 var bytes: [8]u8 = undefined;
514 @memcpy(&bytes, @ptrCast([*]u8, &all), 8);511 @memcpy(&bytes, @ptrCast([*]u8, &all), 8);
515 var bitfields = @ptrCast(*Bitfields, &bytes).*;512 var bitfields = @ptrCast(*Bitfields, &bytes).*;