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...@@ -2427,7 +2427,57 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
24272427
2428 if (var_is_mut) {2428 if (var_is_mut) {
2429 try sema.validateVarType(block, ty_src, final_elem_ty, false);2429 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;
2430 }2479 }
2480
2431 // Change it to a normal alloc.2481 // Change it to a normal alloc.
2432 const final_ptr_ty = try Type.ptr(sema.arena, .{2482 const final_ptr_ty = try Type.ptr(sema.arena, .{
2433 .pointee_type = final_elem_ty,2483 .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...@@ -1047,7 +1047,7 @@ fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerErro
1047 const offset = @intCast(u32, self.code.items.len);1047 const offset = @intCast(u32, self.code.items.len);
1048 const atom = &self.decl.link.wasm;1048 const atom = &self.decl.link.wasm;
1049 const target_sym_index = decl.link.wasm.sym_index;1049 const target_sym_index = decl.link.wasm.sym_index;
1050 decl.alive = true;1050 markDeclAlive(decl);
1051 if (decl.ty.zigTypeTag() == .Fn) {1051 if (decl.ty.zigTypeTag() == .Fn) {
1052 // We found a function pointer, so add it to our table,1052 // We found a function pointer, so add it to our table,
1053 // as function pointers are not allowed to be stored inside the data section,1053 // 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 {...@@ -1876,7 +1876,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1876 try self.emitConstant(slice.data.len, Type.usize);1876 try self.emitConstant(slice.data.len, Type.usize);
1877 } else if (val.castTag(.decl_ref)) |payload| {1877 } else if (val.castTag(.decl_ref)) |payload| {
1878 const decl = payload.data;1878 const decl = payload.data;
1879 decl.alive = true;1879 markDeclAlive(decl);
1880 // Function pointers use a table index, rather than a memory address1880 // Function pointers use a table index, rather than a memory address
1881 if (decl.ty.zigTypeTag() == .Fn) {1881 if (decl.ty.zigTypeTag() == .Fn) {
1882 const target_sym_index = decl.link.wasm.sym_index;1882 const target_sym_index = decl.link.wasm.sym_index;
...@@ -1985,6 +1985,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1985,6 +1985,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1985 }1985 }
1986}1986}
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
1988fn emitUndefined(self: *Self, ty: Type) InnerError!void {2001fn emitUndefined(self: *Self, ty: Type) InnerError!void {
1989 switch (ty.zigTypeTag()) {2002 switch (ty.zigTypeTag()) {
1990 .Int => switch (ty.intInfo(self.target).bits) {2003 .Int => switch (ty.intInfo(self.target).bits) {
src/codegen.zig+14-1
...@@ -464,7 +464,7 @@ fn lowerDeclRef(...@@ -464,7 +464,7 @@ fn lowerDeclRef(
464 }464 }
465465
466 if (decl.analysis != .complete) return error.AnalysisFail;466 if (decl.analysis != .complete) return error.AnalysisFail;
467 decl.alive = true;467 markDeclAlive(decl);
468 // TODO handle the dependency of this symbol on the decl's vaddr.468 // TODO handle the dependency of this symbol on the decl's vaddr.
469 // If the decl changes vaddr, then this symbol needs to get regenerated.469 // If the decl changes vaddr, then this symbol needs to get regenerated.
470 const vaddr = bin_file.getDeclVAddr(decl);470 const vaddr = bin_file.getDeclVAddr(decl);
...@@ -478,3 +478,16 @@ fn lowerDeclRef(...@@ -478,3 +478,16 @@ fn lowerDeclRef(
478478
479 return Result{ .appended = {} };479 return Result{ .appended = {} };
480}480}
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 {...@@ -195,7 +195,7 @@ pub const DeclGen = struct {
195 val: Value,195 val: Value,
196 decl: *Decl,196 decl: *Decl,
197 ) error{ OutOfMemory, AnalysisFail }!void {197 ) error{ OutOfMemory, AnalysisFail }!void {
198 decl.alive = true;198 markDeclAlive(decl);
199199
200 if (ty.isSlice()) {200 if (ty.isSlice()) {
201 try writer.writeByte('(');201 try writer.writeByte('(');
...@@ -227,6 +227,19 @@ pub const DeclGen = struct {...@@ -227,6 +227,19 @@ pub const DeclGen = struct {
227 try dg.renderDeclName(decl, writer);227 try dg.renderDeclName(decl, writer);
228 }228 }
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
230 fn renderInt128(243 fn renderInt128(
231 writer: anytype,244 writer: anytype,
232 int_val: anytype,245 int_val: anytype,
src/codegen/llvm.zig+22-5
...@@ -749,7 +749,6 @@ pub const DeclGen = struct {...@@ -749,7 +749,6 @@ pub const DeclGen = struct {
749749
750 fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {750 fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {
751 const gpa = dg.gpa;751 const gpa = dg.gpa;
752 log.debug("llvmType for {}", .{t});
753 switch (t.zigTypeTag()) {752 switch (t.zigTypeTag()) {
754 .Void, .NoReturn => return dg.context.voidType(),753 .Void, .NoReturn => return dg.context.voidType(),
755 .Int => {754 .Int => {
...@@ -1168,7 +1167,7 @@ pub const DeclGen = struct {...@@ -1168,7 +1167,7 @@ pub const DeclGen = struct {
1168 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),1167 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
1169 .variable => {1168 .variable => {
1170 const decl = tv.val.castTag(.variable).?.data.owner_decl;1169 const decl = tv.val.castTag(.variable).?.data.owner_decl;
1171 decl.alive = true;1170 dg.markDeclAlive(decl);
1172 const val = try dg.resolveGlobalDecl(decl);1171 const val = try dg.resolveGlobalDecl(decl);
1173 const llvm_var_type = try dg.llvmType(tv.ty);1172 const llvm_var_type = try dg.llvmType(tv.ty);
1174 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");1173 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
...@@ -1317,7 +1316,7 @@ pub const DeclGen = struct {...@@ -1317,7 +1316,7 @@ pub const DeclGen = struct {
1317 .function => tv.val.castTag(.function).?.data.owner_decl,1316 .function => tv.val.castTag(.function).?.data.owner_decl,
1318 else => unreachable,1317 else => unreachable,
1319 };1318 };
1320 fn_decl.alive = true;1319 dg.markDeclAlive(fn_decl);
1321 return dg.resolveLlvmFunction(fn_decl);1320 return dg.resolveLlvmFunction(fn_decl);
1322 },1321 },
1323 .ErrorSet => {1322 .ErrorSet => {
...@@ -1625,7 +1624,7 @@ pub const DeclGen = struct {...@@ -1625,7 +1624,7 @@ pub const DeclGen = struct {
1625 ptr_val: Value,1624 ptr_val: Value,
1626 decl: *Module.Decl,1625 decl: *Module.Decl,
1627 ) Error!ParentPtr {1626 ) Error!ParentPtr {
1628 decl.alive = true;1627 dg.markDeclAlive(decl);
1629 var ptr_ty_payload: Type.Payload.ElemType = .{1628 var ptr_ty_payload: Type.Payload.ElemType = .{
1630 .base = .{ .tag = .single_mut_pointer },1629 .base = .{ .tag = .single_mut_pointer },
1631 .data = decl.ty,1630 .data = decl.ty,
...@@ -1707,7 +1706,7 @@ pub const DeclGen = struct {...@@ -1707,7 +1706,7 @@ pub const DeclGen = struct {
1707 return self.lowerPtrToVoid(tv.ty);1706 return self.lowerPtrToVoid(tv.ty);
1708 }1707 }
17091708
1710 decl.alive = true;1709 self.markDeclAlive(decl);
17111710
1712 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)1711 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
1713 try self.resolveLlvmFunction(decl)1712 try self.resolveLlvmFunction(decl)
...@@ -1718,6 +1717,24 @@ pub const DeclGen = struct {...@@ -1718,6 +1717,24 @@ pub const DeclGen = struct {
1718 return llvm_val.constBitCast(llvm_type);1717 return llvm_val.constBitCast(llvm_type);
1719 }1718 }
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
1721 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {1738 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
1722 const target = dg.module.getTarget();1739 const target = dg.module.getTarget();
1723 const alignment = ptr_ty.ptrAlignment(target);1740 const alignment = ptr_ty.ptrAlignment(target);
test/behavior/struct_llvm.zig+20
...@@ -285,3 +285,23 @@ fn getB(data: *const BitField1) u3 {...@@ -285,3 +285,23 @@ fn getB(data: *const BitField1) u3 {
285fn getC(data: *const BitField1) u2 {285fn getC(data: *const BitField1) u2 {
286 return data.c;286 return data.c;
287}287}
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" {...@@ -166,25 +166,6 @@ test "packed struct with fp fields" {
166 try expectEqual(@as(f32, 20.0), s.data[2]);166 try expectEqual(@as(f32, 20.0), s.data[2]);
167}167}
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
188test "fn with C calling convention returns struct by value" {169test "fn with C calling convention returns struct by value" {
189 const S = struct {170 const S = struct {
190 fn entry() !void {171 fn entry() !void {