authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-04 14:42:09+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-04 16:20:29+00:00
log3ca588bcc6d6640b7faa41a271580dd384963927
treec47ab77e60e00f5c52a32257124d3bc6b642cf77
parent55a2e535fdb663793b84769cb6c3a261bda3fc66
signaturelock-open Commit is signed but in an unrecognized format.

compiler: integrate importing ZON with incremental compilation

The changes from a few commits earlier, where semantic analysis no longer occurs if any Zig files failed to lower to ZIR, mean `file` dependencies are no longer necessary! However, we now need them for ZON files, to be invalidated whenever a ZON file changes.

5 files changed, 48 insertions(+), 22 deletions(-)

src/Compilation.zig+12-4
...@@ -2903,10 +2903,12 @@ pub fn makeBinFileWritable(comp: *Compilation) !void {...@@ -2903,10 +2903,12 @@ pub fn makeBinFileWritable(comp: *Compilation) !void {
2903const Header = extern struct {2903const Header = extern struct {
2904 intern_pool: extern struct {2904 intern_pool: extern struct {
2905 thread_count: u32,2905 thread_count: u32,
2906 file_deps_len: u32,
2907 src_hash_deps_len: u32,2906 src_hash_deps_len: u32,
2908 nav_val_deps_len: u32,2907 nav_val_deps_len: u32,
2909 nav_ty_deps_len: u32,2908 nav_ty_deps_len: u32,
2909 interned_deps_len: u32,
2910 zon_file_deps_len: u32,
2911 embed_file_deps_len: u32,
2910 namespace_deps_len: u32,2912 namespace_deps_len: u32,
2911 namespace_name_deps_len: u32,2913 namespace_name_deps_len: u32,
2912 first_dependency_len: u32,2914 first_dependency_len: u32,
...@@ -2947,10 +2949,12 @@ pub fn saveState(comp: *Compilation) !void {...@@ -2947,10 +2949,12 @@ pub fn saveState(comp: *Compilation) !void {
2947 const header: Header = .{2949 const header: Header = .{
2948 .intern_pool = .{2950 .intern_pool = .{
2949 .thread_count = @intCast(ip.locals.len),2951 .thread_count = @intCast(ip.locals.len),
2950 .file_deps_len = @intCast(ip.file_deps.count()),
2951 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),2952 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
2952 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),2953 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
2953 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),2954 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
2955 .interned_deps_len = @intCast(ip.interned_deps.count()),
2956 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
2957 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
2954 .namespace_deps_len = @intCast(ip.namespace_deps.count()),2958 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
2955 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),2959 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
2956 .first_dependency_len = @intCast(ip.first_dependency.count()),2960 .first_dependency_len = @intCast(ip.first_dependency.count()),
...@@ -2975,14 +2979,18 @@ pub fn saveState(comp: *Compilation) !void {...@@ -2975,14 +2979,18 @@ pub fn saveState(comp: *Compilation) !void {
2975 addBuf(&bufs, mem.asBytes(&header));2979 addBuf(&bufs, mem.asBytes(&header));
2976 addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));2980 addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));
29772981
2978 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.keys()));
2979 addBuf(&bufs, mem.sliceAsBytes(ip.file_deps.values()));
2980 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));2982 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));
2981 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));2983 addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
2982 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));2984 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
2983 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));2985 addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
2984 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));2986 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
2985 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));2987 addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
2988 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys()));
2989 addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values()));
2990 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys()));
2991 addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values()));
2992 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys()));
2993 addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values()));
2986 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));2994 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
2987 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));2995 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
2988 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));2996 addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
src/InternPool.zig+8-12
...@@ -17,13 +17,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),...@@ -17,13 +17,6 @@ tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),
17/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.17/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.
18tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),18tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),
1919
20/// Dependencies on whether an entire file gets past AstGen.
21/// These are triggered by `@import`, so that:
22/// * if a file initially fails AstGen, triggering a transitive failure, when a future update
23/// causes it to succeed AstGen, the `@import` is re-analyzed, allowing analysis to proceed
24/// * if a file initially succeds AstGen, but a future update causes the file to fail it,
25/// the `@import` is re-analyzed, registering a transitive failure
26file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
27/// Dependencies on the source code hash associated with a ZIR instruction.20/// Dependencies on the source code hash associated with a ZIR instruction.
28/// * For a `declaration`, this is the entire declaration body.21/// * For a `declaration`, this is the entire declaration body.
29/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).22/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
...@@ -42,6 +35,9 @@ nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),...@@ -42,6 +35,9 @@ nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
42/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)35/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
43/// Value is index into `dep_entries` of the first dependency on this interned value.36/// Value is index into `dep_entries` of the first dependency on this interned value.
44interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),37interned_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
38/// Dependencies on a ZON file. Triggered by `@import` of ZON.
39/// Value is index into `dep_entries` of the first dependency on this ZON file.
40zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
45/// Dependencies on an embedded file.41/// Dependencies on an embedded file.
46/// Introduced by `@embedFile`; invalidated when the file changes.42/// Introduced by `@embedFile`; invalidated when the file changes.
47/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.43/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.
...@@ -89,11 +85,11 @@ pub const empty: InternPool = .{...@@ -89,11 +85,11 @@ pub const empty: InternPool = .{
89 .tid_shift_30 = if (single_threaded) 0 else 31,85 .tid_shift_30 = if (single_threaded) 0 else 31,
90 .tid_shift_31 = if (single_threaded) 0 else 31,86 .tid_shift_31 = if (single_threaded) 0 else 31,
91 .tid_shift_32 = if (single_threaded) 0 else 31,87 .tid_shift_32 = if (single_threaded) 0 else 31,
92 .file_deps = .empty,
93 .src_hash_deps = .empty,88 .src_hash_deps = .empty,
94 .nav_val_deps = .empty,89 .nav_val_deps = .empty,
95 .nav_ty_deps = .empty,90 .nav_ty_deps = .empty,
96 .interned_deps = .empty,91 .interned_deps = .empty,
92 .zon_file_deps = .empty,
97 .embed_file_deps = .empty,93 .embed_file_deps = .empty,
98 .namespace_deps = .empty,94 .namespace_deps = .empty,
99 .namespace_name_deps = .empty,95 .namespace_name_deps = .empty,
...@@ -824,11 +820,11 @@ pub const Nav = struct {...@@ -824,11 +820,11 @@ pub const Nav = struct {
824};820};
825821
826pub const Dependee = union(enum) {822pub const Dependee = union(enum) {
827 file: FileIndex,
828 src_hash: TrackedInst.Index,823 src_hash: TrackedInst.Index,
829 nav_val: Nav.Index,824 nav_val: Nav.Index,
830 nav_ty: Nav.Index,825 nav_ty: Nav.Index,
831 interned: Index,826 interned: Index,
827 zon_file: FileIndex,
832 embed_file: Zcu.EmbedFile.Index,828 embed_file: Zcu.EmbedFile.Index,
833 namespace: TrackedInst.Index,829 namespace: TrackedInst.Index,
834 namespace_name: NamespaceNameKey,830 namespace_name: NamespaceNameKey,
...@@ -876,11 +872,11 @@ pub const DependencyIterator = struct {...@@ -876,11 +872,11 @@ pub const DependencyIterator = struct {
876872
877pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {873pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
878 const first_entry = switch (dependee) {874 const first_entry = switch (dependee) {
879 .file => |x| ip.file_deps.get(x),
880 .src_hash => |x| ip.src_hash_deps.get(x),875 .src_hash => |x| ip.src_hash_deps.get(x),
881 .nav_val => |x| ip.nav_val_deps.get(x),876 .nav_val => |x| ip.nav_val_deps.get(x),
882 .nav_ty => |x| ip.nav_ty_deps.get(x),877 .nav_ty => |x| ip.nav_ty_deps.get(x),
883 .interned => |x| ip.interned_deps.get(x),878 .interned => |x| ip.interned_deps.get(x),
879 .zon_file => |x| ip.zon_file_deps.get(x),
884 .embed_file => |x| ip.embed_file_deps.get(x),880 .embed_file => |x| ip.embed_file_deps.get(x),
885 .namespace => |x| ip.namespace_deps.get(x),881 .namespace => |x| ip.namespace_deps.get(x),
886 .namespace_name => |x| ip.namespace_name_deps.get(x),882 .namespace_name => |x| ip.namespace_name_deps.get(x),
...@@ -947,11 +943,11 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend...@@ -947,11 +943,11 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
947 },943 },
948 inline else => |dependee_payload, tag| new_index: {944 inline else => |dependee_payload, tag| new_index: {
949 const gop = try switch (tag) {945 const gop = try switch (tag) {
950 .file => ip.file_deps,
951 .src_hash => ip.src_hash_deps,946 .src_hash => ip.src_hash_deps,
952 .nav_val => ip.nav_val_deps,947 .nav_val => ip.nav_val_deps,
953 .nav_ty => ip.nav_ty_deps,948 .nav_ty => ip.nav_ty_deps,
954 .interned => ip.interned_deps,949 .interned => ip.interned_deps,
950 .zon_file => ip.zon_file_deps,
955 .embed_file => ip.embed_file_deps,951 .embed_file => ip.embed_file_deps,
956 .namespace => ip.namespace_deps,952 .namespace => ip.namespace_deps,
957 .namespace_name => ip.namespace_name_deps,953 .namespace_name => ip.namespace_name_deps,
...@@ -6688,11 +6684,11 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {...@@ -6688,11 +6684,11 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
6688pub fn deinit(ip: *InternPool, gpa: Allocator) void {6684pub fn deinit(ip: *InternPool, gpa: Allocator) void {
6689 if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null);6685 if (debug_state.enable_checks) std.debug.assert(debug_state.intern_pool == null);
66906686
6691 ip.file_deps.deinit(gpa);
6692 ip.src_hash_deps.deinit(gpa);6687 ip.src_hash_deps.deinit(gpa);
6693 ip.nav_val_deps.deinit(gpa);6688 ip.nav_val_deps.deinit(gpa);
6694 ip.nav_ty_deps.deinit(gpa);6689 ip.nav_ty_deps.deinit(gpa);
6695 ip.interned_deps.deinit(gpa);6690 ip.interned_deps.deinit(gpa);
6691 ip.zon_file_deps.deinit(gpa);
6696 ip.embed_file_deps.deinit(gpa);6692 ip.embed_file_deps.deinit(gpa);
6697 ip.namespace_deps.deinit(gpa);6693 ip.namespace_deps.deinit(gpa);
6698 ip.namespace_name_deps.deinit(gpa);6694 ip.namespace_name_deps.deinit(gpa);
src/Sema.zig+1-2
...@@ -6143,7 +6143,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -6143,7 +6143,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
6143 pt.updateFile(result.file, path_digest) catch |err|6143 pt.updateFile(result.file, path_digest) catch |err|
6144 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});6144 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
61456145
6146 try sema.declareDependency(.{ .file = result.file_index });
6147 try pt.ensureFileAnalyzed(result.file_index);6146 try pt.ensureFileAnalyzed(result.file_index);
6148 const ty = zcu.fileRootType(result.file_index);6147 const ty = zcu.fileRootType(result.file_index);
6149 try sema.declareDependency(.{ .interned = ty });6148 try sema.declareDependency(.{ .interned = ty });
...@@ -13986,7 +13985,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -13986,7 +13985,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
13986 };13985 };
13987 switch (result.file.getMode()) {13986 switch (result.file.getMode()) {
13988 .zig => {13987 .zig => {
13989 try sema.declareDependency(.{ .file = result.file_index });
13990 try pt.ensureFileAnalyzed(result.file_index);13988 try pt.ensureFileAnalyzed(result.file_index);
13991 const ty = zcu.fileRootType(result.file_index);13989 const ty = zcu.fileRootType(result.file_index);
13992 try sema.declareDependency(.{ .interned = ty });13990 try sema.declareDependency(.{ .interned = ty });
...@@ -14003,6 +14001,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -14003,6 +14001,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
14003 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});14001 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
14004 }14002 }
1400514003
14004 try sema.declareDependency(.{ .zon_file = result.file_index });
14006 const interned = try LowerZon.run(14005 const interned = try LowerZon.run(
14007 sema,14006 sema,
14008 result.file,14007 result.file,
src/Zcu.zig+12-4
...@@ -705,6 +705,14 @@ pub const File = struct {...@@ -705,6 +705,14 @@ pub const File = struct {
705 /// field is populated with that old ZIR.705 /// field is populated with that old ZIR.
706 prev_zir: ?*Zir = null,706 prev_zir: ?*Zir = null,
707707
708 /// This field serves a similar purpose to `prev_zir`, but for ZOIR. However, since we do not
709 /// need to map old ZOIR to new ZOIR -- instead only invalidating dependencies if the ZOIR
710 /// changed -- this field is just a simple boolean.
711 ///
712 /// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`,
713 /// we invalidate the corresponding `zon_file` dependency, and reset it to `false`.
714 zoir_invalidated: bool = false,
715
708 /// A single reference to a file.716 /// A single reference to a file.
709 pub const Reference = union(enum) {717 pub const Reference = union(enum) {
710 /// The file is imported directly (i.e. not as a package) with @import.718 /// The file is imported directly (i.e. not as a package) with @import.
...@@ -4074,10 +4082,6 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com...@@ -4074,10 +4082,6 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
4074 const zcu = data.zcu;4082 const zcu = data.zcu;
4075 const ip = &zcu.intern_pool;4083 const ip = &zcu.intern_pool;
4076 switch (data.dependee) {4084 switch (data.dependee) {
4077 .file => |file| {
4078 const file_path = zcu.fileByIndex(file).sub_file_path;
4079 return writer.print("file('{s}')", .{file_path});
4080 },
4081 .src_hash => |ti| {4085 .src_hash => |ti| {
4082 const info = ti.resolveFull(ip) orelse {4086 const info = ti.resolveFull(ip) orelse {
4083 return writer.writeAll("inst(<lost>)");4087 return writer.writeAll("inst(<lost>)");
...@@ -4098,6 +4102,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com...@@ -4098,6 +4102,10 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
4098 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),4102 .func => |f| return writer.print("ies('{}')", .{ip.getNav(f.owner_nav).fqn.fmt(ip)}),
4099 else => unreachable,4103 else => unreachable,
4100 },4104 },
4105 .zon_file => |file| {
4106 const file_path = zcu.fileByIndex(file).sub_file_path;
4107 return writer.print("zon_file('{s}')", .{file_path});
4108 },
4101 .embed_file => |ef_idx| {4109 .embed_file => |ef_idx| {
4102 const ef = ef_idx.get(zcu);4110 const ef = ef_idx.get(zcu);
4103 return writer.print("embed_file('{s}')", .{std.fs.path.fmtJoin(&.{4111 return writer.print("embed_file('{s}')", .{std.fs.path.fmtJoin(&.{
src/Zcu/PerThread.zig+15
...@@ -145,6 +145,9 @@ pub fn updateFile(...@@ -145,6 +145,9 @@ pub fn updateFile(
145 file.zir = null;145 file.zir = null;
146 }146 }
147147
148 // If ZOIR is changing, then we need to invalidate dependencies on it
149 if (file.zoir != null) file.zoir_invalidated = true;
150
148 // We're going to re-load everything, so unload source, AST, ZIR, ZOIR.151 // We're going to re-load everything, so unload source, AST, ZIR, ZOIR.
149 file.unload(gpa);152 file.unload(gpa);
150153
...@@ -380,11 +383,23 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -380,11 +383,23 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
380 const gpa = zcu.gpa;383 const gpa = zcu.gpa;
381384
382 // We need to visit every updated File for every TrackedInst in InternPool.385 // We need to visit every updated File for every TrackedInst in InternPool.
386 // This only includes Zig files; ZON files are omitted.
383 var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty;387 var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty;
384 defer cleanupUpdatedFiles(gpa, &updated_files);388 defer cleanupUpdatedFiles(gpa, &updated_files);
389
385 for (zcu.import_table.values()) |file_index| {390 for (zcu.import_table.values()) |file_index| {
386 const file = zcu.fileByIndex(file_index);391 const file = zcu.fileByIndex(file_index);
387 assert(file.status == .success);392 assert(file.status == .success);
393 switch (file.getMode()) {
394 .zig => {}, // logic below
395 .zon => {
396 if (file.zoir_invalidated) {
397 try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index });
398 file.zoir_invalidated = false;
399 }
400 continue;
401 },
402 }
388 const old_zir = file.prev_zir orelse continue;403 const old_zir = file.prev_zir orelse continue;
389 const new_zir = file.zir.?;404 const new_zir = file.zir.?;
390 const gop = try updated_files.getOrPut(gpa, file_index);405 const gop = try updated_files.getOrPut(gpa, file_index);