| 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 | 267 | .set_union_tag, |
| 268 | 268 | .min, |
| 269 | 269 | .max, |
| 270 | .slice, | |
| 271 | 270 | => { |
| 272 | 271 | const o = inst_datas[inst].bin_op; |
| 273 | 272 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); |
| ... | ... | @@ -363,7 +362,7 @@ fn analyzeInst( |
| 363 | 362 | const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; |
| 364 | 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 | 366 | const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; |
| 368 | 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 | 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 | 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 | 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 | 11947 | // Coercions where the source is a single pointer to an array. |
| 11929 | 11948 | src_array_ptr: { |
| 11930 | 11949 | if (!inst_ty.isSinglePointer()) break :src_array_ptr; |
| 11931 | const array_type = inst_ty.elemType(); | |
| 11932 | if (array_type.zigTypeTag() != .Array) break :src_array_ptr; | |
| 11933 | const array_elem_type = array_type.elemType(); | |
| 11950 | const array_ty = inst_ty.childType(); | |
| 11951 | if (array_ty.zigTypeTag() != .Array) break :src_array_ptr; | |
| 11952 | const array_elem_type = array_ty.childType(); | |
| 11934 | 11953 | const dest_is_mut = !dest_ty.isConstPtr(); |
| 11935 | 11954 | if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr; |
| 11936 | 11955 | if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr; |
| 11937 | 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 | 11959 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) { |
| 11941 | 11960 | .ok => {}, |
| 11942 | 11961 | .no_match => break :src_array_ptr, |
| ... | ... | @@ -11949,20 +11968,20 @@ fn coerce( |
| 11949 | 11968 | }, |
| 11950 | 11969 | .C => { |
| 11951 | 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 | 11973 | .Many => { |
| 11955 | 11974 | // *[N]T to [*]T |
| 11956 | 11975 | // *[N:s]T to [*:s]T |
| 11957 | 11976 | // *[N:s]T to [*]T |
| 11958 | 11977 | if (dest_ty.sentinel()) |dst_sentinel| { |
| 11959 | if (array_type.sentinel()) |src_sentinel| { | |
| 11978 | if (array_ty.sentinel()) |src_sentinel| { | |
| 11960 | 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 | 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 | 11987 | .One => {}, |
| ... | ... | @@ -12680,7 +12699,7 @@ fn coerceArrayPtrToSlice( |
| 12680 | 12699 | return block.addTyOp(.array_to_slice, dest_ty, inst); |
| 12681 | 12700 | } |
| 12682 | 12701 | |
| 12683 | fn coerceArrayPtrToMany( | |
| 12702 | fn coerceCompatiblePtrs( | |
| 12684 | 12703 | sema: *Sema, |
| 12685 | 12704 | block: *Block, |
| 12686 | 12705 | dest_ty: Type, |
| ... | ... | @@ -12888,10 +12907,15 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { |
| 12888 | 12907 | const decl_tv = try decl.typedValue(); |
| 12889 | 12908 | if (decl_tv.val.castTag(.variable)) |payload| { |
| 12890 | 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 | 12914 | const ty = try Type.ptr(sema.arena, .{ |
| 12892 | 12915 | .pointee_type = decl_tv.ty, |
| 12893 | 12916 | .mutable = variable.is_mutable, |
| 12894 | 12917 | .@"addrspace" = decl.@"addrspace", |
| 12918 | .@"align" = alignment, | |
| 12895 | 12919 | }); |
| 12896 | 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 | 876 | } |
| 877 | 877 | |
| 878 | 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 | 881 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}); |
| 881 | 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 | 1246 | } |
| 1247 | 1247 | |
| 1248 | 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 | 1251 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| 1251 | 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 | 1663 | fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1664 | 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 | 1668 | const ptr = try f.resolveInst(bin_op.lhs); |
| 1668 | 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 | 581 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { |
| 582 | 582 | _ = try self.resolveLlvmFunction(extern_fn.data); |
| 583 | 583 | } else { |
| 584 | const target = self.module.getTarget(); | |
| 584 | 585 | const global = try self.resolveGlobalDecl(decl); |
| 586 | global.setAlignment(decl.getAlignment(target)); | |
| 585 | 587 | assert(decl.has_tv); |
| 586 | 588 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { |
| 587 | 589 | const variable = payload.data; |
| ... | ... | @@ -2713,7 +2715,8 @@ pub const FuncGen = struct { |
| 2713 | 2715 | fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2714 | 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 | 2720 | const ptr = try self.resolveInst(bin_op.lhs); |
| 2718 | 2721 | const len = try self.resolveInst(bin_op.rhs); |
| 2719 | 2722 | const inst_ty = self.air.typeOfIndex(inst); |
src/print_air.zig+6-13| ... | ... | @@ -141,7 +141,6 @@ const Writer = struct { |
| 141 | 141 | .set_union_tag, |
| 142 | 142 | .min, |
| 143 | 143 | .max, |
| 144 | .slice, | |
| 145 | 144 | => try w.writeBinOp(s, inst), |
| 146 | 145 | |
| 147 | 146 | .is_null, |
| ... | ... | @@ -203,8 +202,11 @@ const Writer = struct { |
| 203 | 202 | .loop, |
| 204 | 203 | => try w.writeBlock(s, inst), |
| 205 | 204 | |
| 206 | .slice_elem_ptr => try w.writeSliceElemPtr(s, inst), | |
| 207 | .ptr_elem_ptr => try w.writePtrElemPtr(s, inst), | |
| 205 | .slice, | |
| 206 | .slice_elem_ptr, | |
| 207 | .ptr_elem_ptr, | |
| 208 | => try w.writeTyPlBin(s, inst), | |
| 209 | ||
| 208 | 210 | .struct_field_ptr => try w.writeStructField(s, inst), |
| 209 | 211 | .struct_field_val => try w.writeStructField(s, inst), |
| 210 | 212 | .constant => try w.writeConstant(s, inst), |
| ... | ... | @@ -285,16 +287,7 @@ const Writer = struct { |
| 285 | 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 { | |
| 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 { | |
| 290 | fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 298 | 291 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 299 | 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 | 2 | |
| 3 | 3 | test { |
| 4 | 4 | // Tests that pass for both. |
| 5 | _ = @import("behavior/align.zig"); | |
| 5 | 6 | _ = @import("behavior/array.zig"); |
| 6 | 7 | _ = @import("behavior/atomics.zig"); |
| 7 | 8 | _ = @import("behavior/basic.zig"); |
| ... | ... | @@ -65,7 +66,7 @@ test { |
| 65 | 66 | // When all comptime_memory.zig tests pass, #9646 can be closed. |
| 66 | 67 | // _ = @import("behavior/comptime_memory.zig"); |
| 67 | 68 | } else { |
| 68 | _ = @import("behavior/align.zig"); | |
| 69 | _ = @import("behavior/align_stage1.zig"); | |
| 69 | 70 | _ = @import("behavior/alignof.zig"); |
| 70 | 71 | _ = @import("behavior/array_stage1.zig"); |
| 71 | 72 | if (builtin.os.tag != .wasi) { |
test/behavior/align.zig-308| ... | ... | @@ -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 | 22 | test "default alignment allows unspecified in type syntax" { |
| 59 | 23 | try expect(*u32 == *align(@alignOf(u32)) u32); |
| 60 | 24 | } |
| ... | ... | @@ -77,275 +41,3 @@ test "implicitly decreasing slice alignment" { |
| 77 | 41 | fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { |
| 78 | 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 | } |