authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-11 14:17:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-11 14:17:52-07:00
logbcf15e39e2d4e2243f475852aca7749e40a70fbd
tree367c697ea25a0649a263d13131ffaf0ec3f82ad4
parentdae22a0a1f13cc963e96cd704941eed29b8dde27

stage2: add `owns_tv` flag to `Module.Decl`

Decl objects need to know whether they are the owner of the Type/Value associated with them, in order to decide whether to destroy the associated Namespace, Fn, or Var when cleaning up.

6 files changed, 128 insertions(+), 59 deletions(-)

BRANCH_TODO-10
......@@ -1,13 +1,3 @@
1 * The next problem is that when trying to deinitialize everything, when we
2 deinit a Decl that is the owner of a Namespace, there may still be other Decl
3 objects that reference that Namespace. They want to check if they are the owner
4 in order to find out if they should destroy it. But they can't check if they are
5 the owner because the owner_decl field is destroyed.
6 So there's a memory management problem to solve. We could easily solve this with
7 ref counting or whatever but the goal is to not introduce extra overhead / unnecessary
8 fields just to help figure out how to free stuff. So come up with some way to make
9 this sound, and easily debuggable when something goes wrong.
10
111 * get stage2 tests passing
122 * modify stage2 tests so that only 1 uses _start and the rest use
133 pub fn main
src/Module.zig+57-29
......@@ -227,6 +227,10 @@ pub const Decl = struct {
227227 },
228228 /// Whether `typed_value`, `align_val`, and `linksection_val` are populated.
229229 has_tv: bool,
230 /// If `true` it means the `Decl` is the resource owner of the type/value associated
231 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
232 /// check if the value owns a `Namespace`, and destroy that too.
233 owns_tv: bool,
230234 /// This flag is set when this Decl is added to `Module.deletion_set`, and cleared
231235 /// when removed.
232236 deletion_flag: bool,
......@@ -278,9 +282,7 @@ pub const Decl = struct {
278282 decl.clearName(gpa);
279283 if (decl.has_tv) {
280284 if (decl.getInnerNamespace()) |namespace| {
281 if (namespace.getDecl() == decl) {
282 namespace.clearDecls(module);
283 }
285 namespace.clearDecls(module);
284286 }
285287 decl.clearValues(gpa);
286288 }
......@@ -307,6 +309,7 @@ pub const Decl = struct {
307309 arena_state.promote(gpa).deinit();
308310 decl.value_arena = null;
309311 decl.has_tv = false;
312 decl.owns_tv = false;
310313 }
311314 }
312315
......@@ -441,36 +444,36 @@ pub const Decl = struct {
441444 /// If the Decl has a value and it is a struct, return it,
442445 /// otherwise null.
443446 pub fn getStruct(decl: *Decl) ?*Struct {
444 if (!decl.has_tv) return null;
447 if (!decl.owns_tv) return null;
445448 const ty = (decl.val.castTag(.ty) orelse return null).data;
446449 const struct_obj = (ty.castTag(.@"struct") orelse return null).data;
447 if (struct_obj.owner_decl != decl) return null;
450 assert(struct_obj.owner_decl == decl);
448451 return struct_obj;
449452 }
450453
451454 /// If the Decl has a value and it is a union, return it,
452455 /// otherwise null.
453456 pub fn getUnion(decl: *Decl) ?*Union {
454 if (!decl.has_tv) return null;
457 if (!decl.owns_tv) return null;
455458 const ty = (decl.val.castTag(.ty) orelse return null).data;
456459 const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data;
457 if (union_obj.owner_decl != decl) return null;
460 assert(union_obj.owner_decl == decl);
458461 return union_obj;
459462 }
460463
461464 /// If the Decl has a value and it is a function, return it,
462465 /// otherwise null.
463466 pub fn getFunction(decl: *Decl) ?*Fn {
464 if (!decl.has_tv) return null;
467 if (!decl.owns_tv) return null;
465468 const func = (decl.val.castTag(.function) orelse return null).data;
466 if (func.owner_decl != decl) return null;
469 assert(func.owner_decl == decl);
467470 return func;
468471 }
469472
470473 pub fn getVariable(decl: *Decl) ?*Var {
471 if (!decl.has_tv) return null;
474 if (!decl.owns_tv) return null;
472475 const variable = (decl.val.castTag(.variable) orelse return null).data;
473 if (variable.owner_decl != decl) return null;
476 assert(variable.owner_decl == decl);
474477 return variable;
475478 }
476479
......@@ -478,29 +481,28 @@ pub const Decl = struct {
478481 /// enum, or opaque.
479482 /// Only returns it if the Decl is the owner.
480483 pub fn getInnerNamespace(decl: *Decl) ?*Scope.Namespace {
481 if (!decl.has_tv) return null;
484 if (!decl.owns_tv) return null;
482485 const ty = (decl.val.castTag(.ty) orelse return null).data;
483486 switch (ty.tag()) {
484487 .@"struct" => {
485488 const struct_obj = ty.castTag(.@"struct").?.data;
486 if (struct_obj.owner_decl != decl) return null;
489 assert(struct_obj.owner_decl == decl);
487490 return &struct_obj.namespace;
488491 },
489492 .enum_full => {
490493 const enum_obj = ty.castTag(.enum_full).?.data;
491 if (enum_obj.owner_decl != decl) return null;
494 assert(enum_obj.owner_decl == decl);
492495 return &enum_obj.namespace;
493496 },
494497 .empty_struct => {
495 // design flaw, can't verify the owner is this decl
496 @panic("TODO can't implement getInnerNamespace for this type");
498 return ty.castTag(.empty_struct).?.data;
497499 },
498500 .@"opaque" => {
499501 @panic("TODO opaque types");
500502 },
501503 .@"union", .union_tagged => {
502504 const union_obj = ty.cast(Type.Payload.Union).?.data;
503 if (union_obj.owner_decl != decl) return null;
505 assert(union_obj.owner_decl == decl);
504506 return &union_obj.namespace;
505507 },
506508
......@@ -554,7 +556,11 @@ pub const Decl = struct {
554556
555557 .complete,
556558 .outdated,
557 => true,
559 => {
560 if (!decl.owns_tv)
561 return false;
562 return decl.ty.hasCodeGenBits();
563 },
558564 };
559565 }
560566};
......@@ -2838,6 +2844,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
28382844 new_decl.ty = struct_ty;
28392845 new_decl.val = struct_val;
28402846 new_decl.has_tv = true;
2847 new_decl.owns_tv = true;
28412848 new_decl.analysis = .complete;
28422849 new_decl.generation = mod.generation;
28432850
......@@ -2948,7 +2955,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29482955 errdefer decl_arena.deinit();
29492956 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
29502957
2951 if (decl_tv.val.tag() == .function) {
2958 if (decl_tv.val.castTag(.function)) |fn_payload| {
29522959 var prev_type_has_bits = false;
29532960 var prev_is_inline = false;
29542961 var type_changed = true;
......@@ -2956,10 +2963,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29562963 if (decl.has_tv) {
29572964 prev_type_has_bits = decl.ty.hasCodeGenBits();
29582965 type_changed = !decl.ty.eql(decl_tv.ty);
2959 if (decl.val.castTag(.function)) |payload| {
2960 const prev_func = payload.data;
2966 if (decl.getFunction()) |prev_func| {
29612967 prev_is_inline = prev_func.state == .inline_only;
2962 prev_func.deinit(gpa);
29632968 }
29642969 decl.clearValues(gpa);
29652970 }
......@@ -2969,6 +2974,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29692974 decl.align_val = try align_val.copy(&decl_arena.allocator);
29702975 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
29712976 decl.has_tv = true;
2977 decl.owns_tv = fn_payload.data.owner_decl == decl;
29722978 decl_arena_state.* = decl_arena.state;
29732979 decl.value_arena = decl_arena_state;
29742980 decl.analysis = .complete;
......@@ -3004,6 +3010,25 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30043010 decl.clearValues(gpa);
30053011 }
30063012
3013 decl.owns_tv = false;
3014 var queue_linker_work = false;
3015 if (decl_tv.val.castTag(.variable)) |payload| {
3016 const variable = payload.data;
3017 if (variable.owner_decl == decl) {
3018 decl.owns_tv = true;
3019 queue_linker_work = true;
3020
3021 const copied_init = try variable.init.copy(&decl_arena.allocator);
3022 variable.init = copied_init;
3023 }
3024 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3025 const owner_decl = payload.data;
3026 if (decl == owner_decl) {
3027 decl.owns_tv = true;
3028 queue_linker_work = true;
3029 }
3030 }
3031
30073032 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
30083033 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
30093034 decl.align_val = try align_val.copy(&decl_arena.allocator);
......@@ -3014,13 +3039,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30143039 decl.analysis = .complete;
30153040 decl.generation = mod.generation;
30163041
3017 if (decl.is_exported) {
3018 const export_src = src; // TODO point to the export token
3019 // The scope needs to have the decl in it.
3020 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3021 }
3022
3023 if (decl.val.tag() == .extern_fn) {
3042 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
30243043 try mod.comp.bin_file.allocateDeclIndexes(decl);
30253044 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
30263045
......@@ -3029,6 +3048,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30293048 }
30303049 }
30313050
3051 if (decl.is_exported) {
3052 const export_src = src; // TODO point to the export token
3053 // The scope needs to have the decl in it.
3054 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3055 }
3056
3057
30323058 return type_changed;
30333059 }
30343060}
......@@ -3531,6 +3557,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
35313557 .src_node = src_node,
35323558 .src_line = undefined,
35333559 .has_tv = false,
3560 .owns_tv = false,
35343561 .ty = undefined,
35353562 .val = undefined,
35363563 .align_val = undefined,
......@@ -3779,6 +3806,7 @@ pub fn createAnonymousDeclNamed(
37793806 new_decl.ty = typed_value.ty;
37803807 new_decl.val = typed_value.val;
37813808 new_decl.has_tv = true;
3809 new_decl.owns_tv = true;
37823810 new_decl.analysis = .complete;
37833811 new_decl.generation = mod.generation;
37843812
src/Sema.zig+1-1
......@@ -5867,7 +5867,7 @@ fn zirVarExtended(
58675867 extra_index += 1;
58685868 const init_tv = try sema.resolveInstConst(block, init_src, init_ref);
58695869 break :blk init_tv.val;
5870 } else Value.initTag(.null_value);
5870 } else Value.initTag(.unreachable_value);
58715871
58725872 if (!var_ty.isValidVarType(small.is_extern)) {
58735873 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
src/codegen/c.zig+51-4
......@@ -30,6 +30,7 @@ pub const CValue = union(enum) {
3030 arg: usize,
3131 /// By-value
3232 decl: *Decl,
33 decl_ref: *Decl,
3334};
3435
3536pub const CValueMap = std.AutoHashMap(*Inst, CValue);
......@@ -117,6 +118,7 @@ pub const Object = struct {
117118 .constant => |inst| return o.dg.renderValue(w, inst.ty, inst.value().?),
118119 .arg => |i| return w.print("a{d}", .{i}),
119120 .decl => |decl| return w.writeAll(mem.span(decl.name)),
121 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),
120122 }
121123 }
122124
......@@ -528,13 +530,17 @@ pub const DeclGen = struct {
528530 }
529531 }
530532
531 fn functionIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
533 fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool {
532534 switch (tv.val.tag()) {
533535 .extern_fn => return true,
534536 .function => {
535537 const func = tv.val.castTag(.function).?.data;
536538 return dg.module.decl_exports.contains(func.owner_decl);
537539 },
540 .variable => {
541 const variable = tv.val.castTag(.variable).?.data;
542 return dg.module.decl_exports.contains(variable.owner_decl);
543 },
538544 else => unreachable,
539545 }
540546 }
......@@ -549,7 +555,7 @@ pub fn genDecl(o: *Object) !void {
549555 .val = o.dg.decl.val,
550556 };
551557 if (tv.val.castTag(.function)) |func_payload| {
552 const is_global = o.dg.functionIsGlobal(tv);
558 const is_global = o.dg.declIsGlobal(tv);
553559 const fwd_decl_writer = o.dg.fwd_decl.writer();
554560 if (is_global) {
555561 try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
......@@ -570,6 +576,30 @@ pub fn genDecl(o: *Object) !void {
570576 try writer.writeAll("ZIG_EXTERN_C ");
571577 try o.dg.renderFunctionSignature(writer, true);
572578 try writer.writeAll(";\n");
579 } else if (tv.val.castTag(.variable)) |var_payload| {
580 const variable: *Module.Var = var_payload.data;
581 const is_global = o.dg.declIsGlobal(tv);
582 const fwd_decl_writer = o.dg.fwd_decl.writer();
583 if (is_global or variable.is_extern) {
584 try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
585 }
586 if (variable.is_threadlocal) {
587 try fwd_decl_writer.writeAll("zig_threadlocal ");
588 }
589 try o.dg.renderType(fwd_decl_writer, o.dg.decl.ty);
590 const decl_name = mem.span(o.dg.decl.name);
591 try fwd_decl_writer.print(" {s};\n", .{decl_name});
592
593 try o.indent_writer.insertNewline();
594 const w = o.writer();
595 try o.dg.renderType(w, o.dg.decl.ty);
596 try w.print(" {s} = ", .{decl_name});
597 if (variable.init.tag() != .unreachable_value) {
598 try o.dg.renderValue(w, tv.ty, variable.init);
599 }
600 try w.writeAll(";");
601 try o.indent_writer.insertNewline();
602
573603 } else {
574604 const writer = o.writer();
575605 try writer.writeAll("static ");
......@@ -598,7 +628,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
598628
599629 switch (tv.ty.zigTypeTag()) {
600630 .Fn => {
601 const is_global = dg.functionIsGlobal(tv);
631 const is_global = dg.declIsGlobal(tv);
602632 if (is_global) {
603633 try writer.writeAll("ZIG_EXTERN_C ");
604634 }
......@@ -696,7 +726,7 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
696726 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
697727 .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}),
698728 .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}),
699 .varptr => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for varptr", .{}),
729 .varptr => try genVarPtr(o, inst.castTag(.varptr).?),
700730 .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}),
701731 };
702732 switch (result_value) {
......@@ -709,6 +739,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
709739 try writer.writeAll("}");
710740}
711741
742fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue {
743 return CValue{ .decl_ref = inst.variable.owner_decl };
744}
745
712746fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue {
713747 const writer = o.writer();
714748
......@@ -743,6 +777,12 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue {
743777 try o.writeCValue(writer, wrapped);
744778 try writer.writeAll(";\n");
745779 },
780 .decl_ref => |decl| {
781 const wrapped: CValue = .{ .decl = decl };
782 try writer.writeAll(" = ");
783 try o.writeCValue(writer, wrapped);
784 try writer.writeAll(";\n");
785 },
746786 else => {
747787 try writer.writeAll(" = *");
748788 try o.writeCValue(writer, operand);
......@@ -791,6 +831,13 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
791831 try o.writeCValue(writer, src_val);
792832 try writer.writeAll(";\n");
793833 },
834 .decl_ref => |decl| {
835 const dest: CValue = .{ .decl = decl };
836 try o.writeCValue(writer, dest);
837 try writer.writeAll(" = ");
838 try o.writeCValue(writer, src_val);
839 try writer.writeAll(";\n");
840 },
794841 else => {
795842 try writer.writeAll("*");
796843 try o.writeCValue(writer, dest_ptr);
src/link/C/zig.h+18-14
......@@ -1,25 +1,15 @@
1#if __STDC_VERSION__ >= 199901L
2#include <stdbool.h>
3#else
4#define bool unsigned char
5#define true 1
6#define false 0
7#endif
8
91#if __STDC_VERSION__ >= 201112L
102#define zig_noreturn _Noreturn
3#define zig_threadlocal thread_local
114#elif __GNUC__
125#define zig_noreturn __attribute__ ((noreturn))
6#define zig_threadlocal __thread
137#elif _MSC_VER
148#define zig_noreturn __declspec(noreturn)
9#define zig_threadlocal __declspec(thread)
1510#else
1611#define zig_noreturn
17#endif
18
19#if defined(__GNUC__)
20#define zig_unreachable() __builtin_unreachable()
21#else
22#define zig_unreachable()
12#define zig_threadlocal zig_threadlocal_unavailable
2313#endif
2414
2515#if __STDC_VERSION__ >= 199901L
......@@ -30,6 +20,20 @@
3020#define ZIG_RESTRICT
3121#endif
3222
23#if __STDC_VERSION__ >= 199901L
24#include <stdbool.h>
25#else
26#define bool unsigned char
27#define true 1
28#define false 0
29#endif
30
31#if defined(__GNUC__)
32#define zig_unreachable() __builtin_unreachable()
33#else
34#define zig_unreachable()
35#endif
36
3337#ifdef __cplusplus
3438#define ZIG_EXTERN_C extern "C"
3539#else
test/stage2/cbe.zig+1-1
......@@ -517,7 +517,7 @@ pub fn addCases(ctx: *TestContext) !void {
517517 \\}
518518 , &.{
519519 ":3:21: error: missing struct field: x",
520 ":1:15: note: struct 'Point' declared here",
520 ":1:15: note: struct 'test_case.Point' declared here",
521521 });
522522 case.addError(
523523 \\const Point = struct { x: i32, y: i32 };