aboutsummaryrefslogtreecommitdiff
path: root/test/behavior/align.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-04-29 15:54:04 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-04-29 15:54:04 -0700
commit4307436b9945f814ff5731981df1d19febf3ba0a (patch)
treeefaaec94a41d632d45f255d464d9787eb02c4c9b /test/behavior/align.zig
parent5a02c938dafdf2bb11b2350b6ad3161ef93744f0 (diff)
downloadzig-4307436b9945f814ff5731981df1d19febf3ba0a.tar.gz
zig-4307436b9945f814ff5731981df1d19febf3ba0a.zip
move behavior tests from test/stage1/ to test/
And fix test cases to make them pass. This is in preparation for starting to pass behavior tests with self-hosted.
Diffstat (limited to 'test/behavior/align.zig')
-rw-r--r--test/behavior/align.zig347
1 files changed, 347 insertions, 0 deletions
diff --git a/test/behavior/align.zig b/test/behavior/align.zig
new file mode 100644
index 0000000000..3ef147746c
--- /dev/null
+++ b/test/behavior/align.zig
@@ -0,0 +1,347 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const builtin = @import("builtin");
+const native_arch = builtin.target.cpu.arch;
+
+var foo: u8 align(4) = 100;
+
+test "global variable alignment" {
+ comptime expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
+ comptime expect(@TypeOf(&foo) == *align(4) u8);
+ {
+ const slice = @as(*[1]u8, &foo)[0..];
+ comptime expect(@TypeOf(slice) == *align(4) [1]u8);
+ }
+ {
+ var runtime_zero: usize = 0;
+ const slice = @as(*[1]u8, &foo)[runtime_zero..];
+ comptime expect(@TypeOf(slice) == []align(4) u8);
+ }
+}
+
+fn derp() align(@sizeOf(usize) * 2) i32 {
+ return 1234;
+}
+fn noop1() align(1) void {}
+fn noop4() align(4) void {}
+
+test "function alignment" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(derp() == 1234);
+ expect(@TypeOf(noop1) == fn () align(1) void);
+ expect(@TypeOf(noop4) == fn () align(4) void);
+ noop1();
+ noop4();
+}
+
+var baz: packed struct {
+ a: u32,
+ b: u32,
+} = undefined;
+
+test "packed struct alignment" {
+ expect(@TypeOf(&baz.b) == *align(1) u32);
+}
+
+const blah: packed struct {
+ a: u3,
+ b: u3,
+ c: u2,
+} = undefined;
+
+test "bit field alignment" {
+ expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
+}
+
+test "default alignment allows unspecified in type syntax" {
+ expect(*u32 == *align(@alignOf(u32)) u32);
+}
+
+test "implicitly decreasing pointer alignment" {
+ const a: u32 align(4) = 3;
+ const b: u32 align(8) = 4;
+ expect(addUnaligned(&a, &b) == 7);
+}
+
+fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
+ return a.* + b.*;
+}
+
+test "implicitly decreasing slice alignment" {
+ const a: u32 align(4) = 3;
+ const b: u32 align(8) = 4;
+ expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7);
+}
+fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
+ return a[0] + b[0];
+}
+
+test "specifying alignment allows pointer cast" {
+ testBytesAlign(0x33);
+}
+fn testBytesAlign(b: u8) void {
+ var bytes align(4) = [_]u8{
+ b,
+ b,
+ b,
+ b,
+ };
+ const ptr = @ptrCast(*u32, &bytes[0]);
+ expect(ptr.* == 0x33333333);
+}
+
+test "@alignCast pointers" {
+ var x: u32 align(4) = 1;
+ expectsOnly1(&x);
+ expect(x == 2);
+}
+fn expectsOnly1(x: *align(1) u32) void {
+ expects4(@alignCast(4, x));
+}
+fn expects4(x: *align(4) u32) void {
+ x.* += 1;
+}
+
+test "@alignCast slices" {
+ var array align(4) = [_]u32{
+ 1,
+ 1,
+ };
+ const slice = array[0..];
+ sliceExpectsOnly1(slice);
+ expect(slice[0] == 2);
+}
+fn sliceExpectsOnly1(slice: []align(1) u32) void {
+ sliceExpects4(@alignCast(4, slice));
+}
+fn sliceExpects4(slice: []align(4) u32) void {
+ slice[0] += 1;
+}
+
+test "implicitly decreasing fn alignment" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
+ testImplicitlyDecreaseFnAlign(alignedBig, 5678);
+}
+
+fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void {
+ expect(ptr() == answer);
+}
+
+fn alignedSmall() align(8) i32 {
+ return 1234;
+}
+fn alignedBig() align(16) i32 {
+ return 5678;
+}
+
+test "@alignCast functions" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(fnExpectsOnly1(simple4) == 0x19);
+}
+fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
+ return fnExpects4(@alignCast(4, ptr));
+}
+fn fnExpects4(ptr: fn () align(4) i32) i32 {
+ return ptr();
+}
+fn simple4() align(4) i32 {
+ return 0x19;
+}
+
+test "generic function with align param" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect(whyWouldYouEverDoThis(1) == 0x1);
+ expect(whyWouldYouEverDoThis(4) == 0x1);
+ expect(whyWouldYouEverDoThis(8) == 0x1);
+}
+
+fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
+ return 0x1;
+}
+
+test "@ptrCast preserves alignment of bigger source" {
+ var x: u32 align(16) = 1234;
+ const ptr = @ptrCast(*u8, &x);
+ expect(@TypeOf(ptr) == *align(16) u8);
+}
+
+test "runtime known array index has best alignment possible" {
+ // take full advantage of over-alignment
+ var array align(4) = [_]u8{ 1, 2, 3, 4 };
+ expect(@TypeOf(&array[0]) == *align(4) u8);
+ expect(@TypeOf(&array[1]) == *u8);
+ expect(@TypeOf(&array[2]) == *align(2) u8);
+ expect(@TypeOf(&array[3]) == *u8);
+
+ // because align is too small but we still figure out to use 2
+ var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
+ expect(@TypeOf(&bigger[0]) == *align(2) u64);
+ expect(@TypeOf(&bigger[1]) == *align(2) u64);
+ expect(@TypeOf(&bigger[2]) == *align(2) u64);
+ expect(@TypeOf(&bigger[3]) == *align(2) u64);
+
+ // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
+ var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
+ var runtime_zero: usize = 0;
+ comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
+ comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
+ testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
+
+ // has to use ABI alignment because index known at runtime only
+ testIndex2(array[runtime_zero..].ptr, 0, *u8);
+ testIndex2(array[runtime_zero..].ptr, 1, *u8);
+ testIndex2(array[runtime_zero..].ptr, 2, *u8);
+ testIndex2(array[runtime_zero..].ptr, 3, *u8);
+}
+fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void {
+ comptime expect(@TypeOf(&smaller[index]) == T);
+}
+fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void {
+ comptime expect(@TypeOf(&ptr[index]) == T);
+}
+
+test "alignstack" {
+ expect(fnWithAlignedStack() == 1234);
+}
+
+fn fnWithAlignedStack() i32 {
+ @setAlignStack(256);
+ return 1234;
+}
+
+test "alignment of structs" {
+ expect(@alignOf(struct {
+ a: i32,
+ b: *i32,
+ }) == @alignOf(usize));
+}
+
+test "alignment of function with c calling convention" {
+ var runtime_nothing = nothing;
+ const casted1 = @ptrCast(*const u8, runtime_nothing);
+ const casted2 = @ptrCast(fn () callconv(.C) void, casted1);
+ casted2();
+}
+
+fn nothing() callconv(.C) void {}
+
+test "return error union with 128-bit integer" {
+ expect(3 == try give());
+}
+fn give() anyerror!u128 {
+ return 3;
+}
+
+test "alignment of >= 128-bit integer type" {
+ expect(@alignOf(u128) == 16);
+ expect(@alignOf(u129) == 16);
+}
+
+test "alignment of struct with 128-bit field" {
+ expect(@alignOf(struct {
+ x: u128,
+ }) == 16);
+
+ comptime {
+ expect(@alignOf(struct {
+ x: u128,
+ }) == 16);
+ }
+}
+
+test "size of extern struct with 128-bit field" {
+ expect(@sizeOf(extern struct {
+ x: u128,
+ y: u8,
+ }) == 32);
+
+ comptime {
+ expect(@sizeOf(extern struct {
+ x: u128,
+ y: u8,
+ }) == 32);
+ }
+}
+
+const DefaultAligned = struct {
+ nevermind: u32,
+ badguy: i128,
+};
+
+test "read 128-bit field from default aligned struct in stack memory" {
+ var default_aligned = DefaultAligned{
+ .nevermind = 1,
+ .badguy = 12,
+ };
+ expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
+ expect(12 == default_aligned.badguy);
+}
+
+var default_aligned_global = DefaultAligned{
+ .nevermind = 1,
+ .badguy = 12,
+};
+
+test "read 128-bit field from default aligned struct in global memory" {
+ expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
+ expect(12 == default_aligned_global.badguy);
+}
+
+test "struct field explicit alignment" {
+ const S = struct {
+ const Node = struct {
+ next: *Node,
+ massive_byte: u8 align(64),
+ };
+ };
+
+ var node: S.Node = undefined;
+ node.massive_byte = 100;
+ expect(node.massive_byte == 100);
+ comptime expect(@TypeOf(&node.massive_byte) == *align(64) u8);
+ expect(@ptrToInt(&node.massive_byte) % 64 == 0);
+}
+
+test "align(@alignOf(T)) T does not force resolution of T" {
+ const S = struct {
+ const A = struct {
+ a: *align(@alignOf(A)) A,
+ };
+ fn doTheTest() void {
+ suspend {
+ resume @frame();
+ }
+ _ = bar(@Frame(doTheTest));
+ }
+ fn bar(comptime T: type) *align(@alignOf(T)) T {
+ ok = true;
+ return undefined;
+ }
+
+ var ok = false;
+ };
+ _ = async S.doTheTest();
+ expect(S.ok);
+}
+
+test "align(N) on functions" {
+ // function alignment is a compile error on wasm32/wasm64
+ if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
+
+ expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
+}
+fn overaligned_fn() align(0x1000) i32 {
+ return 42;
+}