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 {...@@ -783,6 +783,16 @@ pub const Decl = struct {
783 return decl.ty.abiAlignment(target);783 return decl.ty.abiAlignment(target);
784 }784 }
785 }785 }
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 }
786};796};
787797
788/// This state is attached to every Decl when Module emit_h is non-null.798/// 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...@@ -1037,7 +1037,7 @@ fn lowerDeclRef(self: *Self, ty: Type, val: Value, decl: *Module.Decl) InnerErro
1037 const offset = @intCast(u32, self.code.items.len);1037 const offset = @intCast(u32, self.code.items.len);
1038 const atom = &self.decl.link.wasm;1038 const atom = &self.decl.link.wasm;
1039 const target_sym_index = decl.link.wasm.sym_index;1039 const target_sym_index = decl.link.wasm.sym_index;
1040 markDeclAlive(decl);1040 decl.markAlive();
1041 if (decl.ty.zigTypeTag() == .Fn) {1041 if (decl.ty.zigTypeTag() == .Fn) {
1042 // We found a function pointer, so add it to our table,1042 // We found a function pointer, so add it to our table,
1043 // as function pointers are not allowed to be stored inside the data section,1043 // 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 {...@@ -1935,7 +1935,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1935 try self.emitConstant(slice.data.len, Type.usize);1935 try self.emitConstant(slice.data.len, Type.usize);
1936 } else if (val.castTag(.decl_ref)) |payload| {1936 } else if (val.castTag(.decl_ref)) |payload| {
1937 const decl = payload.data;1937 const decl = payload.data;
1938 markDeclAlive(decl);1938 decl.markAlive();
1939 // Function pointers use a table index, rather than a memory address1939 // Function pointers use a table index, rather than a memory address
1940 if (decl.ty.zigTypeTag() == .Fn) {1940 if (decl.ty.zigTypeTag() == .Fn) {
1941 const target_sym_index = decl.link.wasm.sym_index;1941 const target_sym_index = decl.link.wasm.sym_index;
...@@ -2101,19 +2101,6 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -2101,19 +2101,6 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
2101 }2101 }
2102}2102}
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
2117fn emitUndefined(self: *Self, ty: Type) InnerError!void {2104fn emitUndefined(self: *Self, ty: Type) InnerError!void {
2118 switch (ty.zigTypeTag()) {2105 switch (ty.zigTypeTag()) {
2119 .Int => switch (ty.intInfo(self.target).bits) {2106 .Int => switch (ty.intInfo(self.target).bits) {
src/codegen.zig+1-14
...@@ -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 markDeclAlive(decl);467 decl.markAlive();
468 const vaddr = vaddr: {468 const vaddr = vaddr: {
469 if (bin_file.cast(link.File.MachO)) |macho_file| {469 if (bin_file.cast(link.File.MachO)) |macho_file| {
470 break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len);470 break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len);
...@@ -484,16 +484,3 @@ fn lowerDeclRef(...@@ -484,16 +484,3 @@ fn lowerDeclRef(
484484
485 return Result{ .appended = {} };485 return Result{ .appended = {} };
486}486}
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 {...@@ -215,7 +215,7 @@ pub const DeclGen = struct {
215 val: Value,215 val: Value,
216 decl: *Decl,216 decl: *Decl,
217 ) error{ OutOfMemory, AnalysisFail }!void {217 ) error{ OutOfMemory, AnalysisFail }!void {
218 markDeclAlive(decl);218 decl.markAlive();
219219
220 if (ty.isSlice()) {220 if (ty.isSlice()) {
221 try writer.writeByte('(');221 try writer.writeByte('(');
...@@ -253,19 +253,6 @@ pub const DeclGen = struct {...@@ -253,19 +253,6 @@ pub const DeclGen = struct {
253 try dg.renderDeclName(decl, writer);253 try dg.renderDeclName(decl, writer);
254 }254 }
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
269 fn renderInt128(256 fn renderInt128(
270 writer: anytype,257 writer: anytype,
271 int_val: anytype,258 int_val: anytype,
src/codegen/llvm.zig+4-22
...@@ -1224,7 +1224,7 @@ pub const DeclGen = struct {...@@ -1224,7 +1224,7 @@ pub const DeclGen = struct {
1224 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),1224 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
1225 .variable => {1225 .variable => {
1226 const decl = tv.val.castTag(.variable).?.data.owner_decl;1226 const decl = tv.val.castTag(.variable).?.data.owner_decl;
1227 dg.markDeclAlive(decl);1227 decl.markAlive();
1228 const val = try dg.resolveGlobalDecl(decl);1228 const val = try dg.resolveGlobalDecl(decl);
1229 const llvm_var_type = try dg.llvmType(tv.ty);1229 const llvm_var_type = try dg.llvmType(tv.ty);
1230 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");1230 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
...@@ -1373,7 +1373,7 @@ pub const DeclGen = struct {...@@ -1373,7 +1373,7 @@ pub const DeclGen = struct {
1373 .function => tv.val.castTag(.function).?.data.owner_decl,1373 .function => tv.val.castTag(.function).?.data.owner_decl,
1374 else => unreachable,1374 else => unreachable,
1375 };1375 };
1376 dg.markDeclAlive(fn_decl);1376 fn_decl.markAlive();
1377 return dg.resolveLlvmFunction(fn_decl);1377 return dg.resolveLlvmFunction(fn_decl);
1378 },1378 },
1379 .ErrorSet => {1379 .ErrorSet => {
...@@ -1681,7 +1681,7 @@ pub const DeclGen = struct {...@@ -1681,7 +1681,7 @@ pub const DeclGen = struct {
1681 ptr_val: Value,1681 ptr_val: Value,
1682 decl: *Module.Decl,1682 decl: *Module.Decl,
1683 ) Error!ParentPtr {1683 ) Error!ParentPtr {
1684 dg.markDeclAlive(decl);1684 decl.markAlive();
1685 var ptr_ty_payload: Type.Payload.ElemType = .{1685 var ptr_ty_payload: Type.Payload.ElemType = .{
1686 .base = .{ .tag = .single_mut_pointer },1686 .base = .{ .tag = .single_mut_pointer },
1687 .data = decl.ty,1687 .data = decl.ty,
...@@ -1763,7 +1763,7 @@ pub const DeclGen = struct {...@@ -1763,7 +1763,7 @@ pub const DeclGen = struct {
1763 return self.lowerPtrToVoid(tv.ty);1763 return self.lowerPtrToVoid(tv.ty);
1764 }1764 }
17651765
1766 self.markDeclAlive(decl);1766 decl.markAlive();
17671767
1768 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)1768 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
1769 try self.resolveLlvmFunction(decl)1769 try self.resolveLlvmFunction(decl)
...@@ -1774,24 +1774,6 @@ pub const DeclGen = struct {...@@ -1774,24 +1774,6 @@ pub const DeclGen = struct {
1774 return llvm_val.constBitCast(llvm_type);1774 return llvm_val.constBitCast(llvm_type);
1775 }1775 }
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
1795 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {1777 fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
1796 const target = dg.module.getTarget();1778 const target = dg.module.getTarget();
1797 const alignment = ptr_ty.ptrAlignment(target);1779 const alignment = ptr_ty.ptrAlignment(target);
src/value.zig+49
...@@ -1746,6 +1746,55 @@ pub const Value = extern union {...@@ -1746,6 +1746,55 @@ pub const Value = extern union {
1746 };1746 };
1747 }1747 }
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
1749 pub fn slicePtr(val: Value) Value {1798 pub fn slicePtr(val: Value) Value {
1750 return switch (val.tag()) {1799 return switch (val.tag()) {
1751 .slice => val.castTag(.slice).?.data.ptr,1800 .slice => val.castTag(.slice).?.data.ptr,
test/behavior/array_llvm.zig+79
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const testing = std.testing;2const testing = std.testing;
3const expect = testing.expect;3const expect = testing.expect;
4const mem = std.mem;
45
5var s_array: [8]Sub = undefined;6var s_array: [8]Sub = undefined;
6const Sub = struct { b: u8 };7const Sub = struct { b: u8 };
...@@ -87,3 +88,81 @@ test "array literal as argument to function" {...@@ -87,3 +88,81 @@ test "array literal as argument to function" {
87 try S.entry(2);88 try S.entry(2);
88 comptime try S.entry(2);89 comptime try S.entry(2);
89}90}
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;...@@ -4,84 +4,6 @@ const mem = std.mem;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const 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
85test "access the null element of a null terminated array" {7test "access the null element of a null terminated array" {
86 const S = struct {8 const S = struct {
87 fn doTheTest() !void {9 fn doTheTest() !void {
...@@ -100,9 +22,9 @@ test "type deduction for array subscript expression" {...@@ -100,9 +22,9 @@ test "type deduction for array subscript expression" {
100 fn doTheTest() !void {22 fn doTheTest() !void {
101 var array = [_]u8{ 0x55, 0xAA };23 var array = [_]u8{ 0x55, 0xAA };
102 var v0 = true;24 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]);
104 var v1 = false;26 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]);
106 }28 }
107 };29 };
108 try S.doTheTest();30 try S.doTheTest();
...@@ -119,9 +41,9 @@ test "sentinel element count towards the ABI size calculation" {...@@ -119,9 +41,9 @@ test "sentinel element count towards the ABI size calculation" {
119 };41 };
120 var x = T{};42 var x = T{};
121 var as_slice = mem.asBytes(&x);43 var as_slice = mem.asBytes(&x);
122 try expectEqual(@as(usize, 3), as_slice.len);44 try expect(@as(usize, 3) == as_slice.len);
123 try expectEqual(@as(u8, 0x55), as_slice[0]);45 try expect(@as(u8, 0x55) == as_slice[0]);
124 try expectEqual(@as(u8, 0xAA), as_slice[2]);46 try expect(@as(u8, 0xAA) == as_slice[2]);
125 }47 }
126 };48 };
12749
...@@ -144,7 +66,7 @@ test "zero-sized array with recursive type definition" {...@@ -144,7 +66,7 @@ test "zero-sized array with recursive type definition" {
144 };66 };
14567
146 var t: S = .{ .list = .{ .s = undefined } };68 var t: S = .{ .list = .{ .s = undefined } };
147 try expectEqual(@as(usize, 0), t.list.x);69 try expect(@as(usize, 0) == t.list.x);
148}70}
14971
150test "type coercion of anon struct literal to array" {72test "type coercion of anon struct literal to array" {