authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-06 00:52:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-06 00:52:10-07:00
log8c6175c1343a00278efc029a0be4091ff505dc3d
tree63f9c87d0f37757f5c0c0b543fc94ea4fe68ae22
parent713d2a9b3883942491b40738245232680877cc66

Sema: const inferred alloc infers comptime-ness

const locals now detect if the value ends up being comptime known. In such case, it replaces the runtime AIR instructions with a decl_ref const. In the backends, some more sophisticated logic for marking decls as alive was needed to prevent Decls incorrectly being garbage collected that were indirectly referenced in such manner.

7 files changed, 135 insertions(+), 28 deletions(-)

src/Sema.zig+50
......@@ -2427,7 +2427,57 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
24272427
24282428 if (var_is_mut) {
24292429 try sema.validateVarType(block, ty_src, final_elem_ty, false);
2430 } else ct: {
2431 // Detect if the value is comptime known. In such case, the
2432 // last 3 AIR instructions of the block will look like this:
2433 //
2434 // %a = constant
2435 // %b = bitcast(%a)
2436 // %c = store(%b, %d)
2437 //
2438 // If `%d` is comptime-known, then we want to store the value
2439 // inside an anonymous Decl and then erase these three AIR
2440 // instructions from the block, replacing the inst_map entry
2441 // corresponding to the ZIR alloc instruction with a constant
2442 // decl_ref pointing at our new Decl.
2443 if (block.instructions.items.len < 3) break :ct;
2444 // zig fmt: off
2445 const const_inst = block.instructions.items[block.instructions.items.len - 3];
2446 const bitcast_inst = block.instructions.items[block.instructions.items.len - 2];
2447 const store_inst = block.instructions.items[block.instructions.items.len - 1];
2448 const air_tags = sema.air_instructions.items(.tag);
2449 const air_datas = sema.air_instructions.items(.data);
2450 if (air_tags[const_inst] != .constant) break :ct;
2451 if (air_tags[bitcast_inst] != .bitcast ) break :ct;
2452 if (air_tags[store_inst] != .store ) break :ct;
2453 // zig fmt: on
2454 const store_op = air_datas[store_inst].bin_op;
2455 const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct;
2456 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
2457 if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
2458
2459 const bitcast_ty_ref = air_datas[bitcast_inst].ty_op.ty;
2460
2461 const new_decl = d: {
2462 var anon_decl = try block.startAnonDecl();
2463 defer anon_decl.deinit();
2464 const new_decl = try anon_decl.finish(
2465 try final_elem_ty.copy(anon_decl.arena()),
2466 try store_val.copy(anon_decl.arena()),
2467 );
2468 break :d new_decl;
2469 };
2470 try sema.mod.declareDeclDependency(sema.owner_decl, new_decl);
2471
2472 // Even though we reuse the constant instruction, we still remove it from the
2473 // block so that codegen does not see it.
2474 block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
2475 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);
2476 air_datas[ptr_inst].ty_pl.ty = bitcast_ty_ref;
2477
2478 return;
24302479 }
2480
24312481 // Change it to a normal alloc.
24322482 const final_ptr_ty = try Type.ptr(sema.arena, .{
24332483 .pointee_type = final_elem_ty,
src/arch/wasm/CodeGen.zig+15-2
......@@ -1047,7 +1047,7 @@ fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerErro
10471047 const offset = @intCast(u32, self.code.items.len);
10481048 const atom = &self.decl.link.wasm;
10491049 const target_sym_index = decl.link.wasm.sym_index;
1050 decl.alive = true;
1050 markDeclAlive(decl);
10511051 if (decl.ty.zigTypeTag() == .Fn) {
10521052 // We found a function pointer, so add it to our table,
10531053 // as function pointers are not allowed to be stored inside the data section,
......@@ -1876,7 +1876,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
18761876 try self.emitConstant(slice.data.len, Type.usize);
18771877 } else if (val.castTag(.decl_ref)) |payload| {
18781878 const decl = payload.data;
1879 decl.alive = true;
1879 markDeclAlive(decl);
18801880 // Function pointers use a table index, rather than a memory address
18811881 if (decl.ty.zigTypeTag() == .Fn) {
18821882 const target_sym_index = decl.link.wasm.sym_index;
......@@ -1985,6 +1985,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
19851985 }
19861986}
19871987
1988fn markDeclAlive(decl: *Decl) void {
1989 if (decl.alive) return;
1990 decl.alive = true;
1991
1992 // This is the first time we are marking this Decl alive. We must
1993 // therefore recurse into its value and mark any Decl it references
1994 // as also alive, so that any Decl referenced does not get garbage collected.
1995
1996 if (decl.val.pointerDecl()) |pointee| {
1997 return markDeclAlive(pointee);
1998 }
1999}
2000
19882001fn emitUndefined(self: *Self, ty: Type) InnerError!void {
19892002 switch (ty.zigTypeTag()) {
19902003 .Int => switch (ty.intInfo(self.target).bits) {
src/codegen.zig+14-1
......@@ -464,7 +464,7 @@ fn lowerDeclRef(
464464 }
465465
466466 if (decl.analysis != .complete) return error.AnalysisFail;
467 decl.alive = true;
467 markDeclAlive(decl);
468468 // TODO handle the dependency of this symbol on the decl's vaddr.
469469 // If the decl changes vaddr, then this symbol needs to get regenerated.
470470 const vaddr = bin_file.getDeclVAddr(decl);
......@@ -478,3 +478,16 @@ fn lowerDeclRef(
478478
479479 return Result{ .appended = {} };
480480}
481
482fn markDeclAlive(decl: *Module.Decl) void {
483 if (decl.alive) return;
484 decl.alive = true;
485
486 // This is the first time we are marking this Decl alive. We must
487 // therefore recurse into its value and mark any Decl it references
488 // as also alive, so that any Decl referenced does not get garbage collected.
489
490 if (decl.val.pointerDecl()) |pointee| {
491 return markDeclAlive(pointee);
492 }
493}
src/codegen/c.zig+14-1
......@@ -195,7 +195,7 @@ pub const DeclGen = struct {
195195 val: Value,
196196 decl: *Decl,
197197 ) error{ OutOfMemory, AnalysisFail }!void {
198 decl.alive = true;
198 markDeclAlive(decl);
199199
200200 if (ty.isSlice()) {
201201 try writer.writeByte('(');
......@@ -227,6 +227,19 @@ pub const DeclGen = struct {
227227 try dg.renderDeclName(decl, writer);
228228 }
229229
230 fn markDeclAlive(decl: *Decl) void {
231 if (decl.alive) return;
232 decl.alive = true;
233
234 // This is the first time we are marking this Decl alive. We must
235 // therefore recurse into its value and mark any Decl it references
236 // as also alive, so that any Decl referenced does not get garbage collected.
237
238 if (decl.val.pointerDecl()) |pointee| {
239 return markDeclAlive(pointee);
240 }
241 }
242
230243 fn renderInt128(
231244 writer: anytype,
232245 int_val: anytype,
src/codegen/llvm.zig+22-5
......@@ -749,7 +749,6 @@ pub const DeclGen = struct {
749749
750750 fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {
751751 const gpa = dg.gpa;
752 log.debug("llvmType for {}", .{t});
753752 switch (t.zigTypeTag()) {
754753 .Void, .NoReturn => return dg.context.voidType(),
755754 .Int => {
......@@ -1168,7 +1167,7 @@ pub const DeclGen = struct {
11681167 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
11691168 .variable => {
11701169 const decl = tv.val.castTag(.variable).?.data.owner_decl;
1171 decl.alive = true;
1170 dg.markDeclAlive(decl);
11721171 const val = try dg.resolveGlobalDecl(decl);
11731172 const llvm_var_type = try dg.llvmType(tv.ty);
11741173 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
......@@ -1317,7 +1316,7 @@ pub const DeclGen = struct {
13171316 .function => tv.val.castTag(.function).?.data.owner_decl,
13181317 else => unreachable,
13191318 };
1320 fn_decl.alive = true;
1319 dg.markDeclAlive(fn_decl);
13211320 return dg.resolveLlvmFunction(fn_decl);
13221321 },
13231322 .ErrorSet => {
......@@ -1625,7 +1624,7 @@ pub const DeclGen = struct {
16251624 ptr_val: Value,
16261625 decl: *Module.Decl,
16271626 ) Error!ParentPtr {
1628 decl.alive = true;
1627 dg.markDeclAlive(decl);
16291628 var ptr_ty_payload: Type.Payload.ElemType = .{
16301629 .base = .{ .tag = .single_mut_pointer },
16311630 .data = decl.ty,
......@@ -1707,7 +1706,7 @@ pub const DeclGen = struct {
17071706 return self.lowerPtrToVoid(tv.ty);
17081707 }
17091708
1710 decl.alive = true;
1709 self.markDeclAlive(decl);
17111710
17121711 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
17131712 try self.resolveLlvmFunction(decl)
......@@ -1718,6 +1717,24 @@ pub const DeclGen = struct {
17181717 return llvm_val.constBitCast(llvm_type);
17191718 }
17201719
1720 fn markDeclAlive(dg: *DeclGen, decl: *Module.Decl) void {
1721 if (decl.alive) return;
1722 decl.alive = true;
1723
1724 log.debug("{*} ({s}) marked alive by {*} ({s})", .{
1725 decl, decl.name,
1726 dg.decl, dg.decl.name,
1727 });
1728
1729 // This is the first time we are marking this Decl alive. We must
1730 // therefore recurse into its value and mark any Decl it references
1731 // as also alive, so that any Decl referenced does not get garbage collected.
1732
1733 if (decl.val.pointerDecl()) |pointee| {
1734 return dg.markDeclAlive(pointee);
1735 }
1736 }
1737
17211738 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
17221739 const target = dg.module.getTarget();
17231740 const alignment = ptr_ty.ptrAlignment(target);
test/behavior/struct_llvm.zig+20
......@@ -285,3 +285,23 @@ fn getB(data: *const BitField1) u3 {
285285fn getC(data: *const BitField1) u2 {
286286 return data.c;
287287}
288
289test "default struct initialization fields" {
290 const S = struct {
291 a: i32 = 1234,
292 b: i32,
293 };
294 const x = S{
295 .b = 5,
296 };
297 var five: i32 = 5;
298 const y = S{
299 .b = five,
300 };
301 if (x.a + x.b != 1239) {
302 @compileError("it should be comptime known");
303 }
304 try expect(y.a == x.a);
305 try expect(y.b == x.b);
306 try expect(1239 == x.a + x.b);
307}
test/behavior/struct_stage1.zig-19
......@@ -166,25 +166,6 @@ test "packed struct with fp fields" {
166166 try expectEqual(@as(f32, 20.0), s.data[2]);
167167}
168168
169test "default struct initialization fields" {
170 const S = struct {
171 a: i32 = 1234,
172 b: i32,
173 };
174 const x = S{
175 .b = 5,
176 };
177 var five: i32 = 5;
178 const y = S{
179 .b = five,
180 };
181 if (x.a + x.b != 1239) {
182 @compileError("it should be comptime known");
183 }
184 try expectEqual(y, x);
185 try expectEqual(1239, x.a + x.b);
186}
187
188169test "fn with C calling convention returns struct by value" {
189170 const S = struct {
190171 fn entry() !void {