authorgravatar for spexguy070@gmail.comMartin Wickham <spexguy070@gmail.com> 2021-08-30 13:01:37-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-30 18:50:07-04:00
logcf75cad899069d67d50e6d08160b998b7cba16dc
treec704d84190798fc80dc8396ac8eb853dab139353
parent4334dbb9a5ddfa58a06f3ef7759927e2cb38e65b

Add comptime memory tests


2 files changed, 319 insertions(+), 0 deletions(-)

test/behavior.zig+2
......@@ -89,6 +89,8 @@ test {
8989 _ = @import("behavior/byval_arg_var.zig");
9090 _ = @import("behavior/call.zig");
9191 _ = @import("behavior/cast_stage1.zig");
92 // When these tests pass, #9646 can be closed.
93 // _ = @import("behavior/comptime_memory.zig");
9294 _ = @import("behavior/const_slice_child.zig");
9395 _ = @import("behavior/defer.zig");
9496 _ = @import("behavior/enum.zig");
test/behavior/comptime_memory.zig created+317
......@@ -0,0 +1,317 @@
1const endian = @import("builtin").cpu.arch.endian();
2const testing = @import("std").testing;
3const ptr_size = @sizeOf(usize);
4
5test "type pun signed and unsigned as single pointer" {
6 comptime {
7 var x: u32 = 0;
8 const y = @ptrCast(*i32, &x);
9 y.* = -1;
10 try testing.expectEqual(@as(u32, 0xFFFFFFFF), x);
11 }
12}
13
14test "type pun signed and unsigned as many pointer" {
15 comptime {
16 var x: u32 = 0;
17 const y = @ptrCast([*]i32, &x);
18 y[0] = -1;
19 try testing.expectEqual(@as(u32, 0xFFFFFFFF), x);
20 }
21}
22
23test "type pun signed and unsigned as array pointer" {
24 comptime {
25 var x: u32 = 0;
26 const y = @ptrCast(*[1]i32, &x);
27 y[0] = -1;
28 try testing.expectEqual(@as(u32, 0xFFFFFFFF), x);
29 }
30}
31
32test "type pun signed and unsigned as offset many pointer" {
33 comptime {
34 var x: u32 = 0;
35 var y = @ptrCast([*]i32, &x);
36 y -= 10;
37 y[10] = -1;
38 try testing.expectEqual(@as(u32, 0xFFFFFFFF), x);
39 }
40}
41
42test "type pun signed and unsigned as array pointer" {
43 comptime {
44 var x: u32 = 0;
45 const y = @ptrCast([*]i32, &x) - 10;
46 const z: *[15]i32 = y[0..15];
47 z[10] = -1;
48 try testing.expectEqual(@as(u32, 0xFFFFFFFF), x);
49 }
50}
51
52test "type pun value and struct" {
53 comptime {
54 const StructOfU32 = extern struct { x: u32 };
55 var inst: StructOfU32 = .{ .x = 0 };
56 @ptrCast(*i32, &inst.x).* = -1;
57 try testing.expectEqual(@as(u32, 0xFFFFFFFF), inst.x);
58 @ptrCast(*i32, &inst).* = -2;
59 try testing.expectEqual(@as(u32, 0xFFFFFFFE), inst.x);
60 }
61}
62
63fn bigToNativeEndian(comptime T: type, v: T) T {
64 return if (endian == .Big) v else @byteSwap(T, v);
65}
66test "type pun endianness" {
67 comptime {
68 const StructOfBytes = extern struct { x: [4]u8 };
69 var inst: StructOfBytes = .{ .x = [4]u8{ 0, 0, 0, 0 } };
70 const structPtr = @ptrCast(*align(1) u32, &inst);
71 const arrayPtr = @ptrCast(*align(1) u32, &inst.x);
72 inst.x[0] = 0xFE;
73 inst.x[2] = 0xBE;
74 try testing.expectEqual(bigToNativeEndian(u32, 0xFE00BE00), structPtr.*);
75 try testing.expectEqual(bigToNativeEndian(u32, 0xFE00BE00), arrayPtr.*);
76 structPtr.* = bigToNativeEndian(u32, 0xDEADF00D);
77 try testing.expectEqual(bigToNativeEndian(u32, 0xDEADF00D), structPtr.*);
78 try testing.expectEqual(bigToNativeEndian(u32, 0xDEADF00D), arrayPtr.*);
79 try testing.expectEqual(@as(u8, 0xDE), inst.x[0]);
80 try testing.expectEqual(@as(u8, 0xAD), inst.x[1]);
81 try testing.expectEqual(@as(u8, 0xF0), inst.x[2]);
82 try testing.expectEqual(@as(u8, 0x0D), inst.x[3]);
83 }
84}
85
86const Bits = packed struct {
87 // Note: This struct has only single byte words so it
88 // doesn't need to be byte swapped.
89 p0: u1,
90 p1: u4,
91 p2: u3,
92 p3: u2,
93 p4: u6,
94 p5: u8,
95 p6: u7,
96 p7: u1,
97};
98const ShuffledBits = packed struct {
99 p1: u4,
100 p3: u2,
101 p7: u1,
102 p0: u1,
103 p5: u8,
104 p2: u3,
105 p6: u7,
106 p4: u6,
107};
108fn shuffle(ptr: usize, comptime From: type, comptime To: type) usize {
109 if (@sizeOf(From) != @sizeOf(To))
110 @compileError("Mismatched sizes! " ++ @typeName(From) ++ " and " ++ @typeName(To) ++ " must have the same size!");
111 const array_len = @divExact(ptr_size, @sizeOf(From));
112 var result: usize = 0;
113 const pSource = @ptrCast(*align(1) const [array_len]From, &ptr);
114 const pResult = @ptrCast(*align(1) [array_len]To, &result);
115 var i: usize = 0;
116 while (i < array_len) : (i += 1) {
117 inline for (@typeInfo(To).Struct.fields) |f| {
118 @field(pResult[i], f.name) = @field(pSource[i], f.name);
119 }
120 }
121 return result;
122}
123
124fn doTypePunBitsTest(as_bits: *Bits) !void {
125 const as_u32 = @ptrCast(*align(1) u32, as_bits);
126 const as_bytes = @ptrCast(*[4]u8, as_bits);
127 as_u32.* = bigToNativeEndian(u32, 0xB0A7DEED);
128 try testing.expectEqual(@as(u1, 0x00), as_bits.p0);
129 try testing.expectEqual(@as(u4, 0x08), as_bits.p1);
130 try testing.expectEqual(@as(u3, 0x05), as_bits.p2);
131 try testing.expectEqual(@as(u2, 0x03), as_bits.p3);
132 try testing.expectEqual(@as(u6, 0x29), as_bits.p4);
133 try testing.expectEqual(@as(u8, 0xDE), as_bits.p5);
134 try testing.expectEqual(@as(u7, 0x6D), as_bits.p6);
135 try testing.expectEqual(@as(u1, 0x01), as_bits.p7);
136
137 as_bits.p6 = 0x2D;
138 as_bits.p1 = 0x0F;
139 try testing.expectEqual(bigToNativeEndian(u32, 0xBEA7DEAD), as_u32.*);
140
141 // clobbering one bit doesn't clobber the word
142 as_bits.p7 = undefined;
143 try testing.expectEqual(@as(u7, 0x2D), as_bits.p6);
144 // even when read as a whole
145 const u = as_u32.*;
146 _ = u; // u is undefined
147 try testing.expectEqual(@as(u7, 0x2D), as_bits.p6);
148 // or if a field which shares the byte is modified
149 as_bits.p6 = 0x6D;
150 try testing.expectEqual(@as(u7, 0x6D), as_bits.p6);
151
152 // but overwriting the undefined will clear it
153 as_bytes[3] = 0xAF;
154 try testing.expectEqual(bigToNativeEndian(u32, 0xBEA7DEAF), as_u32.*);
155}
156
157test "type pun bits" {
158 comptime {
159 var v: u32 = undefined;
160 try doTypePunBitsTest(@ptrCast(*Bits, &v));
161 }
162}
163
164const imports = struct {
165 var global_u32: u32 = 0;
166};
167
168// Make sure lazy values work on their own, before getting into more complex tests
169test "basic pointer preservation" {
170 comptime {
171 const lazy_address = @ptrToInt(&imports.global_u32);
172 try testing.expectEqual(@ptrToInt(&imports.global_u32), lazy_address);
173 try testing.expectEqual(&imports.global_u32, @intToPtr(*u32, lazy_address));
174 }
175}
176
177test "byte copy preserves linker value" {
178 const ct_value = comptime blk: {
179 const lazy = &imports.global_u32;
180 var result: *u32 = undefined;
181 const pSource = @ptrCast(*const [ptr_size]u8, &lazy);
182 const pResult = @ptrCast(*[ptr_size]u8, &result);
183 var i: usize = 0;
184 while (i < ptr_size) : (i += 1) {
185 pResult[i] = pSource[i];
186 try testing.expectEqual(pSource[i], pResult[i]);
187 }
188 try testing.expectEqual(&imports.global_u32, result);
189 break :blk result;
190 };
191
192 try testing.expectEqual(&imports.global_u32, ct_value);
193}
194
195test "unordered byte copy preserves linker value" {
196 const ct_value = comptime blk: {
197 const lazy = &imports.global_u32;
198 var result: *u32 = undefined;
199 const pSource = @ptrCast(*const [ptr_size]u8, &lazy);
200 const pResult = @ptrCast(*[ptr_size]u8, &result);
201 if (ptr_size > 8) @compileError("This array needs to be expanded for platform with very big pointers");
202 const shuffled_indices = [_]usize{ 4, 5, 2, 6, 1, 3, 0, 7 };
203 for (shuffled_indices) |i| {
204 pResult[i] = pSource[i];
205 try testing.expectEqual(pSource[i], pResult[i]);
206 }
207 try testing.expectEqual(&imports.global_u32, result);
208 break :blk result;
209 };
210
211 try testing.expectEqual(&imports.global_u32, ct_value);
212}
213
214test "shuffle chunks of linker value" {
215 const lazy_address = @ptrToInt(&imports.global_u32);
216 const shuffled1_rt = shuffle(lazy_address, Bits, ShuffledBits);
217 const unshuffled1_rt = shuffle(shuffled1_rt, ShuffledBits, Bits);
218 try testing.expectEqual(lazy_address, unshuffled1_rt);
219 const shuffled1_ct = comptime shuffle(lazy_address, Bits, ShuffledBits);
220 const shuffled1_ct_2 = comptime shuffle(lazy_address, Bits, ShuffledBits);
221 comptime try testing.expectEqual(shuffled1_ct, shuffled1_ct_2);
222 const unshuffled1_ct = comptime shuffle(shuffled1_ct, ShuffledBits, Bits);
223 comptime try testing.expectEqual(lazy_address, unshuffled1_ct);
224 try testing.expectEqual(shuffled1_ct, shuffled1_rt);
225}
226
227test "dance on linker values" {
228 comptime {
229 var arr: [2]usize = undefined;
230 arr[0] = @ptrToInt(&imports.global_u32);
231 arr[1] = @ptrToInt(&imports.global_u32);
232
233 const weird_ptr = @ptrCast([*]Bits, @ptrCast([*]u8, &arr) + @sizeOf(usize) - 3);
234 try doTypePunBitsTest(&weird_ptr[0]);
235 if (ptr_size > @sizeOf(Bits))
236 try doTypePunBitsTest(&weird_ptr[1]);
237
238 var arr_bytes = @ptrCast(*[2][ptr_size]u8, &arr);
239
240 var rebuilt_bytes: [ptr_size]u8 = undefined;
241 var i: usize = 0;
242 while (i < ptr_size - 3) : (i += 1) {
243 rebuilt_bytes[i] = arr_bytes[0][i];
244 }
245 while (i < ptr_size) : (i += 1) {
246 rebuilt_bytes[i] = arr_bytes[1][i];
247 }
248
249 try testing.expectEqual(&imports.global_u32, @intToPtr(*u32, @bitCast(usize, rebuilt_bytes)));
250 }
251}
252
253test "offset array ptr by element size" {
254 comptime {
255 const VirtualStruct = struct { x: u32 };
256 var arr: [4]VirtualStruct = .{
257 .{ .x = bigToNativeEndian(u32, 0x0004080c) },
258 .{ .x = bigToNativeEndian(u32, 0x0105090d) },
259 .{ .x = bigToNativeEndian(u32, 0x02060a0e) },
260 .{ .x = bigToNativeEndian(u32, 0x03070b0f) },
261 };
262
263 const address = @ptrToInt(&arr);
264 try testing.expectEqual(@ptrToInt(&arr[0]), address);
265 try testing.expectEqual(@ptrToInt(&arr[0]) + 10, address + 10);
266 try testing.expectEqual(@ptrToInt(&arr[1]), address + @sizeOf(VirtualStruct));
267 try testing.expectEqual(@ptrToInt(&arr[2]), address + 2 * @sizeOf(VirtualStruct));
268 try testing.expectEqual(@ptrToInt(&arr[3]), address + @sizeOf(VirtualStruct) * 3);
269
270 const secondElement = @intToPtr(*VirtualStruct, @ptrToInt(&arr[0]) + 2 * @sizeOf(VirtualStruct));
271 try testing.expectEqual(bigToNativeEndian(u32, 0x02060a0e), secondElement.x);
272 }
273}
274
275test "offset instance by field size" {
276 comptime {
277 const VirtualStruct = struct { x: u32, y: u32, z: u32, w: u32 };
278 var inst = VirtualStruct{ .x = 0, .y = 1, .z = 2, .w = 3 };
279
280 var ptr = @ptrToInt(&inst);
281 ptr -= 4;
282 ptr += @offsetOf(VirtualStruct, "x");
283 try testing.expectEqual(@as(u32, 0), @intToPtr([*]u32, ptr)[1]);
284 ptr -= @offsetOf(VirtualStruct, "x");
285 ptr += @offsetOf(VirtualStruct, "y");
286 try testing.expectEqual(@as(u32, 1), @intToPtr([*]u32, ptr)[1]);
287 ptr = ptr - @offsetOf(VirtualStruct, "y") + @offsetOf(VirtualStruct, "z");
288 try testing.expectEqual(@as(u32, 2), @intToPtr([*]u32, ptr)[1]);
289 ptr = @ptrToInt(&inst.z) - 4 - @offsetOf(VirtualStruct, "z");
290 ptr += @offsetOf(VirtualStruct, "w");
291 try testing.expectEqual(@as(u32, 3), @intToPtr(*u32, ptr + 4).*);
292 }
293}
294
295test "offset field ptr by enclosing array element size" {
296 comptime {
297 const VirtualStruct = struct { x: u32 };
298 var arr: [4]VirtualStruct = .{
299 .{ .x = bigToNativeEndian(u32, 0x0004080c) },
300 .{ .x = bigToNativeEndian(u32, 0x0105090d) },
301 .{ .x = bigToNativeEndian(u32, 0x02060a0e) },
302 .{ .x = bigToNativeEndian(u32, 0x03070b0f) },
303 };
304
305 var i: usize = 0;
306 while (i < 4) : (i += 1) {
307 var ptr: [*]u8 = @ptrCast([*]u8, &arr[0]);
308 ptr += i;
309 ptr += @offsetOf(VirtualStruct, "x");
310 var j: usize = 0;
311 while (j < 4) : (j += 1) {
312 const base = ptr + j * @sizeOf(VirtualStruct);
313 try testing.expectEqual(@intCast(u8, i * 4 + j), base[0]);
314 }
315 }
316 }
317}