| author | |
| committer | |
| log | 01c1f415209f5085e09430cc6df182d7eb2245ee |
| tree | 9f93de2769afd58bf1a88db0b9b266aa24bb4b66 |
| parent | b24e9b6347afc66aa94f61b3ed4c2d02cdb0d0ee |
* 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 | }; |
| 776 | 787 | ||
| 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 | } |
| 11927 | 11927 | ||
| 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; |
| 11938 | 11957 | ||
| 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]T | 11970 | // *[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 [*]T | 11974 | // *[N]T to [*]T |
| 11956 | // *[N:s]T to [*:s]T | 11975 | // *[N:s]T to [*:s]T |
| 11957 | // *[N:s]T to [*]T | 11976 | // *[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 | } |
| 12682 | 12701 | ||
| 12683 | fn coerceArrayPtrToMany( | 12702 | fn 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 | } |
| 877 | 877 | ||
| 878 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | 878 | fn 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 | } |
| 1247 | 1247 | ||
| 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 |
| 1663 | fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { | 1663 | fn 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; |
| 1665 | 1665 | ||
| 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); |
| 1669 | 1670 |
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; |
| 2715 | 2717 | ||
| 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), |
| 146 | 145 | ||
| 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), |
| 205 | 204 | ||
| 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 | } |
| 287 | 289 | ||
| 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; |
| 300 | 293 |
test/behavior.zig+2-1| ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); | ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); |
| 2 | 2 | ||
| 3 | test { | 3 | test { |
| 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 | } |
| 21 | 21 | ||
| 22 | fn derp() align(@sizeOf(usize) * 2) i32 { | ||
| 23 | return 1234; | ||
| 24 | } | ||
| 25 | fn noop1() align(1) void {} | ||
| 26 | fn noop4() align(4) void {} | ||
| 27 | |||
| 28 | test "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 | |||
| 39 | var baz: packed struct { | ||
| 40 | a: u32, | ||
| 41 | b: u32, | ||
| 42 | } = undefined; | ||
| 43 | |||
| 44 | test "packed struct alignment" { | ||
| 45 | try expect(@TypeOf(&baz.b) == *align(1) u32); | ||
| 46 | } | ||
| 47 | |||
| 48 | const blah: packed struct { | ||
| 49 | a: u3, | ||
| 50 | b: u3, | ||
| 51 | c: u2, | ||
| 52 | } = undefined; | ||
| 53 | |||
| 54 | test "bit field alignment" { | ||
| 55 | try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "default alignment allows unspecified in type syntax" { | 22 | test "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" { |
| 77 | fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { | 41 | fn 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 | |||
| 81 | test "specifying alignment allows pointer cast" { | ||
| 82 | try testBytesAlign(0x33); | ||
| 83 | } | ||
| 84 | fn 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 | |||
| 95 | test "@alignCast pointers" { | ||
| 96 | var x: u32 align(4) = 1; | ||
| 97 | expectsOnly1(&x); | ||
| 98 | try expect(x == 2); | ||
| 99 | } | ||
| 100 | fn expectsOnly1(x: *align(1) u32) void { | ||
| 101 | expects4(@alignCast(4, x)); | ||
| 102 | } | ||
| 103 | fn expects4(x: *align(4) u32) void { | ||
| 104 | x.* += 1; | ||
| 105 | } | ||
| 106 | |||
| 107 | test "@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 | } | ||
| 116 | fn sliceExpectsOnly1(slice: []align(1) u32) void { | ||
| 117 | sliceExpects4(@alignCast(4, slice)); | ||
| 118 | } | ||
| 119 | fn sliceExpects4(slice: []align(4) u32) void { | ||
| 120 | slice[0] += 1; | ||
| 121 | } | ||
| 122 | |||
| 123 | test "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 | |||
| 131 | fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) !void { | ||
| 132 | try expect(ptr() == answer); | ||
| 133 | } | ||
| 134 | |||
| 135 | fn alignedSmall() align(8) i32 { | ||
| 136 | return 1234; | ||
| 137 | } | ||
| 138 | fn alignedBig() align(16) i32 { | ||
| 139 | return 5678; | ||
| 140 | } | ||
| 141 | |||
| 142 | test "@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 | } | ||
| 149 | fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { | ||
| 150 | return fnExpects4(@alignCast(4, ptr)); | ||
| 151 | } | ||
| 152 | fn fnExpects4(ptr: fn () align(4) i32) i32 { | ||
| 153 | return ptr(); | ||
| 154 | } | ||
| 155 | fn simple4() align(4) i32 { | ||
| 156 | return 0x19; | ||
| 157 | } | ||
| 158 | |||
| 159 | test "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 | |||
| 169 | fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { | ||
| 170 | _ = align_bytes; | ||
| 171 | return 0x1; | ||
| 172 | } | ||
| 173 | |||
| 174 | test "@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 | |||
| 180 | test "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 | } | ||
| 211 | fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void { | ||
| 212 | comptime try expect(@TypeOf(&smaller[index]) == T); | ||
| 213 | } | ||
| 214 | fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void { | ||
| 215 | comptime try expect(@TypeOf(&ptr[index]) == T); | ||
| 216 | } | ||
| 217 | |||
| 218 | test "alignstack" { | ||
| 219 | try expect(fnWithAlignedStack() == 1234); | ||
| 220 | } | ||
| 221 | |||
| 222 | fn fnWithAlignedStack() i32 { | ||
| 223 | @setAlignStack(256); | ||
| 224 | return 1234; | ||
| 225 | } | ||
| 226 | |||
| 227 | test "alignment of structs" { | ||
| 228 | try expect(@alignOf(struct { | ||
| 229 | a: i32, | ||
| 230 | b: *i32, | ||
| 231 | }) == @alignOf(usize)); | ||
| 232 | } | ||
| 233 | |||
| 234 | test "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 | |||
| 241 | fn nothing() callconv(.C) void {} | ||
| 242 | |||
| 243 | test "return error union with 128-bit integer" { | ||
| 244 | try expect(3 == try give()); | ||
| 245 | } | ||
| 246 | fn give() anyerror!u128 { | ||
| 247 | return 3; | ||
| 248 | } | ||
| 249 | |||
| 250 | test "alignment of >= 128-bit integer type" { | ||
| 251 | try expect(@alignOf(u128) == 16); | ||
| 252 | try expect(@alignOf(u129) == 16); | ||
| 253 | } | ||
| 254 | |||
| 255 | test "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 | |||
| 267 | test "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 | |||
| 281 | const DefaultAligned = struct { | ||
| 282 | nevermind: u32, | ||
| 283 | badguy: i128, | ||
| 284 | }; | ||
| 285 | |||
| 286 | test "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 | |||
| 295 | var default_aligned_global = DefaultAligned{ | ||
| 296 | .nevermind = 1, | ||
| 297 | .badguy = 12, | ||
| 298 | }; | ||
| 299 | |||
| 300 | test "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 | |||
| 305 | test "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 | |||
| 320 | test "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 | |||
| 342 | test "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 | } | ||
| 349 | fn overaligned_fn() align(0x1000) i32 { | ||
| 350 | return 42; | ||
| 351 | } |
test/behavior/align_stage1.zig created+307| ... | @@ -0,0 +1,307 @@ | ||
| 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 | |||
| 6 | fn derp() align(@sizeOf(usize) * 2) i32 { | ||
| 7 | return 1234; | ||
| 8 | } | ||
| 9 | fn noop1() align(1) void {} | ||
| 10 | fn noop4() align(4) void {} | ||
| 11 | |||
| 12 | test "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 | |||
| 23 | var baz: packed struct { | ||
| 24 | a: u32, | ||
| 25 | b: u32, | ||
| 26 | } = undefined; | ||
| 27 | |||
| 28 | test "packed struct alignment" { | ||
| 29 | try expect(@TypeOf(&baz.b) == *align(1) u32); | ||
| 30 | } | ||
| 31 | |||
| 32 | const blah: packed struct { | ||
| 33 | a: u3, | ||
| 34 | b: u3, | ||
| 35 | c: u2, | ||
| 36 | } = undefined; | ||
| 37 | |||
| 38 | test "bit field alignment" { | ||
| 39 | try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); | ||
| 40 | } | ||
| 41 | |||
| 42 | test "specifying alignment allows pointer cast" { | ||
| 43 | try testBytesAlign(0x33); | ||
| 44 | } | ||
| 45 | fn 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 | |||
| 51 | test "@alignCast pointers" { | ||
| 52 | var x: u32 align(4) = 1; | ||
| 53 | expectsOnly1(&x); | ||
| 54 | try expect(x == 2); | ||
| 55 | } | ||
| 56 | fn expectsOnly1(x: *align(1) u32) void { | ||
| 57 | expects4(@alignCast(4, x)); | ||
| 58 | } | ||
| 59 | fn expects4(x: *align(4) u32) void { | ||
| 60 | x.* += 1; | ||
| 61 | } | ||
| 62 | |||
| 63 | test "@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 | } | ||
| 72 | fn sliceExpectsOnly1(slice: []align(1) u32) void { | ||
| 73 | sliceExpects4(@alignCast(4, slice)); | ||
| 74 | } | ||
| 75 | fn sliceExpects4(slice: []align(4) u32) void { | ||
| 76 | slice[0] += 1; | ||
| 77 | } | ||
| 78 | |||
| 79 | test "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 | |||
| 87 | fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) !void { | ||
| 88 | try expect(ptr() == answer); | ||
| 89 | } | ||
| 90 | |||
| 91 | fn alignedSmall() align(8) i32 { | ||
| 92 | return 1234; | ||
| 93 | } | ||
| 94 | fn alignedBig() align(16) i32 { | ||
| 95 | return 5678; | ||
| 96 | } | ||
| 97 | |||
| 98 | test "@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 | } | ||
| 105 | fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { | ||
| 106 | return fnExpects4(@alignCast(4, ptr)); | ||
| 107 | } | ||
| 108 | fn fnExpects4(ptr: fn () align(4) i32) i32 { | ||
| 109 | return ptr(); | ||
| 110 | } | ||
| 111 | fn simple4() align(4) i32 { | ||
| 112 | return 0x19; | ||
| 113 | } | ||
| 114 | |||
| 115 | test "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 | |||
| 125 | fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { | ||
| 126 | _ = align_bytes; | ||
| 127 | return 0x1; | ||
| 128 | } | ||
| 129 | |||
| 130 | test "@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 | |||
| 136 | test "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 | } | ||
| 167 | fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void { | ||
| 168 | comptime try expect(@TypeOf(&smaller[index]) == T); | ||
| 169 | } | ||
| 170 | fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void { | ||
| 171 | comptime try expect(@TypeOf(&ptr[index]) == T); | ||
| 172 | } | ||
| 173 | |||
| 174 | test "alignstack" { | ||
| 175 | try expect(fnWithAlignedStack() == 1234); | ||
| 176 | } | ||
| 177 | |||
| 178 | fn fnWithAlignedStack() i32 { | ||
| 179 | @setAlignStack(256); | ||
| 180 | return 1234; | ||
| 181 | } | ||
| 182 | |||
| 183 | test "alignment of structs" { | ||
| 184 | try expect(@alignOf(struct { | ||
| 185 | a: i32, | ||
| 186 | b: *i32, | ||
| 187 | }) == @alignOf(usize)); | ||
| 188 | } | ||
| 189 | |||
| 190 | test "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 | |||
| 197 | fn nothing() callconv(.C) void {} | ||
| 198 | |||
| 199 | test "return error union with 128-bit integer" { | ||
| 200 | try expect(3 == try give()); | ||
| 201 | } | ||
| 202 | fn give() anyerror!u128 { | ||
| 203 | return 3; | ||
| 204 | } | ||
| 205 | |||
| 206 | test "alignment of >= 128-bit integer type" { | ||
| 207 | try expect(@alignOf(u128) == 16); | ||
| 208 | try expect(@alignOf(u129) == 16); | ||
| 209 | } | ||
| 210 | |||
| 211 | test "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 | |||
| 223 | test "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 | |||
| 237 | const DefaultAligned = struct { | ||
| 238 | nevermind: u32, | ||
| 239 | badguy: i128, | ||
| 240 | }; | ||
| 241 | |||
| 242 | test "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 | |||
| 251 | var default_aligned_global = DefaultAligned{ | ||
| 252 | .nevermind = 1, | ||
| 253 | .badguy = 12, | ||
| 254 | }; | ||
| 255 | |||
| 256 | test "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 | |||
| 261 | test "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 | |||
| 276 | test "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 | |||
| 298 | test "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 | } | ||
| 305 | fn overaligned_fn() align(0x1000) i32 { | ||
| 306 | return 42; | ||
| 307 | } | ||