authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-15 00:17:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-15 00:17:25-07:00
loga5c7742ba6fc793608b8bb7ba058e33eccd9cfec
tree29bf43a0bb915e35244d457514885baca2a5916b
parent41f3799bf0cfc8241f458094781ba45967e2576e

stage2: fix Decl garbage collection not marking enough

It is the job of codegen backends to mark Decls that are referenced as alive so that the frontend does not sweep them with the garbage. This commit unifies the code between the backends with an added method on Decl. The implementation is more complete than before, switching on the Decl val tag and recursing into sub-values. As a result, two more array tests are passing.

8 files changed, 152 insertions(+), 149 deletions(-)

src/Module.zig+10
......@@ -783,6 +783,16 @@ pub const Decl = struct {
783783 return decl.ty.abiAlignment(target);
784784 }
785785 }
786
787 pub fn markAlive(decl: *Decl) void {
788 if (decl.alive) return;
789 decl.alive = true;
790
791 // This is the first time we are marking this Decl alive. We must
792 // therefore recurse into its value and mark any Decl it references
793 // as also alive, so that any Decl referenced does not get garbage collected.
794 decl.val.markReferencedDeclsAlive();
795 }
786796};
787797
788798/// This state is attached to every Decl when Module emit_h is non-null.
src/arch/wasm/CodeGen.zig+2-15
......@@ -1037,7 +1037,7 @@ fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerErro
10371037 const offset = @intCast(u32, self.code.items.len);
10381038 const atom = &self.decl.link.wasm;
10391039 const target_sym_index = decl.link.wasm.sym_index;
1040 markDeclAlive(decl);
1040 decl.markAlive();
10411041 if (decl.ty.zigTypeTag() == .Fn) {
10421042 // We found a function pointer, so add it to our table,
10431043 // as function pointers are not allowed to be stored inside the data section,
......@@ -1935,7 +1935,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
19351935 try self.emitConstant(slice.data.len, Type.usize);
19361936 } else if (val.castTag(.decl_ref)) |payload| {
19371937 const decl = payload.data;
1938 markDeclAlive(decl);
1938 decl.markAlive();
19391939 // Function pointers use a table index, rather than a memory address
19401940 if (decl.ty.zigTypeTag() == .Fn) {
19411941 const target_sym_index = decl.link.wasm.sym_index;
......@@ -2101,19 +2101,6 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
21012101 }
21022102}
21032103
2104fn markDeclAlive(decl: *Decl) void {
2105 if (decl.alive) return;
2106 decl.alive = true;
2107
2108 // This is the first time we are marking this Decl alive. We must
2109 // therefore recurse into its value and mark any Decl it references
2110 // as also alive, so that any Decl referenced does not get garbage collected.
2111
2112 if (decl.val.pointerDecl()) |pointee| {
2113 return markDeclAlive(pointee);
2114 }
2115}
2116
21172104fn emitUndefined(self: *Self, ty: Type) InnerError!void {
21182105 switch (ty.zigTypeTag()) {
21192106 .Int => switch (ty.intInfo(self.target).bits) {
src/codegen.zig+1-14
......@@ -464,7 +464,7 @@ fn lowerDeclRef(
464464 }
465465
466466 if (decl.analysis != .complete) return error.AnalysisFail;
467 markDeclAlive(decl);
467 decl.markAlive();
468468 const vaddr = vaddr: {
469469 if (bin_file.cast(link.File.MachO)) |macho_file| {
470470 break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len);
......@@ -484,16 +484,3 @@ fn lowerDeclRef(
484484
485485 return Result{ .appended = {} };
486486}
487
488fn markDeclAlive(decl: *Module.Decl) void {
489 if (decl.alive) return;
490 decl.alive = true;
491
492 // This is the first time we are marking this Decl alive. We must
493 // therefore recurse into its value and mark any Decl it references
494 // as also alive, so that any Decl referenced does not get garbage collected.
495
496 if (decl.val.pointerDecl()) |pointee| {
497 return markDeclAlive(pointee);
498 }
499}
src/codegen/c.zig+1-14
......@@ -215,7 +215,7 @@ pub const DeclGen = struct {
215215 val: Value,
216216 decl: *Decl,
217217 ) error{ OutOfMemory, AnalysisFail }!void {
218 markDeclAlive(decl);
218 decl.markAlive();
219219
220220 if (ty.isSlice()) {
221221 try writer.writeByte('(');
......@@ -253,19 +253,6 @@ pub const DeclGen = struct {
253253 try dg.renderDeclName(decl, writer);
254254 }
255255
256 fn markDeclAlive(decl: *Decl) void {
257 if (decl.alive) return;
258 decl.alive = true;
259
260 // This is the first time we are marking this Decl alive. We must
261 // therefore recurse into its value and mark any Decl it references
262 // as also alive, so that any Decl referenced does not get garbage collected.
263
264 if (decl.val.pointerDecl()) |pointee| {
265 return markDeclAlive(pointee);
266 }
267 }
268
269256 fn renderInt128(
270257 writer: anytype,
271258 int_val: anytype,
src/codegen/llvm.zig+4-22
......@@ -1224,7 +1224,7 @@ pub const DeclGen = struct {
12241224 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
12251225 .variable => {
12261226 const decl = tv.val.castTag(.variable).?.data.owner_decl;
1227 dg.markDeclAlive(decl);
1227 decl.markAlive();
12281228 const val = try dg.resolveGlobalDecl(decl);
12291229 const llvm_var_type = try dg.llvmType(tv.ty);
12301230 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
......@@ -1373,7 +1373,7 @@ pub const DeclGen = struct {
13731373 .function => tv.val.castTag(.function).?.data.owner_decl,
13741374 else => unreachable,
13751375 };
1376 dg.markDeclAlive(fn_decl);
1376 fn_decl.markAlive();
13771377 return dg.resolveLlvmFunction(fn_decl);
13781378 },
13791379 .ErrorSet => {
......@@ -1681,7 +1681,7 @@ pub const DeclGen = struct {
16811681 ptr_val: Value,
16821682 decl: *Module.Decl,
16831683 ) Error!ParentPtr {
1684 dg.markDeclAlive(decl);
1684 decl.markAlive();
16851685 var ptr_ty_payload: Type.Payload.ElemType = .{
16861686 .base = .{ .tag = .single_mut_pointer },
16871687 .data = decl.ty,
......@@ -1763,7 +1763,7 @@ pub const DeclGen = struct {
17631763 return self.lowerPtrToVoid(tv.ty);
17641764 }
17651765
1766 self.markDeclAlive(decl);
1766 decl.markAlive();
17671767
17681768 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
17691769 try self.resolveLlvmFunction(decl)
......@@ -1774,24 +1774,6 @@ pub const DeclGen = struct {
17741774 return llvm_val.constBitCast(llvm_type);
17751775 }
17761776
1777 fn markDeclAlive(dg: *DeclGen, decl: *Module.Decl) void {
1778 if (decl.alive) return;
1779 decl.alive = true;
1780
1781 log.debug("{*} ({s}) marked alive by {*} ({s})", .{
1782 decl, decl.name,
1783 dg.decl, dg.decl.name,
1784 });
1785
1786 // This is the first time we are marking this Decl alive. We must
1787 // therefore recurse into its value and mark any Decl it references
1788 // as also alive, so that any Decl referenced does not get garbage collected.
1789
1790 if (decl.val.pointerDecl()) |pointee| {
1791 return dg.markDeclAlive(pointee);
1792 }
1793 }
1794
17951777 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
17961778 const target = dg.module.getTarget();
17971779 const alignment = ptr_ty.ptrAlignment(target);
src/value.zig+49
......@@ -1746,6 +1746,55 @@ pub const Value = extern union {
17461746 };
17471747 }
17481748
1749 pub fn markReferencedDeclsAlive(val: Value) void {
1750 switch (val.tag()) {
1751 .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(),
1752 .extern_fn, .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(),
1753 .function => return val.castTag(.function).?.data.owner_decl.markAlive(),
1754 .variable => return val.castTag(.variable).?.data.owner_decl.markAlive(),
1755
1756 .repeated,
1757 .eu_payload,
1758 .eu_payload_ptr,
1759 .opt_payload,
1760 .opt_payload_ptr,
1761 .empty_array_sentinel,
1762 => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data),
1763
1764 .array => {
1765 for (val.cast(Payload.Array).?.data) |elem_val| {
1766 markReferencedDeclsAlive(elem_val);
1767 }
1768 },
1769 .slice => {
1770 const slice = val.cast(Payload.Slice).?.data;
1771 markReferencedDeclsAlive(slice.ptr);
1772 markReferencedDeclsAlive(slice.len);
1773 },
1774
1775 .elem_ptr => {
1776 const elem_ptr = val.cast(Payload.ElemPtr).?.data;
1777 return markReferencedDeclsAlive(elem_ptr.array_ptr);
1778 },
1779 .field_ptr => {
1780 const field_ptr = val.cast(Payload.FieldPtr).?.data;
1781 return markReferencedDeclsAlive(field_ptr.container_ptr);
1782 },
1783 .@"struct" => {
1784 for (val.cast(Payload.Struct).?.data) |field_val| {
1785 markReferencedDeclsAlive(field_val);
1786 }
1787 },
1788 .@"union" => {
1789 const data = val.cast(Payload.Union).?.data;
1790 markReferencedDeclsAlive(data.tag);
1791 markReferencedDeclsAlive(data.val);
1792 },
1793
1794 else => {},
1795 }
1796 }
1797
17491798 pub fn slicePtr(val: Value) Value {
17501799 return switch (val.tag()) {
17511800 .slice => val.castTag(.slice).?.data.ptr,
test/behavior/array_llvm.zig+79
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const testing = std.testing;
33const expect = testing.expect;
4const mem = std.mem;
45
56var s_array: [8]Sub = undefined;
67const Sub = struct { b: u8 };
......@@ -87,3 +88,81 @@ test "array literal as argument to function" {
8788 try S.entry(2);
8889 comptime try S.entry(2);
8990}
91
92test "double nested array to const slice cast in array literal" {
93 const S = struct {
94 fn entry(two: i32) !void {
95 const cases = [_][]const []const i32{
96 &[_][]const i32{&[_]i32{1}},
97 &[_][]const i32{&[_]i32{ 2, 3 }},
98 &[_][]const i32{
99 &[_]i32{4},
100 &[_]i32{ 5, 6, 7 },
101 },
102 };
103 try check(&cases);
104
105 const cases2 = [_][]const i32{
106 &[_]i32{1},
107 &[_]i32{ two, 3 },
108 };
109 try expect(cases2.len == 2);
110 try expect(cases2[0].len == 1);
111 try expect(cases2[0][0] == 1);
112 try expect(cases2[1].len == 2);
113 try expect(cases2[1][0] == 2);
114 try expect(cases2[1][1] == 3);
115
116 const cases3 = [_][]const []const i32{
117 &[_][]const i32{&[_]i32{1}},
118 &[_][]const i32{&[_]i32{ two, 3 }},
119 &[_][]const i32{
120 &[_]i32{4},
121 &[_]i32{ 5, 6, 7 },
122 },
123 };
124 try check(&cases3);
125 }
126
127 fn check(cases: []const []const []const i32) !void {
128 try expect(cases.len == 3);
129 try expect(cases[0].len == 1);
130 try expect(cases[0][0].len == 1);
131 try expect(cases[0][0][0] == 1);
132 try expect(cases[1].len == 1);
133 try expect(cases[1][0].len == 2);
134 try expect(cases[1][0][0] == 2);
135 try expect(cases[1][0][1] == 3);
136 try expect(cases[2].len == 2);
137 try expect(cases[2][0].len == 1);
138 try expect(cases[2][0][0] == 4);
139 try expect(cases[2][1].len == 3);
140 try expect(cases[2][1][0] == 5);
141 try expect(cases[2][1][1] == 6);
142 try expect(cases[2][1][2] == 7);
143 }
144 };
145 try S.entry(2);
146 comptime try S.entry(2);
147}
148
149test "anonymous literal in array" {
150 const S = struct {
151 const Foo = struct {
152 a: usize = 2,
153 b: usize = 4,
154 };
155 fn doTheTest() !void {
156 var array: [2]Foo = .{
157 .{ .a = 3 },
158 .{ .b = 3 },
159 };
160 try expect(array[0].a == 3);
161 try expect(array[0].b == 4);
162 try expect(array[1].a == 2);
163 try expect(array[1].b == 3);
164 }
165 };
166 try S.doTheTest();
167 comptime try S.doTheTest();
168}
test/behavior/array_stage1.zig+6-84
......@@ -4,84 +4,6 @@ const mem = std.mem;
44const expect = testing.expect;
55const expectEqual = testing.expectEqual;
66
7test "double nested array to const slice cast in array literal" {
8 const S = struct {
9 fn entry(two: i32) !void {
10 const cases = [_][]const []const i32{
11 &[_][]const i32{&[_]i32{1}},
12 &[_][]const i32{&[_]i32{ 2, 3 }},
13 &[_][]const i32{
14 &[_]i32{4},
15 &[_]i32{ 5, 6, 7 },
16 },
17 };
18 try check(&cases);
19
20 const cases2 = [_][]const i32{
21 &[_]i32{1},
22 &[_]i32{ two, 3 },
23 };
24 try expect(cases2.len == 2);
25 try expect(cases2[0].len == 1);
26 try expect(cases2[0][0] == 1);
27 try expect(cases2[1].len == 2);
28 try expect(cases2[1][0] == 2);
29 try expect(cases2[1][1] == 3);
30
31 const cases3 = [_][]const []const i32{
32 &[_][]const i32{&[_]i32{1}},
33 &[_][]const i32{&[_]i32{ two, 3 }},
34 &[_][]const i32{
35 &[_]i32{4},
36 &[_]i32{ 5, 6, 7 },
37 },
38 };
39 try check(&cases3);
40 }
41
42 fn check(cases: []const []const []const i32) !void {
43 try expect(cases.len == 3);
44 try expect(cases[0].len == 1);
45 try expect(cases[0][0].len == 1);
46 try expect(cases[0][0][0] == 1);
47 try expect(cases[1].len == 1);
48 try expect(cases[1][0].len == 2);
49 try expect(cases[1][0][0] == 2);
50 try expect(cases[1][0][1] == 3);
51 try expect(cases[2].len == 2);
52 try expect(cases[2][0].len == 1);
53 try expect(cases[2][0][0] == 4);
54 try expect(cases[2][1].len == 3);
55 try expect(cases[2][1][0] == 5);
56 try expect(cases[2][1][1] == 6);
57 try expect(cases[2][1][2] == 7);
58 }
59 };
60 try S.entry(2);
61 comptime try S.entry(2);
62}
63
64test "anonymous literal in array" {
65 const S = struct {
66 const Foo = struct {
67 a: usize = 2,
68 b: usize = 4,
69 };
70 fn doTheTest() !void {
71 var array: [2]Foo = .{
72 .{ .a = 3 },
73 .{ .b = 3 },
74 };
75 try expect(array[0].a == 3);
76 try expect(array[0].b == 4);
77 try expect(array[1].a == 2);
78 try expect(array[1].b == 3);
79 }
80 };
81 try S.doTheTest();
82 comptime try S.doTheTest();
83}
84
857test "access the null element of a null terminated array" {
868 const S = struct {
879 fn doTheTest() !void {
......@@ -100,9 +22,9 @@ test "type deduction for array subscript expression" {
10022 fn doTheTest() !void {
10123 var array = [_]u8{ 0x55, 0xAA };
10224 var v0 = true;
103 try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]);
25 try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]);
10426 var v1 = false;
105 try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]);
27 try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]);
10628 }
10729 };
10830 try S.doTheTest();
......@@ -119,9 +41,9 @@ test "sentinel element count towards the ABI size calculation" {
11941 };
12042 var x = T{};
12143 var as_slice = mem.asBytes(&x);
122 try expectEqual(@as(usize, 3), as_slice.len);
123 try expectEqual(@as(u8, 0x55), as_slice[0]);
124 try expectEqual(@as(u8, 0xAA), as_slice[2]);
44 try expect(@as(usize, 3) == as_slice.len);
45 try expect(@as(u8, 0x55) == as_slice[0]);
46 try expect(@as(u8, 0xAA) == as_slice[2]);
12547 }
12648 };
12749
......@@ -144,7 +66,7 @@ test "zero-sized array with recursive type definition" {
14466 };
14567
14668 var t: S = .{ .list = .{ .s = undefined } };
147 try expectEqual(@as(usize, 0), t.list.x);
69 try expect(@as(usize, 0) == t.list.x);
14870}
14971
15072test "type coercion of anon struct literal to array" {