authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-22 17:50:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-22 17:50:36-07:00
logd97928bf52a217f75c359ae9b1513bd9d42c82d3
tree1f88eea918dfbb1615fa3716088ebf99cc586cb3
parent01c1f415209f5085e09430cc6df182d7eb2245ee

stage2: implement aligned variables and `@alignCast`

* Sema: implement zirAllocExtended * Sema: implement zirAlignCast

3 files changed, 172 insertions(+), 103 deletions(-)

src/Sema.zig+93-21
...@@ -1940,7 +1940,7 @@ fn zirRetPtr(...@@ -1940,7 +1940,7 @@ fn zirRetPtr(
1940 try sema.requireFunctionBlock(block, src);1940 try sema.requireFunctionBlock(block, src);
19411941
1942 if (block.is_comptime) {1942 if (block.is_comptime) {
1943 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);1943 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty, 0);
1944 }1944 }
19451945
1946 const ptr_type = try Type.ptr(sema.arena, .{1946 const ptr_type = try Type.ptr(sema.arena, .{
...@@ -2067,9 +2067,7 @@ fn zirAllocExtended(...@@ -2067,9 +2067,7 @@ fn zirAllocExtended(
2067 const type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);2067 const type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
2068 extra_index += 1;2068 extra_index += 1;
2069 break :blk try sema.resolveType(block, ty_src, type_ref);2069 break :blk try sema.resolveType(block, ty_src, type_ref);
2070 } else {2070 } else undefined;
2071 return sema.fail(block, src, "TODO implement Sema.zirAllocExtended inferred", .{});
2072 };
20732071
2074 const alignment: u16 = if (small.has_align) blk: {2072 const alignment: u16 = if (small.has_align) blk: {
2075 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);2073 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
...@@ -2078,22 +2076,47 @@ fn zirAllocExtended(...@@ -2078,22 +2076,47 @@ fn zirAllocExtended(
2078 break :blk alignment;2076 break :blk alignment;
2079 } else 0;2077 } else 0;
20802078
2079 const inferred_alloc_ty = if (small.is_const)
2080 Type.initTag(.inferred_alloc_const)
2081 else
2082 Type.initTag(.inferred_alloc_mut);
2083
2081 if (small.is_comptime) {2084 if (small.is_comptime) {
2082 return sema.fail(block, src, "TODO implement Sema.zirAllocExtended comptime", .{});2085 if (small.has_type) {
2086 return sema.analyzeComptimeAlloc(block, var_ty, alignment);
2087 } else {
2088 return sema.addConstant(
2089 inferred_alloc_ty,
2090 try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
2091 );
2092 }
2083 }2093 }
20842094
2085 if (!small.is_const) {2095 if (small.has_type) {
2086 return sema.fail(block, src, "TODO implement Sema.zirAllocExtended var", .{});2096 if (!small.is_const) {
2097 try sema.validateVarType(block, ty_src, var_ty, false);
2098 }
2099 const ptr_type = try Type.ptr(sema.arena, .{
2100 .pointee_type = var_ty,
2101 .@"align" = alignment,
2102 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
2103 });
2104 try sema.requireRuntimeBlock(block, src);
2105 try sema.resolveTypeLayout(block, src, var_ty);
2106 return block.addTy(.alloc, ptr_type);
2087 }2107 }
20882108
2089 const ptr_type = try Type.ptr(sema.arena, .{2109 // `Sema.addConstant` does not add the instruction to the block because it is
2090 .pointee_type = var_ty,2110 // not needed in the case of constant values. However here, we plan to "downgrade"
2091 .@"align" = alignment,2111 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
2092 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2112 // to the block even though it is currently a `.constant`.
2093 });2113 const result = try sema.addConstant(
2094 try sema.requireRuntimeBlock(block, src);2114 inferred_alloc_ty,
2095 try sema.resolveTypeLayout(block, src, var_ty);2115 try Value.Tag.inferred_alloc.create(sema.arena, .{}),
2096 return block.addTy(.alloc, ptr_type);2116 );
2117 try sema.requireFunctionBlock(block, src);
2118 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
2119 return result;
2097}2120}
20982121
2099fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2122fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -2103,7 +2126,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -2103,7 +2126,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
2103 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2126 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2104 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2127 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2105 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);2128 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2106 return sema.analyzeComptimeAlloc(block, var_ty);2129 return sema.analyzeComptimeAlloc(block, var_ty, 0);
2107}2130}
21082131
2109fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2132fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -2125,7 +2148,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -2125,7 +2148,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
2125 const var_decl_src = inst_data.src();2148 const var_decl_src = inst_data.src();
2126 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);2149 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2127 if (block.is_comptime) {2150 if (block.is_comptime) {
2128 return sema.analyzeComptimeAlloc(block, var_ty);2151 return sema.analyzeComptimeAlloc(block, var_ty, 0);
2129 }2152 }
2130 const ptr_type = try Type.ptr(sema.arena, .{2153 const ptr_type = try Type.ptr(sema.arena, .{
2131 .pointee_type = var_ty,2154 .pointee_type = var_ty,
...@@ -2145,7 +2168,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -2145,7 +2168,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
2145 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2168 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2146 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);2169 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2147 if (block.is_comptime) {2170 if (block.is_comptime) {
2148 return sema.analyzeComptimeAlloc(block, var_ty);2171 return sema.analyzeComptimeAlloc(block, var_ty, 0);
2149 }2172 }
2150 try sema.validateVarType(block, ty_src, var_ty, false);2173 try sema.validateVarType(block, ty_src, var_ty, false);
2151 const ptr_type = try Type.ptr(sema.arena, .{2174 const ptr_type = try Type.ptr(sema.arena, .{
...@@ -9436,8 +9459,10 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9436,8 +9459,10 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9436 const inst_data = sema.code.instructions.items(.data)[inst].un_node;9459 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9437 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };9460 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
9438 const ty = try sema.resolveType(block, operand_src, inst_data.operand);9461 const ty = try sema.resolveType(block, operand_src, inst_data.operand);
9462 const resolved_ty = try sema.resolveTypeFields(block, operand_src, ty);
9463 try sema.resolveTypeLayout(block, operand_src, resolved_ty);
9439 const target = sema.mod.getTarget();9464 const target = sema.mod.getTarget();
9440 const abi_align = ty.abiAlignment(target);9465 const abi_align = resolved_ty.abiAlignment(target);
9441 return sema.addIntUnsigned(Type.comptime_int, abi_align);9466 return sema.addIntUnsigned(Type.comptime_int, abi_align);
9442}9467}
94439468
...@@ -9735,8 +9760,33 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -9735,8 +9760,33 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
97359760
9736fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9761fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9737 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;9762 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9738 const src = inst_data.src();9763 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9739 return sema.fail(block, src, "TODO: Sema.zirAlignCast", .{});9764 const align_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
9765 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
9766 const dest_align = try sema.resolveAlign(block, align_src, extra.lhs);
9767 const ptr = sema.resolveInst(extra.rhs);
9768 const ptr_ty = sema.typeOf(ptr);
9769
9770 // TODO in addition to pointers, this instruction is supposed to work for
9771 // pointer-like optionals and slices.
9772 try sema.checkPtrType(block, ptr_src, ptr_ty);
9773
9774 // TODO compile error if the result pointer is comptime known and would have an
9775 // alignment that disagrees with the Decl's alignment.
9776
9777 // TODO insert safety check that the alignment is correct
9778
9779 const ptr_info = ptr_ty.ptrInfo().data;
9780 const dest_ty = try Type.ptr(sema.arena, .{
9781 .pointee_type = ptr_info.pointee_type,
9782 .@"align" = dest_align,
9783 .@"addrspace" = ptr_info.@"addrspace",
9784 .mutable = ptr_info.mutable,
9785 .@"allowzero" = ptr_info.@"allowzero",
9786 .@"volatile" = ptr_info.@"volatile",
9787 .size = ptr_info.size,
9788 });
9789 return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
9740}9790}
97419791
9742fn zirClz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9792fn zirClz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9838,6 +9888,18 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr...@@ -9838,6 +9888,18 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
9838 }9888 }
9839}9889}
98409890
9891fn checkPtrType(
9892 sema: *Sema,
9893 block: *Block,
9894 ty_src: LazySrcLoc,
9895 ty: Type,
9896) CompileError!void {
9897 switch (ty.zigTypeTag()) {
9898 .Pointer => {},
9899 else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}),
9900 }
9901}
9902
9841fn checkFloatType(9903fn checkFloatType(
9842 sema: *Sema,9904 sema: *Sema,
9843 block: *Block,9905 block: *Block,
...@@ -14630,14 +14692,22 @@ fn analyzeComptimeAlloc(...@@ -14630,14 +14692,22 @@ fn analyzeComptimeAlloc(
14630 sema: *Sema,14692 sema: *Sema,
14631 block: *Block,14693 block: *Block,
14632 var_type: Type,14694 var_type: Type,
14695 alignment: u32,
14633) CompileError!Air.Inst.Ref {14696) CompileError!Air.Inst.Ref {
14634 const ptr_type = try Type.ptr(sema.arena, .{14697 const ptr_type = try Type.ptr(sema.arena, .{
14635 .pointee_type = var_type,14698 .pointee_type = var_type,
14636 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),14699 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
14700 .@"align" = alignment,
14637 });14701 });
1463814702
14639 var anon_decl = try block.startAnonDecl();14703 var anon_decl = try block.startAnonDecl();
14640 defer anon_decl.deinit();14704 defer anon_decl.deinit();
14705
14706 const align_val = if (alignment == 0)
14707 Value.@"null"
14708 else
14709 try Value.Tag.int_u64.create(anon_decl.arena(), alignment);
14710
14641 const decl = try anon_decl.finish(14711 const decl = try anon_decl.finish(
14642 try var_type.copy(anon_decl.arena()),14712 try var_type.copy(anon_decl.arena()),
14643 // There will be stores before the first load, but they may be to sub-elements or14713 // There will be stores before the first load, but they may be to sub-elements or
...@@ -14645,6 +14715,8 @@ fn analyzeComptimeAlloc(...@@ -14645,6 +14715,8 @@ fn analyzeComptimeAlloc(
14645 // into fields/elements and have those overridden with stored values.14715 // into fields/elements and have those overridden with stored values.
14646 Value.undef,14716 Value.undef,
14647 );14717 );
14718 decl.align_val = align_val;
14719
14648 try sema.mod.declareDeclDependency(sema.owner_decl, decl);14720 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
14649 return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{14721 return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{
14650 .runtime_index = block.runtime_index,14722 .runtime_index = block.runtime_index,
test/behavior/align.zig+79
...@@ -41,3 +41,82 @@ test "implicitly decreasing slice alignment" {...@@ -41,3 +41,82 @@ test "implicitly decreasing slice alignment" {
41fn 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 {
42 return a[0] + b[0];42 return a[0] + b[0];
43}43}
44
45test "@alignCast pointers" {
46 var x: u32 align(4) = 1;
47 expectsOnly1(&x);
48 try expect(x == 2);
49}
50fn expectsOnly1(x: *align(1) u32) void {
51 expects4(@alignCast(4, x));
52}
53fn expects4(x: *align(4) u32) void {
54 x.* += 1;
55}
56
57test "specifying alignment allows pointer cast" {
58 try testBytesAlign(0x33);
59}
60fn testBytesAlign(b: u8) !void {
61 var bytes align(4) = [_]u8{ b, b, b, b };
62 const ptr = @ptrCast(*u32, &bytes[0]);
63 try expect(ptr.* == 0x33333333);
64}
65
66test "@alignCast slices" {
67 var array align(4) = [_]u32{ 1, 1 };
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 "alignment of structs" {
80 try expect(@alignOf(struct {
81 a: i32,
82 b: *i32,
83 }) == @alignOf(usize));
84}
85
86test "return error union with 128-bit integer" {
87 try expect(3 == try give());
88}
89fn give() anyerror!u128 {
90 return 3;
91}
92
93test "alignment of >= 128-bit integer type" {
94 try expect(@alignOf(u128) == 16);
95 try expect(@alignOf(u129) == 16);
96}
97
98test "alignment of struct with 128-bit field" {
99 try expect(@alignOf(struct {
100 x: u128,
101 }) == 16);
102
103 comptime {
104 try expect(@alignOf(struct {
105 x: u128,
106 }) == 16);
107 }
108}
109
110test "size of extern struct with 128-bit field" {
111 try expect(@sizeOf(extern struct {
112 x: u128,
113 y: u8,
114 }) == 32);
115
116 comptime {
117 try expect(@sizeOf(extern struct {
118 x: u128,
119 y: u8,
120 }) == 32);
121 }
122}
test/behavior/align_stage1.zig-82
...@@ -39,43 +39,6 @@ test "bit field alignment" {...@@ -39,43 +39,6 @@ test "bit field alignment" {
39 try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);39 try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
40}40}
4141
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" {42test "implicitly decreasing fn alignment" {
80 // function alignment is a compile error on wasm32/wasm6443 // function alignment is a compile error on wasm32/wasm64
81 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;44 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
...@@ -180,13 +143,6 @@ fn fnWithAlignedStack() i32 {...@@ -180,13 +143,6 @@ fn fnWithAlignedStack() i32 {
180 return 1234;143 return 1234;
181}144}
182145
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" {146test "alignment of function with c calling convention" {
191 var runtime_nothing = nothing;147 var runtime_nothing = nothing;
192 const casted1 = @ptrCast(*const u8, runtime_nothing);148 const casted1 = @ptrCast(*const u8, runtime_nothing);
...@@ -196,44 +152,6 @@ test "alignment of function with c calling convention" {...@@ -196,44 +152,6 @@ test "alignment of function with c calling convention" {
196152
197fn nothing() callconv(.C) void {}153fn nothing() callconv(.C) void {}
198154
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 {155const DefaultAligned = struct {
238 nevermind: u32,156 nevermind: u32,
239 badguy: i128,157 badguy: i128,