authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-07 18:52:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-07 18:52:11-07:00
log81d5104e228dc30184b31158c1b36ec0ec371b0b
tree396f2bd67ffac87f6de0d9eacb7e83e496ddc41b
parente7c4d545cd34321c61b85f1ce286d46976293617

stage2: implement global variables

* Sema: implement global variables - Improved global constants to stop needlessly creating a Var structure; they can just store the value directly. - This required making memory management a bit more sophisticated to detect when a Decl owns the Namespace associated with it, for the purposes of deinitialization. * Decl.name and Namespace decl table keys no longer directly reference ZIR; instead they have heap-duped names, so that deleted decls, which no longer have any ZIR to reference for their names, can be removed from the parent Namespace table. - In the future I would like to explore going a different direction with this, where the strings would still point to the ZIR however they would be removed from their owner Namespace objects during the update detection. The design principle here is that the existence of incremental compilation as a feature should not incur any cost for the use case when it is not used. In this example Decl names could simply point to ZIR string table memory, and it is only because of incremental compilation that we duplicate their names. * AstGen: implement threadlocal variables * CLI: call cleanExit after building a compilation so that in release modes we don't bother freeing memory or closing file descriptors, allowing the OS to do it more efficiently. * Avoid calling `freeDecl` in the linker for unreferenced Decl objects. * Fix CBE test case expecting the compile error to point to the wrong column.

8 files changed, 157 insertions(+), 103 deletions(-)

