authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-22 17:12:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-22 17:12:12-07:00
log01c1f415209f5085e09430cc6df182d7eb2245ee
tree9f93de2769afd58bf1a88db0b9b266aa24bb4b66
parentb24e9b6347afc66aa94f61b3ed4c2d02cdb0d0ee

stage2: slice and alignment fixes

* Fix backend using wrong union field of the slice instruction. * LLVM backend properly sets alignment on global variables. * Sema: add coercion for *T to *[1]T * Sema: pointers to Decls with explicit alignment now have alignment metadata in them.

11 files changed, 370 insertions(+), 337 deletions(-)

src/Liveness.zig+1-2
...@@ -267,7 +267,6 @@ fn analyzeInst(...@@ -267,7 +267,6 @@ fn analyzeInst(
267 .set_union_tag,267 .set_union_tag,
268 .min,268 .min,
269 .max,269 .max,
270 .slice,
271 => {270 => {
272 const o = inst_datas[inst].bin_op;271 const o = inst_datas[inst].bin_op;
273 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });272 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
...@@ -363,7 +362,7 @@ fn analyzeInst(...@@ -363,7 +362,7 @@ fn analyzeInst(
363 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;362 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
364 return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });363 return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });
365 },364 },
366 .ptr_elem_ptr, .slice_elem_ptr => {365 .ptr_elem_ptr, .slice_elem_ptr, .slice => {
367 const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;366 const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;
368 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });367 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
369 },368 },
src/Module.zig+11
...@@ -772,6 +772,17 @@ pub const Decl = struct {...@@ -772,6 +772,17 @@ pub const Decl = struct {
772 else => false,772 else => false,
773 };773 };
774 }774 }
775
776 pub fn getAlignment(decl: Decl, target: Target) u32 {
777 assert(decl.has_tv);
778 if (decl.align_val.tag() != .null_value) {
779 // Explicit alignment.
780 return @intCast(u32, decl.align_val.toUnsignedInt());
781 } else {
782 // Natural alignment.
783 return decl.ty.abiAlignment(target);
784 }
785 }
775};786};
776787
777/// This state is attached to every Decl when Module emit_h is non-null.788/// This state is attached to every Decl when Module emit_h is non-null.
src/Sema.zig+33-9
...@@ -11925,18 +11925,37 @@ fn coerce(...@@ -11925,18 +11925,37 @@ fn coerce(
11925 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);11925 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
11926 }11926 }
1192711927
11928 // *T to *[1]T
11929 single_item: {
11930 if (!dest_ty.isSinglePointer()) break :single_item;
11931 if (!inst_ty.isSinglePointer()) break :single_item;
11932 const ptr_elem_ty = inst_ty.childType();
11933 const array_ty = dest_ty.childType();
11934 if (array_ty.zigTypeTag() != .Array) break :single_item;
11935 const array_elem_ty = array_ty.childType();
11936 const dest_is_mut = !dest_ty.isConstPtr();
11937 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
11938 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :single_item;
11939 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :single_item;
11940 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
11941 .ok => {},
11942 .no_match => break :single_item,
11943 }
11944 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
11945 }
11946
11928 // Coercions where the source is a single pointer to an array.11947 // Coercions where the source is a single pointer to an array.
11929 src_array_ptr: {11948 src_array_ptr: {
11930 if (!inst_ty.isSinglePointer()) break :src_array_ptr;11949 if (!inst_ty.isSinglePointer()) break :src_array_ptr;
11931 const array_type = inst_ty.elemType();11950 const array_ty = inst_ty.childType();
11932 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;11951 if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;
11933 const array_elem_type = array_type.elemType();11952 const array_elem_type = array_ty.childType();
11934 const dest_is_mut = !dest_ty.isConstPtr();11953 const dest_is_mut = !dest_ty.isConstPtr();
11935 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;11954 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;
11936 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;11955 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;
11937 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :src_array_ptr;11956 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :src_array_ptr;
1193811957
11939 const dst_elem_type = dest_ty.elemType();11958 const dst_elem_type = dest_ty.childType();
11940 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {11959 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
11941 .ok => {},11960 .ok => {},
11942 .no_match => break :src_array_ptr,11961 .no_match => break :src_array_ptr,
...@@ -11949,20 +11968,20 @@ fn coerce(...@@ -11949,20 +11968,20 @@ fn coerce(
11949 },11968 },
11950 .C => {11969 .C => {
11951 // *[N]T to [*c]T11970 // *[N]T to [*c]T
11952 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);11971 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
11953 },11972 },
11954 .Many => {11973 .Many => {
11955 // *[N]T to [*]T11974 // *[N]T to [*]T
11956 // *[N:s]T to [*:s]T11975 // *[N:s]T to [*:s]T
11957 // *[N:s]T to [*]T11976 // *[N:s]T to [*]T
11958 if (dest_ty.sentinel()) |dst_sentinel| {11977 if (dest_ty.sentinel()) |dst_sentinel| {
11959 if (array_type.sentinel()) |src_sentinel| {11978 if (array_ty.sentinel()) |src_sentinel| {
11960 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {11979 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
11961 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);11980 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
11962 }11981 }
11963 }11982 }
11964 } else {11983 } else {
11965 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);11984 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
11966 }11985 }
11967 },11986 },
11968 .One => {},11987 .One => {},
...@@ -12680,7 +12699,7 @@ fn coerceArrayPtrToSlice(...@@ -12680,7 +12699,7 @@ fn coerceArrayPtrToSlice(
12680 return block.addTyOp(.array_to_slice, dest_ty, inst);12699 return block.addTyOp(.array_to_slice, dest_ty, inst);
12681}12700}
1268212701
12683fn coerceArrayPtrToMany(12702fn coerceCompatiblePtrs(
12684 sema: *Sema,12703 sema: *Sema,
12685 block: *Block,12704 block: *Block,
12686 dest_ty: Type,12705 dest_ty: Type,
...@@ -12888,10 +12907,15 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {...@@ -12888,10 +12907,15 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
12888 const decl_tv = try decl.typedValue();12907 const decl_tv = try decl.typedValue();
12889 if (decl_tv.val.castTag(.variable)) |payload| {12908 if (decl_tv.val.castTag(.variable)) |payload| {
12890 const variable = payload.data;12909 const variable = payload.data;
12910 const alignment: u32 = if (decl.align_val.tag() == .null_value)
12911 0
12912 else
12913 @intCast(u32, decl.align_val.toUnsignedInt());
12891 const ty = try Type.ptr(sema.arena, .{12914 const ty = try Type.ptr(sema.arena, .{
12892 .pointee_type = decl_tv.ty,12915 .pointee_type = decl_tv.ty,
12893 .mutable = variable.is_mutable,12916 .mutable = variable.is_mutable,
12894 .@"addrspace" = decl.@"addrspace",12917 .@"addrspace" = decl.@"addrspace",
12918 .@"align" = alignment,
12895 });12919 });
12896 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));12920 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
12897 }12921 }
src/arch/aarch64/CodeGen.zig+2-1
...@@ -876,7 +876,8 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {...@@ -876,7 +876,8 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {
876}876}
877877
878fn airSlice(self: *Self, inst: Air.Inst.Index) !void {878fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
879 const bin_op = self.air.instructions.items(.data)[inst].bin_op;879 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
880 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
880 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});881 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
881 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });882 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
882}883}
src/codegen.zig+2-1
...@@ -1246,7 +1246,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1246,7 +1246,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1246 }1246 }
12471247
1248 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {1248 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1249 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1249 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1250 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1250 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1251 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1251 else => return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}),1252 else => return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}),
1252 };1253 };
src/codegen/c.zig+2-1
...@@ -1663,7 +1663,8 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu...@@ -1663,7 +1663,8 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu
1663fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {1663fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
1664 if (f.liveness.isUnused(inst)) return CValue.none;1664 if (f.liveness.isUnused(inst)) return CValue.none;
16651665
1666 const bin_op = f.air.instructions.items(.data)[inst].bin_op;1666 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1667 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1667 const ptr = try f.resolveInst(bin_op.lhs);1668 const ptr = try f.resolveInst(bin_op.lhs);
1668 const len = try f.resolveInst(bin_op.rhs);1669 const len = try f.resolveInst(bin_op.rhs);
16691670
src/codegen/llvm.zig+4-1
...@@ -581,7 +581,9 @@ pub const DeclGen = struct {...@@ -581,7 +581,9 @@ pub const DeclGen = struct {
581 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {581 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
582 _ = try self.resolveLlvmFunction(extern_fn.data);582 _ = try self.resolveLlvmFunction(extern_fn.data);
583 } else {583 } else {
584 const target = self.module.getTarget();
584 const global = try self.resolveGlobalDecl(decl);585 const global = try self.resolveGlobalDecl(decl);
586 global.setAlignment(decl.getAlignment(target));
585 assert(decl.has_tv);587 assert(decl.has_tv);
586 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {588 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
587 const variable = payload.data;589 const variable = payload.data;
...@@ -2713,7 +2715,8 @@ pub const FuncGen = struct {...@@ -2713,7 +2715,8 @@ pub const FuncGen = struct {
2713 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2715 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2714 if (self.liveness.isUnused(inst)) return null;2716 if (self.liveness.isUnused(inst)) return null;
27152717
2716 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2718 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2719 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2717 const ptr = try self.resolveInst(bin_op.lhs);2720 const ptr = try self.resolveInst(bin_op.lhs);
2718 const len = try self.resolveInst(bin_op.rhs);2721 const len = try self.resolveInst(bin_op.rhs);
2719 const inst_ty = self.air.typeOfIndex(inst);2722 const inst_ty = self.air.typeOfIndex(inst);
src/print_air.zig+6-13
...@@ -141,7 +141,6 @@ const Writer = struct {...@@ -141,7 +141,6 @@ const Writer = struct {
141 .set_union_tag,141 .set_union_tag,
142 .min,142 .min,
143 .max,143 .max,
144 .slice,
145 => try w.writeBinOp(s, inst),144 => try w.writeBinOp(s, inst),
146145
147 .is_null,146 .is_null,
...@@ -203,8 +202,11 @@ const Writer = struct {...@@ -203,8 +202,11 @@ const Writer = struct {
203 .loop,202 .loop,
204 => try w.writeBlock(s, inst),203 => try w.writeBlock(s, inst),
205204
206 .slice_elem_ptr => try w.writeSliceElemPtr(s, inst),205 .slice,
207 .ptr_elem_ptr => try w.writePtrElemPtr(s, inst),206 .slice_elem_ptr,
207 .ptr_elem_ptr,
208 => try w.writeTyPlBin(s, inst),
209
208 .struct_field_ptr => try w.writeStructField(s, inst),210 .struct_field_ptr => try w.writeStructField(s, inst),
209 .struct_field_val => try w.writeStructField(s, inst),211 .struct_field_val => try w.writeStructField(s, inst),
210 .constant => try w.writeConstant(s, inst),212 .constant => try w.writeConstant(s, inst),
...@@ -285,16 +287,7 @@ const Writer = struct {...@@ -285,16 +287,7 @@ const Writer = struct {
285 try s.print(", {d}", .{extra.field_index});287 try s.print(", {d}", .{extra.field_index});
286 }288 }
287289
288 fn writeSliceElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {290 fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
289 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
290 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
291
292 try w.writeOperand(s, inst, 0, extra.lhs);
293 try s.writeAll(", ");
294 try w.writeOperand(s, inst, 1, extra.rhs);
295 }
296
297 fn writePtrElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
298 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;291 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
299 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;292 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
300293
test/behavior.zig+2-1
...@@ -2,6 +2,7 @@ const builtin = @import("builtin");...@@ -2,6 +2,7 @@ const builtin = @import("builtin");
22
3test {3test {
4 // Tests that pass for both.4 // Tests that pass for both.
5 _ = @import("behavior/align.zig");
5 _ = @import("behavior/array.zig");6 _ = @import("behavior/array.zig");
6 _ = @import("behavior/atomics.zig");7 _ = @import("behavior/atomics.zig");
7 _ = @import("behavior/basic.zig");8 _ = @import("behavior/basic.zig");
...@@ -65,7 +66,7 @@ test {...@@ -65,7 +66,7 @@ test {
65 // When all comptime_memory.zig tests pass, #9646 can be closed.66 // When all comptime_memory.zig tests pass, #9646 can be closed.
66 // _ = @import("behavior/comptime_memory.zig");67 // _ = @import("behavior/comptime_memory.zig");
67 } else {68 } else {
68 _ = @import("behavior/align.zig");69 _ = @import("behavior/align_stage1.zig");
69 _ = @import("behavior/alignof.zig");70 _ = @import("behavior/alignof.zig");
70 _ = @import("behavior/array_stage1.zig");71 _ = @import("behavior/array_stage1.zig");
71 if (builtin.os.tag != .wasi) {72 if (builtin.os.tag != .wasi) {
test/behavior/align.zig-308
...@@ -19,42 +19,6 @@ test "global variable alignment" {...@@ -19,42 +19,6 @@ test "global variable alignment" {
19 }19 }
20}20}
2121
22fn derp() align(@sizeOf(usize) * 2) i32 {
23 return 1234;
24}
25fn noop1() align(1) void {}
26fn noop4() align(4) void {}
27
28test "function alignment" {
29 // function alignment is a compile error on wasm32/wasm64
30 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
31
32 try expect(derp() == 1234);
33 try expect(@TypeOf(noop1) == fn () align(1) void);
34 try expect(@TypeOf(noop4) == fn () align(4) void);
35 noop1();
36 noop4();
37}
38
39var baz: packed struct {
40 a: u32,
41 b: u32,
42} = undefined;
43
44test "packed struct alignment" {
45 try expect(@TypeOf(&baz.b) == *align(1) u32);
46}
47
48const blah: packed struct {
49 a: u3,
50 b: u3,
51 c: u2,
52} = undefined;
53
54test "bit field alignment" {
55 try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
56}
57
58test "default alignment allows unspecified in type syntax" {22test "default alignment allows unspecified in type syntax" {
59 try expect(*u32 == *align(@alignOf(u32)) u32);23 try expect(*u32 == *align(@alignOf(u32)) u32);
60}24}
...@@ -77,275 +41,3 @@ test "implicitly decreasing slice alignment" {...@@ -77,275 +41,3 @@ test "implicitly decreasing slice alignment" {
77fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {41fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
78 return a[0] + b[0];42 return a[0] + b[0];
79}43}
80
81test "specifying alignment allows pointer cast" {
82 try testBytesAlign(0x33);
83}
84fn testBytesAlign(b: u8) !void {
85 var bytes align(4) = [_]u8{
86 b,
87 b,
88 b,
89 b,
90 };
91 const ptr = @ptrCast(*u32, &bytes[0]);
92 try expect(ptr.* == 0x33333333);
93}
94
95test "@alignCast pointers" {
96 var x: u32 align(4) = 1;
97 expectsOnly1(&x);
98 try expect(x == 2);
99}
100fn expectsOnly1(x: *align(1) u32) void {
101 expects4(@alignCast(4, x));
102}
103fn expects4(x: *align(4) u32) void {
104 x.* += 1;
105}
106
107test "@alignCast slices" {
108 var array align(4) = [_]u32{
109 1,
110 1,
111 };
112 const slice = array[0..];
113 sliceExpectsOnly1(slice);
114 try expect(slice[0] == 2);
115}
116fn sliceExpectsOnly1(slice: []align(1) u32) void {
117 sliceExpects4(@alignCast(4, slice));
118}
119fn sliceExpects4(slice: []align(4) u32) void {
120 slice[0] += 1;
121}
122
123test "implicitly decreasing fn alignment" {
124 // function alignment is a compile error on wasm32/wasm64
125 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
126
127 try testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
128 try testImplicitlyDecreaseFnAlign(alignedBig, 5678);
129}
130
131fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) !void {
132 try expect(ptr() == answer);
133}
134
135fn alignedSmall() align(8) i32 {
136 return 1234;
137}
138fn alignedBig() align(16) i32 {
139 return 5678;
140}
141
142test "@alignCast functions" {
143 // function alignment is a compile error on wasm32/wasm64
144 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
145 if (native_arch == .thumb) return error.SkipZigTest;
146
147 try expect(fnExpectsOnly1(simple4) == 0x19);
148}
149fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
150 return fnExpects4(@alignCast(4, ptr));
151}
152fn fnExpects4(ptr: fn () align(4) i32) i32 {
153 return ptr();
154}
155fn simple4() align(4) i32 {
156 return 0x19;
157}
158
159test "generic function with align param" {
160 // function alignment is a compile error on wasm32/wasm64
161 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
162 if (native_arch == .thumb) return error.SkipZigTest;
163
164 try expect(whyWouldYouEverDoThis(1) == 0x1);
165 try expect(whyWouldYouEverDoThis(4) == 0x1);
166 try expect(whyWouldYouEverDoThis(8) == 0x1);
167}
168
169fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
170 _ = align_bytes;
171 return 0x1;
172}
173
174test "@ptrCast preserves alignment of bigger source" {
175 var x: u32 align(16) = 1234;
176 const ptr = @ptrCast(*u8, &x);
177 try expect(@TypeOf(ptr) == *align(16) u8);
178}
179
180test "runtime known array index has best alignment possible" {
181 // take full advantage of over-alignment
182 var array align(4) = [_]u8{ 1, 2, 3, 4 };
183 try expect(@TypeOf(&array[0]) == *align(4) u8);
184 try expect(@TypeOf(&array[1]) == *u8);
185 try expect(@TypeOf(&array[2]) == *align(2) u8);
186 try expect(@TypeOf(&array[3]) == *u8);
187
188 // because align is too small but we still figure out to use 2
189 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
190 try expect(@TypeOf(&bigger[0]) == *align(2) u64);
191 try expect(@TypeOf(&bigger[1]) == *align(2) u64);
192 try expect(@TypeOf(&bigger[2]) == *align(2) u64);
193 try expect(@TypeOf(&bigger[3]) == *align(2) u64);
194
195 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
196 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
197 var runtime_zero: usize = 0;
198 comptime try expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
199 comptime try expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
200 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
201 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
202 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
203 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
204
205 // has to use ABI alignment because index known at runtime only
206 try testIndex2(array[runtime_zero..].ptr, 0, *u8);
207 try testIndex2(array[runtime_zero..].ptr, 1, *u8);
208 try testIndex2(array[runtime_zero..].ptr, 2, *u8);
209 try testIndex2(array[runtime_zero..].ptr, 3, *u8);
210}
211fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {
212 comptime try expect(@TypeOf(&smaller[index]) == T);
213}
214fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void {
215 comptime try expect(@TypeOf(&ptr[index]) == T);
216}
217
218test "alignstack" {
219 try expect(fnWithAlignedStack() == 1234);
220}
221
222fn fnWithAlignedStack() i32 {
223 @setAlignStack(256);
224 return 1234;
225}
226
227test "alignment of structs" {
228 try expect(@alignOf(struct {
229 a: i32,
230 b: *i32,
231 }) == @alignOf(usize));
232}
233
234test "alignment of function with c calling convention" {
235 var runtime_nothing = nothing;
236 const casted1 = @ptrCast(*const u8, runtime_nothing);
237 const casted2 = @ptrCast(fn () callconv(.C) void, casted1);
238 casted2();
239}
240
241fn nothing() callconv(.C) void {}
242
243test "return error union with 128-bit integer" {
244 try expect(3 == try give());
245}
246fn give() anyerror!u128 {
247 return 3;
248}
249
250test "alignment of >= 128-bit integer type" {
251 try expect(@alignOf(u128) == 16);
252 try expect(@alignOf(u129) == 16);
253}
254
255test "alignment of struct with 128-bit field" {
256 try expect(@alignOf(struct {
257 x: u128,
258 }) == 16);
259
260 comptime {
261 try expect(@alignOf(struct {
262 x: u128,
263 }) == 16);
264 }
265}
266
267test "size of extern struct with 128-bit field" {
268 try expect(@sizeOf(extern struct {
269 x: u128,
270 y: u8,
271 }) == 32);
272
273 comptime {
274 try expect(@sizeOf(extern struct {
275 x: u128,
276 y: u8,
277 }) == 32);
278 }
279}
280
281const DefaultAligned = struct {
282 nevermind: u32,
283 badguy: i128,
284};
285
286test "read 128-bit field from default aligned struct in stack memory" {
287 var default_aligned = DefaultAligned{
288 .nevermind = 1,
289 .badguy = 12,
290 };
291 try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
292 try expect(12 == default_aligned.badguy);
293}
294
295var default_aligned_global = DefaultAligned{
296 .nevermind = 1,
297 .badguy = 12,
298};
299
300test "read 128-bit field from default aligned struct in global memory" {
301 try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
302 try expect(12 == default_aligned_global.badguy);
303}
304
305test "struct field explicit alignment" {
306 const S = struct {
307 const Node = struct {
308 next: *Node,
309 massive_byte: u8 align(64),
310 };
311 };
312
313 var node: S.Node = undefined;
314 node.massive_byte = 100;
315 try expect(node.massive_byte == 100);
316 comptime try expect(@TypeOf(&node.massive_byte) == *align(64) u8);
317 try expect(@ptrToInt(&node.massive_byte) % 64 == 0);
318}
319
320test "align(@alignOf(T)) T does not force resolution of T" {
321 const S = struct {
322 const A = struct {
323 a: *align(@alignOf(A)) A,
324 };
325 fn doTheTest() void {
326 suspend {
327 resume @frame();
328 }
329 _ = bar(@Frame(doTheTest));
330 }
331 fn bar(comptime T: type) *align(@alignOf(T)) T {
332 ok = true;
333 return undefined;
334 }
335
336 var ok = false;
337 };
338 _ = async S.doTheTest();
339 try expect(S.ok);
340}
341
342test "align(N) on functions" {
343 // function alignment is a compile error on wasm32/wasm64
344 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
345 if (native_arch == .thumb) return error.SkipZigTest;
346
347 try expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
348}
349fn overaligned_fn() align(0x1000) i32 {
350 return 42;
351}
test/behavior/align_stage1.zig created+307
...@@ -0,0 +1,307 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const builtin = @import("builtin");
4const native_arch = builtin.target.cpu.arch;
5
6fn derp() align(@sizeOf(usize) * 2) i32 {
7 return 1234;
8}
9fn noop1() align(1) void {}
10fn noop4() align(4) void {}
11
12test "function alignment" {
13 // function alignment is a compile error on wasm32/wasm64
14 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
15
16 try expect(derp() == 1234);
17 try expect(@TypeOf(noop1) == fn () align(1) void);
18 try expect(@TypeOf(noop4) == fn () align(4) void);
19 noop1();
20 noop4();
21}
22
23var baz: packed struct {
24 a: u32,
25 b: u32,
26} = undefined;
27
28test "packed struct alignment" {
29 try expect(@TypeOf(&baz.b) == *align(1) u32);
30}
31
32const blah: packed struct {
33 a: u3,
34 b: u3,
35 c: u2,
36} = undefined;
37
38test "bit field alignment" {
39 try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
40}
41
42test "specifying alignment allows pointer cast" {
43 try testBytesAlign(0x33);
44}
45fn testBytesAlign(b: u8) !void {
46 var bytes align(4) = [_]u8{ b, b, b, b };
47 const ptr = @ptrCast(*u32, &bytes[0]);
48 try expect(ptr.* == 0x33333333);
49}
50
51test "@alignCast pointers" {
52 var x: u32 align(4) = 1;
53 expectsOnly1(&x);
54 try expect(x == 2);
55}
56fn expectsOnly1(x: *align(1) u32) void {
57 expects4(@alignCast(4, x));
58}
59fn expects4(x: *align(4) u32) void {
60 x.* += 1;
61}
62
63test "@alignCast slices" {
64 var array align(4) = [_]u32{
65 1,
66 1,
67 };
68 const slice = array[0..];
69 sliceExpectsOnly1(slice);
70 try expect(slice[0] == 2);
71}
72fn sliceExpectsOnly1(slice: []align(1) u32) void {
73 sliceExpects4(@alignCast(4, slice));
74}
75fn sliceExpects4(slice: []align(4) u32) void {
76 slice[0] += 1;
77}
78
79test "implicitly decreasing fn alignment" {
80 // function alignment is a compile error on wasm32/wasm64
81 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
82
83 try testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
84 try testImplicitlyDecreaseFnAlign(alignedBig, 5678);
85}
86
87fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) !void {
88 try expect(ptr() == answer);
89}
90
91fn alignedSmall() align(8) i32 {
92 return 1234;
93}
94fn alignedBig() align(16) i32 {
95 return 5678;
96}
97
98test "@alignCast functions" {
99 // function alignment is a compile error on wasm32/wasm64
100 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
101 if (native_arch == .thumb) return error.SkipZigTest;
102
103 try expect(fnExpectsOnly1(simple4) == 0x19);
104}
105fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 {
106 return fnExpects4(@alignCast(4, ptr));
107}
108fn fnExpects4(ptr: fn () align(4) i32) i32 {
109 return ptr();
110}
111fn simple4() align(4) i32 {
112 return 0x19;
113}
114
115test "generic function with align param" {
116 // function alignment is a compile error on wasm32/wasm64
117 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
118 if (native_arch == .thumb) return error.SkipZigTest;
119
120 try expect(whyWouldYouEverDoThis(1) == 0x1);
121 try expect(whyWouldYouEverDoThis(4) == 0x1);
122 try expect(whyWouldYouEverDoThis(8) == 0x1);
123}
124
125fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
126 _ = align_bytes;
127 return 0x1;
128}
129
130test "@ptrCast preserves alignment of bigger source" {
131 var x: u32 align(16) = 1234;
132 const ptr = @ptrCast(*u8, &x);
133 try expect(@TypeOf(ptr) == *align(16) u8);
134}
135
136test "runtime known array index has best alignment possible" {
137 // take full advantage of over-alignment
138 var array align(4) = [_]u8{ 1, 2, 3, 4 };
139 try expect(@TypeOf(&array[0]) == *align(4) u8);
140 try expect(@TypeOf(&array[1]) == *u8);
141 try expect(@TypeOf(&array[2]) == *align(2) u8);
142 try expect(@TypeOf(&array[3]) == *u8);
143
144 // because align is too small but we still figure out to use 2
145 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
146 try expect(@TypeOf(&bigger[0]) == *align(2) u64);
147 try expect(@TypeOf(&bigger[1]) == *align(2) u64);
148 try expect(@TypeOf(&bigger[2]) == *align(2) u64);
149 try expect(@TypeOf(&bigger[3]) == *align(2) u64);
150
151 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
152 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
153 var runtime_zero: usize = 0;
154 comptime try expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
155 comptime try expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
156 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
157 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
158 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
159 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
160
161 // has to use ABI alignment because index known at runtime only
162 try testIndex2(array[runtime_zero..].ptr, 0, *u8);
163 try testIndex2(array[runtime_zero..].ptr, 1, *u8);
164 try testIndex2(array[runtime_zero..].ptr, 2, *u8);
165 try testIndex2(array[runtime_zero..].ptr, 3, *u8);
166}
167fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {
168 comptime try expect(@TypeOf(&smaller[index]) == T);
169}
170fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void {
171 comptime try expect(@TypeOf(&ptr[index]) == T);
172}
173
174test "alignstack" {
175 try expect(fnWithAlignedStack() == 1234);
176}
177
178fn fnWithAlignedStack() i32 {
179 @setAlignStack(256);
180 return 1234;
181}
182
183test "alignment of structs" {
184 try expect(@alignOf(struct {
185 a: i32,
186 b: *i32,
187 }) == @alignOf(usize));
188}
189
190test "alignment of function with c calling convention" {
191 var runtime_nothing = nothing;
192 const casted1 = @ptrCast(*const u8, runtime_nothing);
193 const casted2 = @ptrCast(fn () callconv(.C) void, casted1);
194 casted2();
195}
196
197fn nothing() callconv(.C) void {}
198
199test "return error union with 128-bit integer" {
200 try expect(3 == try give());
201}
202fn give() anyerror!u128 {
203 return 3;
204}
205
206test "alignment of >= 128-bit integer type" {
207 try expect(@alignOf(u128) == 16);
208 try expect(@alignOf(u129) == 16);
209}
210
211test "alignment of struct with 128-bit field" {
212 try expect(@alignOf(struct {
213 x: u128,
214 }) == 16);
215
216 comptime {
217 try expect(@alignOf(struct {
218 x: u128,
219 }) == 16);
220 }
221}
222
223test "size of extern struct with 128-bit field" {
224 try expect(@sizeOf(extern struct {
225 x: u128,
226 y: u8,
227 }) == 32);
228
229 comptime {
230 try expect(@sizeOf(extern struct {
231 x: u128,
232 y: u8,
233 }) == 32);
234 }
235}
236
237const DefaultAligned = struct {
238 nevermind: u32,
239 badguy: i128,
240};
241
242test "read 128-bit field from default aligned struct in stack memory" {
243 var default_aligned = DefaultAligned{
244 .nevermind = 1,
245 .badguy = 12,
246 };
247 try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0);
248 try expect(12 == default_aligned.badguy);
249}
250
251var default_aligned_global = DefaultAligned{
252 .nevermind = 1,
253 .badguy = 12,
254};
255
256test "read 128-bit field from default aligned struct in global memory" {
257 try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0);
258 try expect(12 == default_aligned_global.badguy);
259}
260
261test "struct field explicit alignment" {
262 const S = struct {
263 const Node = struct {
264 next: *Node,
265 massive_byte: u8 align(64),
266 };
267 };
268
269 var node: S.Node = undefined;
270 node.massive_byte = 100;
271 try expect(node.massive_byte == 100);
272 comptime try expect(@TypeOf(&node.massive_byte) == *align(64) u8);
273 try expect(@ptrToInt(&node.massive_byte) % 64 == 0);
274}
275
276test "align(@alignOf(T)) T does not force resolution of T" {
277 const S = struct {
278 const A = struct {
279 a: *align(@alignOf(A)) A,
280 };
281 fn doTheTest() void {
282 suspend {
283 resume @frame();
284 }
285 _ = bar(@Frame(doTheTest));
286 }
287 fn bar(comptime T: type) *align(@alignOf(T)) T {
288 ok = true;
289 return undefined;
290 }
291
292 var ok = false;
293 };
294 _ = async S.doTheTest();
295 try expect(S.ok);
296}
297
298test "align(N) on functions" {
299 // function alignment is a compile error on wasm32/wasm64
300 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
301 if (native_arch == .thumb) return error.SkipZigTest;
302
303 try expect((@ptrToInt(overaligned_fn) & (0x1000 - 1)) == 0);
304}
305fn overaligned_fn() align(0x1000) i32 {
306 return 42;
307}