authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-23 23:29:51-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-23 23:29:51-04:00
logb1b7708cc8380ad9518715e3e285db3011f5a6a4
tree14f2ef168d5d8c38c78f4d07e9b46b0e506e1727
parentd9c1d8fed3e121c4fe91d5aea301574ff763ef95

self-hosted: hook up incremental compilation to .zig source code


2 files changed, 202 insertions(+), 208 deletions(-)

src-self-hosted/Module.zig+198-208
...@@ -74,9 +74,9 @@ const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope...@@ -74,9 +74,9 @@ const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope
74const WorkItem = union(enum) {74const WorkItem = union(enum) {
75 /// Write the machine code for a Decl to the output file.75 /// Write the machine code for a Decl to the output file.
76 codegen_decl: *Decl,76 codegen_decl: *Decl,
77 /// Decl has been determined to be outdated; perform semantic analysis again.
78 re_analyze_decl: *Decl,
79 /// The Decl needs to be analyzed and possibly export itself.77 /// The Decl needs to be analyzed and possibly export itself.
78 /// It may have already be analyzed, or it may have been determined
79 /// to be outdated; in this case perform semantic analysis again.
80 analyze_decl: *Decl,80 analyze_decl: *Decl,
81};81};
8282
...@@ -403,6 +403,17 @@ pub const Scope = struct {...@@ -403,6 +403,17 @@ pub const Scope = struct {
403 }403 }
404 }404 }
405405
406 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
407 pub fn removeDecl(base: *Scope, child: *Decl) void {
408 switch (base.tag) {
409 .file => return @fieldParentPtr(File, "base", base).removeDecl(child),
410 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
411 .block => unreachable,
412 .gen_zir => unreachable,
413 .decl => unreachable,
414 }
415 }
416
406 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.417 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
407 pub fn destroy(base: *Scope, allocator: *Allocator) void {418 pub fn destroy(base: *Scope, allocator: *Allocator) void {
408 switch (base.tag) {419 switch (base.tag) {
...@@ -462,6 +473,9 @@ pub const Scope = struct {...@@ -462,6 +473,9 @@ pub const Scope = struct {
462 loaded_success,473 loaded_success,
463 },474 },
464475
476 /// Direct children of the file.
477 decls: ArrayListUnmanaged(*Decl),
478
465 pub fn unload(self: *File, allocator: *Allocator) void {479 pub fn unload(self: *File, allocator: *Allocator) void {
466 switch (self.status) {480 switch (self.status) {
467 .never_loaded,481 .never_loaded,
...@@ -484,10 +498,20 @@ pub const Scope = struct {...@@ -484,10 +498,20 @@ pub const Scope = struct {
484 }498 }
485499
486 pub fn deinit(self: *File, allocator: *Allocator) void {500 pub fn deinit(self: *File, allocator: *Allocator) void {
501 self.decls.deinit(allocator);
487 self.unload(allocator);502 self.unload(allocator);
488 self.* = undefined;503 self.* = undefined;
489 }504 }
490505
506 pub fn removeDecl(self: *File, child: *Decl) void {
507 for (self.decls.items) |item, i| {
508 if (item == child) {
509 _ = self.decls.swapRemove(i);
510 return;
511 }
512 }
513 }
514
491 pub fn dumpSrc(self: *File, src: usize) void {515 pub fn dumpSrc(self: *File, src: usize) void {
492 const loc = std.zig.findLineColumn(self.source.bytes, src);516 const loc = std.zig.findLineColumn(self.source.bytes, src);
493 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });517 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
...@@ -540,6 +564,11 @@ pub const Scope = struct {...@@ -540,6 +564,11 @@ pub const Scope = struct {
540 loaded_success,564 loaded_success,
541 },565 },
542566
567 /// Even though .zir files only have 1 module, this set is still needed
568 /// because of anonymous Decls, which can exist in the global set, but
569 /// not this one.
570 decls: ArrayListUnmanaged(*Decl),
571
543 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {572 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {
544 switch (self.status) {573 switch (self.status) {
545 .never_loaded,574 .never_loaded,
...@@ -569,10 +598,20 @@ pub const Scope = struct {...@@ -569,10 +598,20 @@ pub const Scope = struct {
569 }598 }
570599
571 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {600 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
601 self.decls.deinit(allocator);
572 self.unload(allocator);602 self.unload(allocator);
573 self.* = undefined;603 self.* = undefined;
574 }604 }
575605
606 pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
607 for (self.decls.items) |item, i| {
608 if (item == child) {
609 _ = self.decls.swapRemove(i);
610 return;
611 }
612 }
613 }
614
576 pub fn dumpSrc(self: *ZIRModule, src: usize) void {615 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
577 const loc = std.zig.findLineColumn(self.source.bytes, src);616 const loc = std.zig.findLineColumn(self.source.bytes, src);
578 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });617 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
...@@ -700,6 +739,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -700,6 +739,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
700 .source = .{ .unloaded = {} },739 .source = .{ .unloaded = {} },
701 .contents = .{ .not_available = {} },740 .contents = .{ .not_available = {} },
702 .status = .never_loaded,741 .status = .never_loaded,
742 .decls = .{},
703 };743 };
704 break :blk &root_scope.base;744 break :blk &root_scope.base;
705 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {745 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
...@@ -709,6 +749,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -709,6 +749,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
709 .source = .{ .unloaded = {} },749 .source = .{ .unloaded = {} },
710 .contents = .{ .not_available = {} },750 .contents = .{ .not_available = {} },
711 .status = .never_loaded,751 .status = .never_loaded,
752 .decls = .{},
712 };753 };
713 break :blk &root_scope.base;754 break :blk &root_scope.base;
714 } else {755 } else {
...@@ -828,13 +869,14 @@ pub fn update(self: *Module) !void {...@@ -828,13 +869,14 @@ pub fn update(self: *Module) !void {
828 try self.performAllTheWork();869 try self.performAllTheWork();
829870
830 // Process the deletion set.871 // Process the deletion set.
831 while (self.deletion_set.popOrNull()) |decl| {872 for (self.deletion_set.items) |decl| {
832 if (decl.dependants.items.len != 0) {873 if (decl.dependants.items.len != 0) {
833 decl.deletion_flag = false;874 decl.deletion_flag = false;
834 continue;875 continue;
835 }876 }
836 try self.deleteDecl(decl);877 try self.deleteDecl(decl);
837 }878 }
879 self.deletion_set.shrink(self.allocator, 0);
838880
839 self.link_error_flags = self.bin_file.error_flags;881 self.link_error_flags = self.bin_file.error_flags;
840882
...@@ -969,49 +1011,6 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -969,49 +1011,6 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
969 };1011 };
970 },1012 },
971 },1013 },
972 .re_analyze_decl => |decl| switch (decl.analysis) {
973 .unreferenced => unreachable,
974 .in_progress => unreachable,
975
976 .sema_failure,
977 .codegen_failure,
978 .dependency_failure,
979 .complete,
980 .codegen_failure_retryable,
981 .sema_failure_retryable,
982 => continue,
983
984 .outdated => {
985 if (decl.scope.cast(Scope.File)) |file_scope| {
986 @panic("TODO re_analyze_decl for .zig files");
987 } else if (decl.scope.cast(Scope.ZIRModule)) |zir_scope| {
988 const zir_module = self.getSrcModule(zir_scope) catch |err| switch (err) {
989 error.OutOfMemory => return error.OutOfMemory,
990 else => {
991 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
992 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
993 self.allocator,
994 decl.src(),
995 "unable to load source file '{}': {}",
996 .{ zir_scope.sub_file_path, @errorName(err) },
997 ));
998 decl.analysis = .codegen_failure_retryable;
999 continue;
1000 },
1001 };
1002 const decl_name = mem.spanZ(decl.name);
1003 // We already detected deletions, so we know this will be found.
1004 const src_decl_and_index = zir_module.findDecl(decl_name).?;
1005 decl.src_index = src_decl_and_index.index;
1006 self.reAnalyzeDecl(decl, src_decl_and_index.decl.inst) catch |err| switch (err) {
1007 error.OutOfMemory => return error.OutOfMemory,
1008 error.AnalysisFail => continue,
1009 };
1010 } else {
1011 unreachable;
1012 }
1013 },
1014 },
1015 .analyze_decl => |decl| {1014 .analyze_decl => |decl| {
1016 self.ensureDeclAnalyzed(decl) catch |err| switch (err) {1015 self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
1017 error.OutOfMemory => return error.OutOfMemory,1016 error.OutOfMemory => return error.OutOfMemory,
...@@ -1022,9 +1021,12 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1022,9 +1021,12 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1022}1021}
10231022
1024fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {1023fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1025 switch (decl.analysis) {1024 const tracy = trace(@src());
1025 defer tracy.end();
1026
1027 const subsequent_analysis = switch (decl.analysis) {
1028 .complete => return,
1026 .in_progress => unreachable,1029 .in_progress => unreachable,
1027 .outdated => unreachable,
10281030
1029 .sema_failure,1031 .sema_failure,
1030 .sema_failure_retryable,1032 .sema_failure_retryable,
...@@ -1033,29 +1035,73 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {...@@ -1033,29 +1035,73 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1033 .codegen_failure_retryable,1035 .codegen_failure_retryable,
1034 => return error.AnalysisFail,1036 => return error.AnalysisFail,
10351037
1036 .complete => return,1038 .outdated => blk: {
1039 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1040
1041 // The exports this Decl performs will be re-discovered, so we remove them here
1042 // prior to re-analysis.
1043 self.deleteDeclExports(decl);
1044 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
1045 for (decl.dependencies.items) |dep| {
1046 dep.removeDependant(decl);
1047 if (dep.dependants.items.len == 0) {
1048 // We don't perform a deletion here, because this Decl or another one
1049 // may end up referencing it before the update is complete.
1050 assert(!dep.deletion_flag);
1051 dep.deletion_flag = true;
1052 try self.deletion_set.append(self.allocator, dep);
1053 }
1054 }
1055 decl.dependencies.shrink(self.allocator, 0);
10371056
1038 .unreferenced => {1057 break :blk true;
1039 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {1058 },
1040 error.OutOfMemory => return error.OutOfMemory,1059
1041 error.AnalysisFail => return error.AnalysisFail,1060 .unreferenced => false,
1042 else => {1061 };
1043 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);1062
1044 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1063 const type_changed = self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
1045 self.allocator,1064 error.OutOfMemory => return error.OutOfMemory,
1046 decl.src(),1065 error.AnalysisFail => return error.AnalysisFail,
1047 "unable to analyze: {}",1066 else => {
1048 .{@errorName(err)},1067 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
1049 ));1068 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1050 decl.analysis = .sema_failure_retryable;1069 self.allocator,
1051 return error.AnalysisFail;1070 decl.src(),
1052 },1071 "unable to analyze: {}",
1053 };1072 .{@errorName(err)},
1073 ));
1074 decl.analysis = .sema_failure_retryable;
1075 return error.AnalysisFail;
1054 },1076 },
1077 };
1078
1079 if (subsequent_analysis) {
1080 // We may need to chase the dependants and re-analyze them.
1081 // However, if the decl is a function, and the type is the same, we do not need to.
1082 if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
1083 for (decl.dependants.items) |dep| {
1084 switch (dep.analysis) {
1085 .unreferenced => unreachable,
1086 .in_progress => unreachable,
1087 .outdated => continue, // already queued for update
1088
1089 .dependency_failure,
1090 .sema_failure,
1091 .sema_failure_retryable,
1092 .codegen_failure,
1093 .codegen_failure_retryable,
1094 .complete,
1095 => if (dep.generation != self.generation) {
1096 try self.markOutdatedDecl(dep);
1097 },
1098 }
1099 }
1100 }
1055 }1101 }
1056}1102}
10571103
1058fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {1104fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1059 const tracy = trace(@src());1105 const tracy = trace(@src());
1060 defer tracy.end();1106 defer tracy.end();
10611107
...@@ -1170,6 +1216,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {...@@ -1170,6 +1216,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {
1170 };1216 };
1171 fn_payload.* = .{ .func = new_func };1217 fn_payload.* = .{ .func = new_func };
11721218
1219 var prev_type_has_bits = false;
1220 var type_changed = true;
1221
1222 if (decl.typedValueManaged()) |tvm| {
1223 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1224 type_changed = !tvm.typed_value.ty.eql(fn_type);
1225
1226 tvm.deinit(self.allocator);
1227 }
1228
1173 decl_arena_state.* = decl_arena.state;1229 decl_arena_state.* = decl_arena.state;
1174 decl.typed_value = .{1230 decl.typed_value = .{
1175 .most_recent = .{1231 .most_recent = .{
...@@ -1183,11 +1239,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {...@@ -1183,11 +1239,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {
1183 decl.analysis = .complete;1239 decl.analysis = .complete;
1184 decl.generation = self.generation;1240 decl.generation = self.generation;
11851241
1186 // We don't fully codegen the decl until later, but we do need to reserve a global1242 if (fn_type.hasCodeGenBits()) {
1187 // offset table index for it. This allows us to codegen decls out of dependency order,1243 // We don't fully codegen the decl until later, but we do need to reserve a global
1188 // increasing how many computations can be done in parallel.1244 // offset table index for it. This allows us to codegen decls out of dependency order,
1189 try self.bin_file.allocateDeclIndexes(decl);1245 // increasing how many computations can be done in parallel.
1190 try self.work_queue.writeItem(.{ .codegen_decl = decl });1246 try self.bin_file.allocateDeclIndexes(decl);
1247 try self.work_queue.writeItem(.{ .codegen_decl = decl });
1248 } else if (prev_type_has_bits) {
1249 self.bin_file.freeDecl(decl);
1250 }
11911251
1192 if (fn_proto.extern_export_inline_token) |maybe_export_token| {1252 if (fn_proto.extern_export_inline_token) |maybe_export_token| {
1193 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1253 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
...@@ -1198,6 +1258,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {...@@ -1198,6 +1258,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !void {
1198 try self.analyzeExport(&block_scope.base, export_src, name, decl);1258 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1199 }1259 }
1200 }1260 }
1261 return type_changed;
1201 },1262 },
1202 .VarDecl => @panic("TODO var decl"),1263 .VarDecl => @panic("TODO var decl"),
1203 .Comptime => @panic("TODO comptime decl"),1264 .Comptime => @panic("TODO comptime decl"),
...@@ -1602,40 +1663,63 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1602,40 +1663,63 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1602}1663}
16031664
1604fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {1665fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1605 switch (root_scope.status) {1666 // We may be analyzing it for the first time, or this may be
1606 .never_loaded => {1667 // an incremental update. This code handles both cases.
1607 const tree = try self.getAstTree(root_scope);1668 const tree = try self.getAstTree(root_scope);
1608 const decls = tree.root_node.decls();1669 const decls = tree.root_node.decls();
16091670
1610 try self.work_queue.ensureUnusedCapacity(decls.len);1671 try self.work_queue.ensureUnusedCapacity(decls.len);
16111672 try root_scope.decls.ensureCapacity(self.allocator, decls.len);
1612 for (decls) |src_decl, decl_i| {1673
1613 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {1674 // Keep track of the decls that we expect to see in this file so that
1614 // We will create a Decl for it regardless of analysis status.1675 // we know which ones have been deleted.
1615 const name_tok = fn_proto.name_token orelse1676 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator);
1616 @panic("TODO handle missing function name in the parser");1677 defer deleted_decls.deinit();
1617 const name_loc = tree.token_locs[name_tok];1678 try deleted_decls.ensureCapacity(root_scope.decls.items.len);
1618 const name = tree.tokenSliceLoc(name_loc);1679 for (root_scope.decls.items) |file_decl| {
1619 const name_hash = root_scope.fullyQualifiedNameHash(name);1680 deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
1620 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1681 }
1621 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);1682
1622 if (fn_proto.extern_export_inline_token) |maybe_export_token| {1683 for (decls) |src_decl, decl_i| {
1623 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1684 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
1624 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1685 // We will create a Decl for it regardless of analysis status.
1625 }1686 const name_tok = fn_proto.name_token orelse
1687 @panic("TODO handle missing function name in the parser");
1688 const name_loc = tree.token_locs[name_tok];
1689 const name = tree.tokenSliceLoc(name_loc);
1690 const name_hash = root_scope.fullyQualifiedNameHash(name);
1691 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1692 if (self.decl_table.get(name_hash)) |kv| {
1693 const decl = kv.value;
1694 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1695 // have been re-ordered.
1696 decl.src_index = decl_i;
1697 deleted_decls.removeAssertDiscard(decl);
1698 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1699 try self.markOutdatedDecl(decl);
1700 decl.contents_hash = contents_hash;
1701 }
1702 } else {
1703 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
1704 root_scope.decls.appendAssumeCapacity(new_decl);
1705 if (fn_proto.extern_export_inline_token) |maybe_export_token| {
1706 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1707 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1626 }1708 }
1627 }1709 }
1628 // TODO also look for global variable declarations
1629 // TODO also look for comptime blocks and exported globals
1630 }1710 }
1631 },1711 }
16321712 // TODO also look for global variable declarations
1633 .unloaded_parse_failure,1713 // TODO also look for comptime blocks and exported globals
1634 .unloaded_success,1714 }
1635 .loaded_success,1715 {
1636 => {1716 // Handle explicitly deleted decls from the source code. Not to be confused
1637 @panic("TODO process update");1717 // with when we delete decls because they are no longer referenced.
1638 },1718 var it = deleted_decls.iterator();
1719 while (it.next()) |kv| {
1720 //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name});
1721 try self.deleteDecl(kv.key);
1722 }
1639 }1723 }
1640}1724}
16411725
...@@ -1684,7 +1768,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1684,7 +1768,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1684 const decl = kv.value;1768 const decl = kv.value;
1685 deleted_decls.removeAssertDiscard(decl);1769 deleted_decls.removeAssertDiscard(decl);
1686 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });1770 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
1687 if (!mem.eql(u8, &src_decl.contents_hash, &decl.contents_hash)) {1771 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1688 try self.markOutdatedDecl(decl);1772 try self.markOutdatedDecl(decl);
1689 decl.contents_hash = src_decl.contents_hash;1773 decl.contents_hash = src_decl.contents_hash;
1690 }1774 }
...@@ -1711,6 +1795,10 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1711,6 +1795,10 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1711fn deleteDecl(self: *Module, decl: *Decl) !void {1795fn deleteDecl(self: *Module, decl: *Decl) !void {
1712 try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len);1796 try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len);
17131797
1798 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
1799 // not be present in the set, and this does nothing.
1800 decl.scope.removeDecl(decl);
1801
1714 //std.debug.warn("deleting decl '{}'\n", .{decl.name});1802 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
1715 const name_hash = decl.fullyQualifiedNameHash();1803 const name_hash = decl.fullyQualifiedNameHash();
1716 self.decl_table.removeAssertDiscard(name_hash);1804 self.decl_table.removeAssertDiscard(name_hash);
...@@ -1799,110 +1887,9 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1799,110 +1887,9 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1799 //std.debug.warn("set {} to success\n", .{decl.name});1887 //std.debug.warn("set {} to success\n", .{decl.name});
1800}1888}
18011889
1802fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void {
1803 switch (decl.analysis) {
1804 .unreferenced => unreachable,
1805 .in_progress => unreachable,
1806 .dependency_failure,
1807 .sema_failure,
1808 .sema_failure_retryable,
1809 .codegen_failure,
1810 .codegen_failure_retryable,
1811 .complete,
1812 => return,
1813
1814 .outdated => {}, // Decl re-analysis
1815 }
1816 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1817
1818 // The exports this Decl performs will be re-discovered, so we remove them here
1819 // prior to re-analysis.
1820 self.deleteDeclExports(decl);
1821 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
1822 for (decl.dependencies.items) |dep| {
1823 dep.removeDependant(decl);
1824 if (dep.dependants.items.len == 0) {
1825 // We don't perform a deletion here, because this Decl or another one
1826 // may end up referencing it before the update is complete.
1827 assert(!dep.deletion_flag);
1828 dep.deletion_flag = true;
1829 try self.deletion_set.append(self.allocator, dep);
1830 }
1831 }
1832 decl.dependencies.shrink(self.allocator, 0);
1833 var decl_scope: Scope.DeclAnalysis = .{
1834 .decl = decl,
1835 .arena = std.heap.ArenaAllocator.init(self.allocator),
1836 };
1837 errdefer decl_scope.arena.deinit();
1838
1839 const typed_value = self.analyzeConstInst(&decl_scope.base, old_inst) catch |err| switch (err) {
1840 error.OutOfMemory => return error.OutOfMemory,
1841 error.AnalysisFail => {
1842 switch (decl.analysis) {
1843 .in_progress => decl.analysis = .dependency_failure,
1844 else => {},
1845 }
1846 decl.generation = self.generation;
1847 return error.AnalysisFail;
1848 },
1849 };
1850 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1851 arena_state.* = decl_scope.arena.state;
1852
1853 var prev_type_has_bits = false;
1854 var type_changed = true;
1855
1856 if (decl.typedValueManaged()) |tvm| {
1857 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1858 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
1859
1860 tvm.deinit(self.allocator);
1861 }
1862 decl.typed_value = .{
1863 .most_recent = .{
1864 .typed_value = typed_value,
1865 .arena = arena_state,
1866 },
1867 };
1868 decl.analysis = .complete;
1869 decl.generation = self.generation;
1870 if (typed_value.ty.hasCodeGenBits()) {
1871 // We don't fully codegen the decl until later, but we do need to reserve a global
1872 // offset table index for it. This allows us to codegen decls out of dependency order,
1873 // increasing how many computations can be done in parallel.
1874 try self.bin_file.allocateDeclIndexes(decl);
1875 try self.work_queue.writeItem(.{ .codegen_decl = decl });
1876 } else if (prev_type_has_bits) {
1877 self.bin_file.freeDecl(decl);
1878 }
1879
1880 // If the decl is a function, and the type is the same, we do not need
1881 // to chase the dependants.
1882 if (type_changed or typed_value.val.tag() != .function) {
1883 for (decl.dependants.items) |dep| {
1884 switch (dep.analysis) {
1885 .unreferenced => unreachable,
1886 .in_progress => unreachable,
1887 .outdated => continue, // already queued for update
1888
1889 .dependency_failure,
1890 .sema_failure,
1891 .sema_failure_retryable,
1892 .codegen_failure,
1893 .codegen_failure_retryable,
1894 .complete,
1895 => if (dep.generation != self.generation) {
1896 try self.markOutdatedDecl(dep);
1897 },
1898 }
1899 }
1900 }
1901}
1902
1903fn markOutdatedDecl(self: *Module, decl: *Decl) !void {1890fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1904 //std.debug.warn("mark {} outdated\n", .{decl.name});1891 //std.debug.warn("mark {} outdated\n", .{decl.name});
1905 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });1892 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1906 if (self.failed_decls.remove(decl)) |entry| {1893 if (self.failed_decls.remove(decl)) |entry| {
1907 entry.value.destroy(self.allocator);1894 entry.value.destroy(self.allocator);
1908 }1895 }
...@@ -2381,11 +2368,10 @@ fn createAnonymousDecl(...@@ -2381,11 +2368,10 @@ fn createAnonymousDecl(
2381 decl_arena: *std.heap.ArenaAllocator,2368 decl_arena: *std.heap.ArenaAllocator,
2382 typed_value: TypedValue,2369 typed_value: TypedValue,
2383) !*Decl {2370) !*Decl {
2384 var name_buf: [32]u8 = undefined;
2385 const name_index = self.getNextAnonNameIndex();2371 const name_index = self.getNextAnonNameIndex();
2386 const name = std.fmt.bufPrint(&name_buf, "unnamed_{}", .{name_index}) catch unreachable;
2387 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
2388 const scope_decl = scope.decl().?;2372 const scope_decl = scope.decl().?;
2373 const name = try std.fmt.allocPrint(self.allocator, "{}${}", .{ scope_decl.name, name_index });
2374 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
2389 const src_hash: std.zig.SrcHash = undefined;2375 const src_hash: std.zig.SrcHash = undefined;
2390 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);2376 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
2391 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);2377 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
...@@ -3360,3 +3346,7 @@ pub const ErrorMsg = struct {...@@ -3360,3 +3346,7 @@ pub const ErrorMsg = struct {
3360 self.* = undefined;3346 self.* = undefined;
3361 }3347 }
3362};3348};
3349
3350fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
3351 return @bitCast(u128, a) == @bitCast(u128, b);
3352}
src-self-hosted/main.zig+4
...@@ -487,7 +487,9 @@ fn buildOutputType(...@@ -487,7 +487,9 @@ fn buildOutputType(
487}487}
488488
489fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {489fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
490 var timer = try std.time.Timer.start();
490 try module.update();491 try module.update();
492 const update_nanos = timer.read();
491493
492 var errors = try module.getAllErrorsAlloc();494 var errors = try module.getAllErrorsAlloc();
493 defer errors.deinit(module.allocator);495 defer errors.deinit(module.allocator);
...@@ -501,6 +503,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo...@@ -501,6 +503,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
501 full_err_msg.msg,503 full_err_msg.msg,
502 });504 });
503 }505 }
506 } else {
507 std.debug.print("Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
504 }508 }
505509
506 if (zir_out_path) |zop| {510 if (zir_out_path) |zop| {