BRANCH_TODO+4-1
......@@ -58,5 +58,8 @@
5858 natural alignment for fields and do not have any comptime fields. this
5959 will save 16 bytes per struct field in the compilation.
6060
61 * AstGen threadlocal
6261 * extern "foo" for vars
62
63 * use ZIR memory for decl names where possible and also for keys
64 - this will require more sophisticated changelist detection which does some
65 pre-emptive deletion of decls from the parent namespace
src/AstGen.zig+4
......@@ -3009,6 +3009,7 @@ fn globalVarDecl(
30093009 .align_inst = .none, // passed via the decls data
30103010 .init = init_inst,
30113011 .is_extern = false,
3012 .is_threadlocal = is_threadlocal,
30123013 });
30133014 break :vi var_inst;
30143015 } else {
......@@ -3026,6 +3027,7 @@ fn globalVarDecl(
30263027 .align_inst = .none, // passed via the decls data
30273028 .init = .none,
30283029 .is_extern = true,
3030 .is_threadlocal = is_threadlocal,
30293031 });
30303032 break :vi var_inst;
30313033 } else {
......@@ -8100,6 +8102,7 @@ const GenZir = struct {
81008102 var_type: Zir.Inst.Ref,
81018103 init: Zir.Inst.Ref,
81028104 is_extern: bool,
8105 is_threadlocal: bool,
81038106 }) !Zir.Inst.Ref {
81048107 const astgen = gz.astgen;
81058108 const gpa = astgen.gpa;
......@@ -8137,6 +8140,7 @@ const GenZir = struct {
81378140 .has_align = args.align_inst != .none,
81388141 .has_init = args.init != .none,
81398142 .is_extern = args.is_extern,
8143 .is_threadlocal = args.is_threadlocal,
81408144 }),
81418145 .operand = payload_index,
81428146 } },
src/Module.zig+88-91
......@@ -154,9 +154,7 @@ pub const DeclPlusEmitH = struct {
154154};
155155
156156pub const Decl = struct {
157 /// For declarations that have corresponding source code, this is identical to
158 /// `getName().?`. For anonymous declarations this is allocated with Module's
159 /// allocator.
157 /// Allocated with Module's allocator; outlives the ZIR code.
160158 name: [*:0]const u8,
161159 /// The most recent Type of the Decl after a successful semantic analysis.
162160 /// Populated when `has_tv`.
......@@ -270,13 +268,7 @@ pub const Decl = struct {
270268 );
271269
272270 pub fn clearName(decl: *Decl, gpa: *Allocator) void {
273 // name could be allocated in the ZIR or it could be owned by gpa.
274 const file = decl.namespace.file_scope;
275 const string_table_start = @ptrToInt(file.zir.string_bytes.ptr);
276 const string_table_end = string_table_start + file.zir.string_bytes.len;
277 if (@ptrToInt(decl.name) < string_table_start or @ptrToInt(decl.name) >= string_table_end) {
278 gpa.free(mem.spanZ(decl.name));
279 }
271 gpa.free(mem.spanZ(decl.name));
280272 decl.name = undefined;
281273 }
282274
......@@ -285,7 +277,7 @@ pub const Decl = struct {
285277 log.debug("destroy {*} ({s})", .{ decl, decl.name });
286278 decl.clearName(gpa);
287279 if (decl.has_tv) {
288 if (decl.val.getTypeNamespace()) |namespace| {
280 if (decl.getInnerNamespace()) |namespace| {
289281 if (namespace.getDecl() == decl) {
290282 namespace.clearDecls(module);
291283 }
......@@ -308,6 +300,9 @@ pub const Decl = struct {
308300 func.deinit(gpa);
309301 gpa.destroy(func);
310302 }
303 if (decl.getVariable()) |variable| {
304 gpa.destroy(variable);
305 }
311306 if (decl.value_arena) |arena_state| {
312307 arena_state.promote(gpa).deinit();
313308 decl.value_arena = null;
......@@ -472,6 +467,47 @@ pub const Decl = struct {
472467 return func;
473468 }
474469
470 pub fn getVariable(decl: *Decl) ?*Var {
471 if (!decl.has_tv) return null;
472 const variable = (decl.val.castTag(.variable) orelse return null).data;
473 if (variable.owner_decl != decl) return null;
474 return variable;
475 }
476
477 /// Gets the namespace that this Decl creates by being a struct, union,
478 /// enum, or opaque.
479 /// Only returns it if the Decl is the owner.
480 pub fn getInnerNamespace(decl: *Decl) ?*Scope.Namespace {
481 if (!decl.has_tv) return null;
482 const ty = (decl.val.castTag(.ty) orelse return null).data;
483 switch (ty.tag()) {
484 .@"struct" => {
485 const struct_obj = ty.castTag(.@"struct").?.data;
486 if (struct_obj.owner_decl != decl) return null;
487 return &struct_obj.namespace;
488 },
489 .enum_full => {
490 const enum_obj = ty.castTag(.enum_full).?.data;
491 if (enum_obj.owner_decl != decl) return null;
492 return &enum_obj.namespace;
493 },
494 .empty_struct => {
495 // design flaw, can't verify the owner is this decl
496 @panic("TODO can't implement getInnerNamespace for this type");
497 },
498 .@"opaque" => {
499 @panic("TODO opaque types");
500 },
501 .@"union", .union_tagged => {
502 const union_obj = ty.cast(Type.Payload.Union).?.data;
503 if (union_obj.owner_decl != decl) return null;
504 return &union_obj.namespace;
505 },
506
507 else => return null,
508 }
509 }
510
475511 pub fn dump(decl: *Decl) void {
476512 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
477513 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{
......@@ -504,6 +540,23 @@ pub const Decl = struct {
504540 fn removeDependency(decl: *Decl, other: *Decl) void {
505541 decl.dependencies.removeAssertDiscard(other);
506542 }
543
544 fn hasLinkAllocation(decl: Decl) bool {
545 return switch (decl.analysis) {
546 .unreferenced,
547 .in_progress,
548 .dependency_failure,
549 .sema_failure,
550 .sema_failure_retryable,
551 .codegen_failure,
552 .codegen_failure_retryable,
553 => false,
554
555 .complete,
556 .outdated,
557 => true,
558 };
559 }
507560};
508561
509562/// This state is attached to every Decl when Module emit_h is non-null.
......@@ -831,9 +884,8 @@ pub const Scope = struct {
831884 /// Direct children of the namespace. Used during an update to detect
832885 /// which decls have been added/removed from source.
833886 /// Declaration order is preserved via entry order.
834 /// Key memory references the string table of the containing `File` ZIR.
887 /// Key memory is owned by `decl.name`.
835888 /// TODO save memory with https://github.com/ziglang/zig/issues/8619.
836 /// Does not contain anonymous decls.
837889 decls: std.StringArrayHashMapUnmanaged(*Decl) = .{},
838890
839891 pub fn deinit(ns: *Namespace, mod: *Module) void {
......@@ -2468,8 +2520,6 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
24682520/// * Decl.zir_index
24692521/// * Fn.zir_body_inst
24702522/// * Decl.zir_decl_index
2471/// * Decl.name
2472/// * Namespace.decl keys
24732523fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
24742524 const new_zir = file.zir;
24752525
......@@ -2484,18 +2534,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
24842534
24852535 try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map);
24862536
2487 // Build string table for new ZIR.
2488 var string_table: std.StringHashMapUnmanaged(u32) = .{};
2489 defer string_table.deinit(gpa);
2490 {
2491 var i: usize = 2;
2492 while (i < new_zir.string_bytes.len) {
2493 const string = new_zir.nullTerminatedString(i);
2494 try string_table.put(gpa, string, @intCast(u32, i));
2495 i += string.len + 1;
2496 }
2497 }
2498
24992537 // Walk the Decl graph, updating ZIR indexes, strings, and populating
25002538 // the deleted and outdated lists.
25012539
......@@ -2523,12 +2561,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
25232561 try file.deleted_decls.append(gpa, decl);
25242562 continue;
25252563 };
2526 const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse {
2527 try file.deleted_decls.append(gpa, decl);
2528 continue;
2529 };
2530 decl.name = new_zir.nullTerminatedString(new_name_index).ptr;
2531
25322564 const new_hash = decl.contentsHashZir(new_zir);
25332565 if (!std.zig.srcHashEql(old_hash, new_hash)) {
25342566 try file.outdated_decls.append(gpa, decl);
......@@ -2558,16 +2590,9 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
25582590 };
25592591 }
25602592
2561 if (decl.val.getTypeNamespace()) |namespace| {
2593 if (decl.getInnerNamespace()) |namespace| {
25622594 for (namespace.decls.items()) |*entry| {
25632595 const sub_decl = entry.value;
2564 if (sub_decl.zir_decl_index != 0) {
2565 const new_key_index = string_table.get(entry.key) orelse {
2566 try file.deleted_decls.append(gpa, sub_decl);
2567 continue;
2568 };
2569 entry.key = new_zir.nullTerminatedString(new_key_index);
2570 }
25712596 try decl_stack.append(gpa, sub_decl);
25722597 }
25732598 }
......@@ -2936,46 +2961,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29362961 }
29372962 return type_changed or is_inline != prev_is_inline;
29382963 } else {
2939 const is_mutable = decl_tv.val.tag() == .variable;
2940
2941 var is_threadlocal = false; // TODO implement threadlocal variables
2942 var is_extern = false; // TODO implement extern variables
2943
2944 if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) {
2945 return mod.fail(
2946 &block_scope.base,
2947 src, // TODO point at the mut token
2948 "variable of type '{}' must be const",
2949 .{decl_tv.ty},
2950 );
2951 }
2952
29532964 var type_changed = true;
29542965 if (decl.has_tv) {
29552966 type_changed = !decl.ty.eql(decl_tv.ty);
29562967 decl.clearValues(gpa);
29572968 }
29582969
2959 const copied_val = try decl_tv.val.copy(&decl_arena.allocator);
2960 const is_extern_fn = copied_val.tag() == .extern_fn;
2961
2962 // TODO: also avoid allocating this Var structure if `!is_mutable`.
2963 // I think this will require adjusting Sema to copy the value or something
2964 // like that; otherwise it causes use of undefined value when freeing resources.
2965 const decl_val: Value = if (is_extern_fn) copied_val else blk: {
2966 const new_variable = try decl_arena.allocator.create(Var);
2967 new_variable.* = .{
2968 .owner_decl = decl,
2969 .init = copied_val,
2970 .is_extern = is_extern,
2971 .is_mutable = is_mutable,
2972 .is_threadlocal = is_threadlocal,
2973 };
2974 break :blk try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
2975 };
2976
29772970 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
2978 decl.val = decl_val;
2971 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
29792972 decl.align_val = try align_val.copy(&decl_arena.allocator);
29802973 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
29812974 decl.has_tv = true;
......@@ -3211,7 +3204,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
32113204 const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
32123205
32133206 // Every Decl needs a name.
3214 const raw_decl_name: [:0]const u8 = switch (decl_name_index) {
3207 var is_named_test = false;
3208 const decl_name: [:0]const u8 = switch (decl_name_index) {
32153209 0 => name: {
32163210 if (is_exported) {
32173211 const i = iter.usingnamespace_index;
......@@ -3228,24 +3222,28 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
32283222 iter.unnamed_test_index += 1;
32293223 break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i});
32303224 },
3231 else => zir.nullTerminatedString(decl_name_index),
3232 };
3233 const decl_name = if (raw_decl_name.len != 0) raw_decl_name else name: {
3234 const test_name = zir.nullTerminatedString(decl_name_index + 1);
3235 break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name});
3225 else => name: {
3226 const raw_name = zir.nullTerminatedString(decl_name_index);
3227 if (raw_name.len == 0) {
3228 is_named_test = true;
3229 const test_name = zir.nullTerminatedString(decl_name_index + 1);
3230 break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name});
3231 } else {
3232 break :name try gpa.dupeZ(u8, raw_name);
3233 }
3234 },
32363235 };
32373236
32383237 // We create a Decl for it regardless of analysis status.
32393238 const gop = try namespace.decls.getOrPut(gpa, decl_name);
32403239 if (!gop.found_existing) {
32413240 const new_decl = try mod.allocateNewDecl(namespace, decl_node);
3242 log.debug("scan new decl {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
3241 log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
32433242 new_decl.src_line = line;
32443243 new_decl.name = decl_name;
32453244 gop.entry.value = new_decl;
32463245 // Exported decls, comptime decls, usingnamespace decls, and
32473246 // test decls if in test mode, get analyzed.
3248 const is_named_test = raw_decl_name.len == 0;
32493247 const want_analysis = is_exported or switch (decl_name_index) {
32503248 0 => true, // comptime decl
32513249 1 => mod.comp.bin_file.options.is_test, // test decl
......@@ -3261,17 +3259,15 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
32613259 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
32623260 return;
32633261 }
3262 gpa.free(decl_name);
32643263 const decl = gop.entry.value;
3265 log.debug("scan existing decl {*} ({s}) of {*}", .{ decl, decl_name, namespace });
3264 log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl_name, namespace });
32663265 // Update the AST node of the decl; even if its contents are unchanged, it may
32673266 // have been re-ordered.
32683267 const prev_src_node = decl.src_node;
32693268 decl.src_node = decl_node;
32703269 decl.src_line = line;
32713270
3272 decl.clearName(gpa);
3273 decl.name = decl_name;
3274
32753271 decl.is_pub = is_pub;
32763272 decl.is_exported = is_exported;
32773273 decl.has_align = has_align;
......@@ -3305,14 +3301,13 @@ pub fn deleteDecl(
33053301 const tracy = trace(@src());
33063302 defer tracy.end();
33073303
3308 log.debug("deleting decl '{s}'", .{decl.name});
3304 log.debug("deleting {*} ({s})", .{ decl, decl.name });
33093305
33103306 if (outdated_decls) |map| {
33113307 _ = map.swapRemove(decl);
3312 try map.ensureCapacity(map.count() + decl.dependants.count());
3308 try map.ensureUnusedCapacity(decl.dependants.count());
33133309 }
3314 try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +
3315 decl.dependencies.count());
3310 try mod.deletion_set.ensureUnusedCapacity(mod.gpa, decl.dependencies.count());
33163311
33173312 // Remove from the namespace it resides in.
33183313 decl.namespace.removeDecl(decl);
......@@ -3354,7 +3349,9 @@ pub fn deleteDecl(
33543349 }
33553350 _ = mod.compile_log_decls.swapRemove(decl);
33563351 mod.deleteDeclExports(decl);
3357 mod.comp.bin_file.freeDecl(decl);
3352 if (decl.hasLinkAllocation()) {
3353 mod.comp.bin_file.freeDecl(decl);
3354 }
33583355
33593356 decl.destroy(mod);
33603357}
src/Sema.zig+56-1
......@@ -5742,8 +5742,63 @@ fn zirVarExtended(
57425742) InnerError!*Inst {
57435743 const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
57445744 const src = sema.src;
5745 const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align
5746 const ty_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at type
5747 const mut_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at mut token
5748 const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr
5749 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
5750 const var_ty = try sema.resolveType(block, ty_src, extra.data.var_type);
5751
5752 var extra_index: usize = extra.end;
5753
5754 const lib_name: ?[]const u8 = if (small.has_lib_name) blk: {
5755 const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
5756 extra_index += 1;
5757 break :blk lib_name;
5758 } else null;
5759
5760 // ZIR supports encoding this information but it is not used; the information
5761 // is encoded via the Decl entry.
5762 assert(!small.has_align);
5763 //const align_val: Value = if (small.has_align) blk: {
5764 // const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
5765 // extra_index += 1;
5766 // const align_tv = try sema.resolveInstConst(block, align_src, align_ref);
5767 // break :blk align_tv.val;
5768 //} else Value.initTag(.null_value);
5769
5770 const init_val: Value = if (small.has_init) blk: {
5771 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
5772 extra_index += 1;
5773 const init_tv = try sema.resolveInstConst(block, init_src, init_ref);
5774 break :blk init_tv.val;
5775 } else Value.initTag(.null_value);
5776
5777 if (!var_ty.isValidVarType(small.is_extern)) {
5778 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
5779 var_ty,
5780 });
5781 }
5782
5783 if (lib_name != null) {
5784 // Look at the sema code for functions which has this logic, it just needs to
5785 // be extracted and shared by both var and func
5786 return sema.mod.fail(&block.base, src, "TODO: handle var with lib_name in Sema", .{});
5787 }
57455788
5746 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirVarExtended", .{});
5789 const new_var = try sema.gpa.create(Module.Var);
5790 new_var.* = .{
5791 .owner_decl = sema.owner_decl,
5792 .init = init_val,
5793 .is_extern = small.is_extern,
5794 .is_mutable = true, // TODO get rid of this unused field
5795 .is_threadlocal = small.is_threadlocal,
5796 };
5797 const result = try sema.mod.constInst(sema.arena, src, .{
5798 .ty = var_ty,
5799 .val = try Value.Tag.variable.create(sema.arena, new_var),
5800 });
5801 return result;
57475802}
57485803
57495804fn zirFuncExtended(
src/Zir.zig+2-1
......@@ -2235,7 +2235,8 @@ pub const Inst = struct {
22352235 has_align: bool,
22362236 has_init: bool,
22372237 is_extern: bool,
2238 _: u12 = undefined,
2238 is_threadlocal: bool,
2239 _: u11 = undefined,
22392240 };
22402241 };
22412242
src/main.zig+2
......@@ -2086,6 +2086,8 @@ fn buildOutputType(
20862086 break;
20872087 }
20882088 }
2089 // Skip resource deallocation in release builds; let the OS do it.
2090 return cleanExit();
20892091}
20902092
20912093fn runOrTest(
src/value.zig-8
......@@ -626,14 +626,6 @@ pub const Value = extern union {
626626 unreachable;
627627 }
628628
629 /// Returns null if not a type or if the type has no namespace.
630 pub fn getTypeNamespace(self: Value) ?*Module.Scope.Namespace {
631 return switch (self.tag()) {
632 .ty => self.castTag(.ty).?.data.getNamespace(),
633 else => null,
634 };
635 }
636
637629 /// Asserts that the value is representable as a type.
638630 pub fn toType(self: Value, allocator: *Allocator) !Type {
639631 return switch (self.tag()) {
test/stage2/cbe.zig+1-1
......@@ -51,7 +51,7 @@ pub fn addCases(ctx: *TestContext) !void {
5151 \\}
5252 \\var y: i32 = 1234;
5353 , &.{
54 ":2:18: error: unable to resolve comptime value",
54 ":2:22: error: unable to resolve comptime value",
5555 ":5:26: error: unable to resolve comptime value",
5656 });
5757 }