| 1 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; |
| 3 | const builtin = @import("builtin"); |
| 4 | const native_arch = builtin.target.cpu.arch; |
| 5 | const assert = std.debug.assert; |
| 6 | |
| 7 | var foo: u8 align(4) = 100; |
| 8 | |
| 9 | test "global variable alignment" { |
| 10 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 11 | |
| 12 | comptime assert(@typeInfo(@TypeOf(&foo)).pointer.attrs.@"align" == 4); |
| 13 | comptime assert(@TypeOf(&foo) == *align(4) u8); |
| 14 | { |
| 15 | const slice = @as(*align(4) [1]u8, &foo)[0..]; |
| 16 | comptime assert(@TypeOf(slice) == *align(4) [1]u8); |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | test "large abi alignment of global" { |
| 21 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 22 | |
| 23 | const S = struct { |
| 24 | var global: @This() = undefined; |
| 25 | x: u64 align(64), |
| 26 | }; |
| 27 | |
| 28 | try std.testing.expect(@ctz(@intFromPtr(&S.global)) >= 6); |
| 29 | } |
| 30 | |
| 31 | test "large alignment of local constant" { |
| 32 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 33 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky |
| 34 | if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest; |
| 35 | |
| 36 | if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .windows) { |
| 37 | // https://codeberg.org/ziglang/zig/issues/35537 |
| 38 | return error.SkipZigTest; |
| 39 | } |
| 40 | |
| 41 | const x: f32 align(128) = 12.34; |
| 42 | try std.testing.expect(@intFromPtr(&x) % 128 == 0); |
| 43 | } |
| 44 | |
| 45 | test "slicing array of length 1 can not assume runtime index is always zero" { |
| 46 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 47 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky |
| 48 | |
| 49 | var runtime_index: usize = 1; |
| 50 | _ = &runtime_index; |
| 51 | const slice = @as(*align(4) [1]u8, &foo)[runtime_index..]; |
| 52 | try expect(@TypeOf(slice) == []align(1) u8); |
| 53 | try expect(slice.len == 0); |
| 54 | try expect(@as(u2, @truncate(@intFromPtr(slice.ptr) - 1)) == 0); |
| 55 | } |
| 56 | |
| 57 | test "implicitly-aligned pointer is coercible to equivalent explicitly-aligned pointer" { |
| 58 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 59 | |
| 60 | const A = *u32; |
| 61 | const B = *align(@alignOf(u32)) u32; |
| 62 | |
| 63 | comptime assert(A != B); |
| 64 | |
| 65 | const static = struct { |
| 66 | fn doTheTest() !void { |
| 67 | var buf: u32 = 123; |
| 68 | |
| 69 | const ptr: A = &buf; |
| 70 | const coerced_ptr: B = ptr; |
| 71 | |
| 72 | try expect(ptr == coerced_ptr); |
| 73 | try expect(ptr.* == 123); |
| 74 | try expect(coerced_ptr.* == 123); |
| 75 | |
| 76 | const ptr_ptr: *const A = &ptr; |
| 77 | const coerced_ptr_ptr: *const B = ptr_ptr; |
| 78 | |
| 79 | try expect(ptr_ptr == coerced_ptr_ptr); |
| 80 | try expect(ptr_ptr.* == &buf); |
| 81 | try expect(coerced_ptr_ptr.* == &buf); |
| 82 | try expect(ptr_ptr.*.* == 123); |
| 83 | try expect(coerced_ptr_ptr.*.* == 123); |
| 84 | } |
| 85 | }; |
| 86 | |
| 87 | try static.doTheTest(); |
| 88 | try comptime static.doTheTest(); |
| 89 | } |
| 90 | |
| 91 | test "implicitly decreasing pointer alignment" { |
| 92 | const a: u32 align(4) = 3; |
| 93 | const b: u32 align(8) = 4; |
| 94 | try expect(addUnaligned(&a, &b) == 7); |
| 95 | } |
| 96 | |
| 97 | fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { |
| 98 | return a.* + b.*; |
| 99 | } |
| 100 | |
| 101 | test "@alignCast pointers" { |
| 102 | var x: u32 align(4) = 1; |
| 103 | expectsOnly1(&x); |
| 104 | try expect(x == 2); |
| 105 | } |
| 106 | fn expectsOnly1(x: *align(1) u32) void { |
| 107 | expects4(@alignCast(x)); |
| 108 | } |
| 109 | fn expects4(x: *align(4) u32) void { |
| 110 | x.* += 1; |
| 111 | } |
| 112 | |
| 113 | test "alignment of struct with pointer has same alignment as usize" { |
| 114 | try expect(@alignOf(struct { |
| 115 | a: i32, |
| 116 | b: *i32, |
| 117 | }) == @alignOf(usize)); |
| 118 | } |
| 119 | |
| 120 | test "alignment and size of structs with 128-bit fields" { |
| 121 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 122 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 123 | |
| 124 | const A = struct { |
| 125 | x: u128, |
| 126 | }; |
| 127 | const B = extern struct { |
| 128 | x: u128, |
| 129 | y: u8, |
| 130 | }; |
| 131 | const expected = switch (builtin.cpu.arch) { |
| 132 | .s390x => .{ |
| 133 | .a_align = 8, |
| 134 | .a_size = 16, |
| 135 | |
| 136 | .b_align = 8, |
| 137 | .b_size = 24, |
| 138 | |
| 139 | .u128_align = 8, |
| 140 | .u128_size = 16, |
| 141 | .u129_align = 8, |
| 142 | .u129_size = 24, |
| 143 | }, |
| 144 | .x86 => switch (builtin.os.tag) { |
| 145 | else => .{ |
| 146 | .a_align = 4, |
| 147 | .a_size = 16, |
| 148 | |
| 149 | .b_align = 4, |
| 150 | .b_size = 20, |
| 151 | |
| 152 | .u128_align = 4, |
| 153 | .u128_size = 16, |
| 154 | .u129_align = 4, |
| 155 | .u129_size = 20, |
| 156 | }, |
| 157 | .uefi, .windows => .{ |
| 158 | .a_align = 8, |
| 159 | .a_size = 16, |
| 160 | |
| 161 | .b_align = 8, |
| 162 | .b_size = 24, |
| 163 | |
| 164 | .u128_align = 8, |
| 165 | .u128_size = 16, |
| 166 | .u129_align = 8, |
| 167 | .u129_size = 24, |
| 168 | }, |
| 169 | }, |
| 170 | .amdgcn, |
| 171 | .arm, |
| 172 | .armeb, |
| 173 | .thumb, |
| 174 | .thumbeb, |
| 175 | .hexagon, |
| 176 | .lanai, |
| 177 | .mips, |
| 178 | .mipsel, |
| 179 | .powerpc, |
| 180 | .powerpcle, |
| 181 | .riscv32, |
| 182 | .sparc, |
| 183 | => .{ |
| 184 | .a_align = 8, |
| 185 | .a_size = 16, |
| 186 | |
| 187 | .b_align = 8, |
| 188 | .b_size = 24, |
| 189 | |
| 190 | .u128_align = 8, |
| 191 | .u128_size = 16, |
| 192 | .u129_align = 8, |
| 193 | .u129_size = 24, |
| 194 | }, |
| 195 | |
| 196 | .aarch64, |
| 197 | .aarch64_be, |
| 198 | .bpfel, |
| 199 | .bpfeb, |
| 200 | .mips64, |
| 201 | .mips64el, |
| 202 | .nvptx, |
| 203 | .nvptx64, |
| 204 | .powerpc64, |
| 205 | .powerpc64le, |
| 206 | .sparc64, |
| 207 | .riscv64, |
| 208 | .wasm32, |
| 209 | .wasm64, |
| 210 | .x86_64, |
| 211 | => .{ |
| 212 | .a_align = 16, |
| 213 | .a_size = 16, |
| 214 | |
| 215 | .b_align = 16, |
| 216 | .b_size = 32, |
| 217 | |
| 218 | .u128_align = 16, |
| 219 | .u128_size = 16, |
| 220 | .u129_align = 16, |
| 221 | .u129_size = 32, |
| 222 | }, |
| 223 | |
| 224 | else => return error.SkipZigTest, |
| 225 | }; |
| 226 | comptime { |
| 227 | assert(@alignOf(A) == expected.a_align); |
| 228 | assert(@sizeOf(A) == expected.a_size); |
| 229 | |
| 230 | assert(@alignOf(B) == expected.b_align); |
| 231 | assert(@sizeOf(B) == expected.b_size); |
| 232 | |
| 233 | assert(@alignOf(u128) == expected.u128_align); |
| 234 | assert(@sizeOf(u128) == expected.u128_size); |
| 235 | |
| 236 | assert(@alignOf(u129) == expected.u129_align); |
| 237 | assert(@sizeOf(u129) == expected.u129_size); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | test "implicitly decreasing slice alignment" { |
| 242 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 243 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 244 | const a: u32 align(4) = 3; |
| 245 | const b: u32 align(8) = 4; |
| 246 | try expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7); |
| 247 | } |
| 248 | fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { |
| 249 | return a[0] + b[0]; |
| 250 | } |
| 251 | |
| 252 | test "specifying alignment allows pointer cast" { |
| 253 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 254 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 255 | |
| 256 | try testBytesAlign(0x33); |
| 257 | } |
| 258 | fn testBytesAlign(b: u8) !void { |
| 259 | var bytes align(4) = [_]u8{ b, b, b, b }; |
| 260 | const ptr = @as(*u32, @ptrCast(&bytes[0])); |
| 261 | try expect(ptr.* == 0x33333333); |
| 262 | } |
| 263 | |
| 264 | test "@alignCast slices" { |
| 265 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 266 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 267 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 268 | |
| 269 | var array align(4) = [_]u32{ 1, 1 }; |
| 270 | const slice = array[0..]; |
| 271 | sliceExpectsOnly1(slice); |
| 272 | try expect(slice[0] == 2); |
| 273 | } |
| 274 | fn sliceExpectsOnly1(slice: []align(1) u32) void { |
| 275 | sliceExpects4(@alignCast(slice)); |
| 276 | } |
| 277 | fn sliceExpects4(slice: []align(4) u32) void { |
| 278 | slice[0] += 1; |
| 279 | } |
| 280 | |
| 281 | test "return error union with 128-bit integer" { |
| 282 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 283 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 284 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 285 | try expect(3 == try give()); |
| 286 | } |
| 287 | fn give() anyerror!u128 { |
| 288 | return 3; |
| 289 | } |
| 290 | |
| 291 | test "page aligned array on stack" { |
| 292 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 293 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 294 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 295 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 296 | // Large alignment value to make it hard to accidentally pass. |
| 297 | var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 298 | var number1: u8 align(16) = 42; |
| 299 | var number2: u8 align(16) = 43; |
| 300 | |
| 301 | try expect(@intFromPtr(&array[0]) & 0xFFF == 0); |
| 302 | try expect(array[3] == 4); |
| 303 | |
| 304 | try expect(@as(u4, @truncate(@intFromPtr(&number1))) == 0); |
| 305 | try expect(@as(u4, @truncate(@intFromPtr(&number2))) == 0); |
| 306 | try expect(number1 == 42); |
| 307 | try expect(number2 == 43); |
| 308 | } |
| 309 | |
| 310 | test "function alignment" { |
| 311 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 312 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 313 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 314 | |
| 315 | // function alignment is a compile error on wasm |
| 316 | if (native_arch.isWasm()) return error.SkipZigTest; |
| 317 | |
| 318 | const S = struct { |
| 319 | fn alignExpr() align(@sizeOf(usize) * 2) i32 { |
| 320 | return 1234; |
| 321 | } |
| 322 | fn align1() align(1) void {} |
| 323 | fn align4() align(4) void {} |
| 324 | }; |
| 325 | |
| 326 | try expect(S.alignExpr() == 1234); |
| 327 | try expect(@TypeOf(S.alignExpr) == fn () i32); |
| 328 | try expect(@TypeOf(&S.alignExpr) == *align(@sizeOf(usize) * 2) const fn () i32); |
| 329 | |
| 330 | S.align1(); |
| 331 | try expect(@TypeOf(S.align1) == fn () void); |
| 332 | try expect(@TypeOf(&S.align1) == *align(1) const fn () void); |
| 333 | |
| 334 | S.align4(); |
| 335 | try expect(@TypeOf(S.align4) == fn () void); |
| 336 | try expect(@TypeOf(&S.align4) == *align(4) const fn () void); |
| 337 | } |
| 338 | |
| 339 | test "implicitly decreasing fn alignment" { |
| 340 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 341 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 342 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 343 | |
| 344 | // function alignment is a compile error on wasm |
| 345 | if (native_arch.isWasm()) return error.SkipZigTest; |
| 346 | |
| 347 | try testImplicitlyDecreaseFnAlign(alignedSmall, 1234); |
| 348 | try testImplicitlyDecreaseFnAlign(alignedBig, 5678); |
| 349 | } |
| 350 | |
| 351 | fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void { |
| 352 | try expect(ptr() == answer); |
| 353 | } |
| 354 | |
| 355 | fn alignedSmall() align(8) i32 { |
| 356 | return 1234; |
| 357 | } |
| 358 | fn alignedBig() align(16) i32 { |
| 359 | return 5678; |
| 360 | } |
| 361 | |
| 362 | test "@alignCast functions" { |
| 363 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 364 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 365 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 366 | |
| 367 | // function alignment is a compile error on wasm |
| 368 | if (native_arch.isWasm()) return error.SkipZigTest; |
| 369 | |
| 370 | try expect(fnExpectsOnly1(simple4) == 0x19); |
| 371 | } |
| 372 | fn fnExpectsOnly1(ptr: *align(1) const fn () i32) i32 { |
| 373 | return fnExpects4(@alignCast(ptr)); |
| 374 | } |
| 375 | fn fnExpects4(ptr: *align(4) const fn () i32) i32 { |
| 376 | return ptr(); |
| 377 | } |
| 378 | fn simple4() align(4) i32 { |
| 379 | return 0x19; |
| 380 | } |
| 381 | |
| 382 | test "runtime-known array index has best alignment possible" { |
| 383 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 384 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 385 | |
| 386 | // take full advantage of over-alignment |
| 387 | var array align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 388 | comptime assert(@TypeOf(&array[0]) == *align(4) u8); |
| 389 | comptime assert(@TypeOf(&array[1]) == *align(1) u8); |
| 390 | comptime assert(@TypeOf(&array[2]) == *align(2) u8); |
| 391 | comptime assert(@TypeOf(&array[3]) == *align(1) u8); |
| 392 | comptime assert(@TypeOf(&array[4]) == *align(4) u8); |
| 393 | comptime assert(@TypeOf(&array[5]) == *align(1) u8); |
| 394 | comptime assert(@TypeOf(&array[6]) == *align(2) u8); |
| 395 | comptime assert(@TypeOf(&array[7]) == *align(1) u8); |
| 396 | |
| 397 | // because align is too small but we still figure out to use 2 |
| 398 | var bigger align(2) = [_]u64{ 1, 2, 3, 4 }; |
| 399 | comptime assert(@TypeOf(&bigger[0]) == *align(2) u64); |
| 400 | comptime assert(@TypeOf(&bigger[1]) == *align(2) u64); |
| 401 | comptime assert(@TypeOf(&bigger[2]) == *align(2) u64); |
| 402 | comptime assert(@TypeOf(&bigger[3]) == *align(2) u64); |
| 403 | |
| 404 | // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 |
| 405 | var smaller align(2) = [_]u32{ 1, 2, 3, 4 }; |
| 406 | var runtime_zero: usize = 0; |
| 407 | _ = &runtime_zero; |
| 408 | comptime assert(@TypeOf(smaller[runtime_zero..]) == []align(2) u32); |
| 409 | comptime assert(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32); |
| 410 | try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32); |
| 411 | try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32); |
| 412 | try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32); |
| 413 | try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32); |
| 414 | |
| 415 | // has to use ABI alignment because index known at runtime only |
| 416 | try testIndex2(&array, 0, *align(1) u8); |
| 417 | try testIndex2(&array, 1, *align(1) u8); |
| 418 | try testIndex2(&array, 2, *align(1) u8); |
| 419 | try testIndex2(&array, 3, *align(1) u8); |
| 420 | try testIndex2(&array, 4, *align(1) u8); |
| 421 | try testIndex2(&array, 5, *align(1) u8); |
| 422 | try testIndex2(&array, 6, *align(1) u8); |
| 423 | try testIndex2(&array, 7, *align(1) u8); |
| 424 | } |
| 425 | fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void { |
| 426 | comptime assert(@TypeOf(&smaller[index]) == T); |
| 427 | } |
| 428 | fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void { |
| 429 | comptime assert(@TypeOf(&ptr[index]) == T); |
| 430 | } |
| 431 | |
| 432 | test "alignment of function with c calling convention" { |
| 433 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 434 | |
| 435 | const a = @alignOf(@TypeOf(nothing)); |
| 436 | |
| 437 | var runtime_nothing = &nothing; |
| 438 | _ = &runtime_nothing; |
| 439 | const casted1: *align(a) const u8 = @ptrCast(runtime_nothing); |
| 440 | const casted2: *const fn () callconv(.c) void = @ptrCast(casted1); |
| 441 | casted2(); |
| 442 | } |
| 443 | |
| 444 | fn nothing() callconv(.c) void {} |
| 445 | |
| 446 | const DefaultAligned = struct { |
| 447 | nevermind: u32, |
| 448 | badguy: i128, |
| 449 | }; |
| 450 | |
| 451 | test "read 128-bit field from default aligned struct in stack memory" { |
| 452 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 453 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 454 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 455 | |
| 456 | var default_aligned = DefaultAligned{ |
| 457 | .nevermind = 1, |
| 458 | .badguy = 12, |
| 459 | }; |
| 460 | _ = &default_aligned; |
| 461 | try expect(12 == default_aligned.badguy); |
| 462 | } |
| 463 | |
| 464 | var default_aligned_global = DefaultAligned{ |
| 465 | .nevermind = 1, |
| 466 | .badguy = 12, |
| 467 | }; |
| 468 | |
| 469 | test "read 128-bit field from default aligned struct in global memory" { |
| 470 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 471 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 472 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 473 | |
| 474 | try expect(12 == default_aligned_global.badguy); |
| 475 | } |
| 476 | |
| 477 | test "struct field explicit alignment" { |
| 478 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 479 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 480 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky |
| 481 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 482 | |
| 483 | const S = struct { |
| 484 | const Node = struct { |
| 485 | next: *Node, |
| 486 | massive_byte: u8 align(64), |
| 487 | }; |
| 488 | }; |
| 489 | |
| 490 | var node: S.Node = undefined; |
| 491 | node.massive_byte = 100; |
| 492 | try expect(node.massive_byte == 100); |
| 493 | comptime assert(@TypeOf(&node.massive_byte) == *align(64) u8); |
| 494 | try expect(@intFromPtr(&node.massive_byte) % 64 == 0); |
| 495 | } |
| 496 | |
| 497 | test "align(N) on functions" { |
| 498 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 499 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 500 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 501 | |
| 502 | if (builtin.zig_backend == .stage2_c) { |
| 503 | // https://github.com/ziglang/zig/issues/16845 |
| 504 | return error.SkipZigTest; |
| 505 | } |
| 506 | |
| 507 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) { |
| 508 | // This is not supported on MSVC. |
| 509 | return error.SkipZigTest; |
| 510 | } |
| 511 | |
| 512 | // function alignment is a compile error on wasm |
| 513 | if (native_arch.isWasm()) return error.SkipZigTest; |
| 514 | if (native_arch.isThumb()) return error.SkipZigTest; |
| 515 | |
| 516 | try expect((@intFromPtr(&overaligned_fn) & (0x1000 - 1)) == 0); |
| 517 | } |
| 518 | fn overaligned_fn() align(0x1000) i32 { |
| 519 | return 42; |
| 520 | } |
| 521 | |
| 522 | test "comptime alloc alignment" { |
| 523 | // TODO: it's impossible to test this in Zig today, since comptime vars do not have runtime addresses. |
| 524 | if (true) return error.SkipZigTest; |
| 525 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 526 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 527 | |
| 528 | comptime var bytes1 = [_]u8{0}; |
| 529 | _ = &bytes1; |
| 530 | |
| 531 | comptime var bytes2 align(256) = [_]u8{0}; |
| 532 | const bytes2_addr = @intFromPtr(&bytes2); |
| 533 | try expect(bytes2_addr & 0xff == 0); |
| 534 | } |
| 535 | |
| 536 | test "@alignCast null" { |
| 537 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 538 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 539 | |
| 540 | var ptr: ?*anyopaque = null; |
| 541 | _ = &ptr; |
| 542 | const aligned: ?*anyopaque = @alignCast(ptr); |
| 543 | try expect(aligned == null); |
| 544 | } |
| 545 | |
| 546 | test "alignment of slice element" { |
| 547 | const a: []align(1024) const u8 = undefined; |
| 548 | try expect(@TypeOf(&a[0]) == *align(1024) const u8); |
| 549 | } |
| 550 | |
| 551 | test "sub-aligned pointer field access" { |
| 552 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 553 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 554 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 555 | |
| 556 | // Originally reported at https://github.com/ziglang/zig/issues/14904 |
| 557 | |
| 558 | const Header = extern struct { |
| 559 | tag: u32, |
| 560 | bytes_len: u32, |
| 561 | }; |
| 562 | var buf: [9]u8 align(4) = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; |
| 563 | const ptr: *align(1) Header = @ptrCast(buf[1..][0..8]); |
| 564 | const x = ptr.bytes_len; |
| 565 | switch (builtin.cpu.arch.endian()) { |
| 566 | .big => try expect(x == 0x06070809), |
| 567 | .little => try expect(x == 0x09080706), |
| 568 | } |
| 569 | } |
| 570 | |
| 571 | test "alignment of zero-bit types is respected" { |
| 572 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 573 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 574 | |
| 575 | const S = struct { arr: [0]usize = .{} }; |
| 576 | |
| 577 | comptime assert(@alignOf(void) == 1); |
| 578 | comptime assert(@alignOf(u0) == 1); |
| 579 | comptime assert(@alignOf([0]usize) == @alignOf(usize)); |
| 580 | comptime assert(@alignOf(S) == @alignOf(usize)); |
| 581 | |
| 582 | var s: S = .{}; |
| 583 | var v32: void align(32) = {}; |
| 584 | var x32: u0 align(32) = 0; |
| 585 | var s32: S align(32) = .{}; |
| 586 | |
| 587 | var zero: usize = 0; |
| 588 | _ = &zero; |
| 589 | |
| 590 | try expect(@intFromPtr(&s) % @alignOf(usize) == 0); |
| 591 | try expect(@intFromPtr(&s.arr) % @alignOf(usize) == 0); |
| 592 | try expect(@intFromPtr(s.arr[zero..zero].ptr) % @alignOf(usize) == 0); |
| 593 | try expect(@intFromPtr(&v32) % 32 == 0); |
| 594 | try expect(@intFromPtr(&x32) % 32 == 0); |
| 595 | try expect(@intFromPtr(&s32) % 32 == 0); |
| 596 | try expect(@intFromPtr(&s32.arr) % 32 == 0); |
| 597 | try expect(@intFromPtr(s32.arr[zero..zero].ptr) % 32 == 0); |
| 598 | } |
| 599 | |
| 600 | test "zero-bit fields in extern struct pad fields appropriately" { |
| 601 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 602 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 603 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 604 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 605 | |
| 606 | const S = extern struct { |
| 607 | x: u8, |
| 608 | a: [0]u16 = .{}, |
| 609 | y: u8, |
| 610 | }; |
| 611 | |
| 612 | // `a` should give `S` alignment 2, and pad the `arr` field. |
| 613 | comptime assert(@alignOf(S) == 2); |
| 614 | comptime assert(@sizeOf(S) == 4); |
| 615 | comptime assert(@offsetOf(S, "x") == 0); |
| 616 | comptime assert(@offsetOf(S, "a") == 2); |
| 617 | comptime assert(@offsetOf(S, "y") == 2); |
| 618 | |
| 619 | var s: S = .{ .x = 100, .y = 200 }; |
| 620 | |
| 621 | try expect(@intFromPtr(&s) % 2 == 0); |
| 622 | try expect(@intFromPtr(&s.y) - @intFromPtr(&s.x) == 2); |
| 623 | try expect(@intFromPtr(&s.y) == @intFromPtr(&s.a)); |
| 624 | try expect(@as(*S, @fieldParentPtr("a", &s.a)) == &s); |
| 625 | } |
| 626 | |
| 627 | test "function pointer @intFromPtr/@ptrFromInt roundtrip" { |
| 628 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 629 | |
| 630 | // This only succeeds on Thumb if we handle the Thumb bit correctly; if not, the `@ptrFromInt` |
| 631 | // will incorrectly trip an alignment safety check. |
| 632 | |
| 633 | const nothing_ptr: *const fn () callconv(.c) void = &nothing; |
| 634 | const nothing_int: usize = @intFromPtr(nothing_ptr); |
| 635 | const nothing_ptr2: *const fn () callconv(.c) void = @ptrFromInt(nothing_int); |
| 636 | |
| 637 | try std.testing.expectEqual(nothing_ptr, nothing_ptr2); |
| 638 | } |
| 639 | |
| 640 | test "function pointer align mask" { |
| 641 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 642 | |
| 643 | const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020; |
| 644 | const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int); |
| 645 | const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned); |
| 646 | try expect(@intFromPtr(aligned) == int); |
| 647 | } |
| 648 | |
| 649 | test "align expression is implicitly comptime" { |
| 650 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 651 | |
| 652 | const S = struct { |
| 653 | fn alignment() usize { |
| 654 | return 4; |
| 655 | } |
| 656 | |
| 657 | var global: [3]u8 align(alignment()) = @splat(0); |
| 658 | fn check() !void { |
| 659 | try std.testing.expect(@intFromPtr(&global) % alignment() == 0); |
| 660 | var local: [3]u8 align(alignment()) = @splat(0); |
| 661 | try std.testing.expect(@intFromPtr(&local) % alignment() == 0); |
| 662 | var de: [3]u8 align(alignment()), var structure: [3]u8 align(alignment()) = .{ @splat(0), @splat(0) }; |
| 663 | try std.testing.expect(@intFromPtr(&de) % alignment() == 0); |
| 664 | try std.testing.expect(@intFromPtr(&structure) % alignment() == 0); |
| 665 | var @"struct": struct { field: [3]u8 align(alignment()) } = .{ .field = @splat(0) }; |
| 666 | try std.testing.expect(@intFromPtr(&@"struct".field) % alignment() == 0); |
| 667 | var @"union": union { field: [3]u8 align(alignment()) } = .{ .field = @splat(0) }; |
| 668 | try std.testing.expect(@intFromPtr(&@"union".field) % alignment() == 0); |
| 669 | const ptr: *align(alignment()) [3]u8 = &global; |
| 670 | try std.testing.expect(@intFromPtr(ptr) % alignment() == 0); |
| 671 | } |
| 672 | }; |
| 673 | try S.check(); |
| 674 | } |