authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-09 13:43:01+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-14 07:40:08+00:00
log075c103332effdac80d2c00e59f06ee0fea95b49
tree198f342622d63b78ab319c2f4c6465052b4f1d6e
parent1421d329a3bbe4891eda8f234139be787fefcd2f
signaturelock-open Commit is signed but in an unrecognized format.

compiler: add `func_ies` incremental dependencies

This was an oversight in my original design. This new form of dependency is invalidated when the resolved IES for a runtime function changes.

3 files changed, 50 insertions(+), 20 deletions(-)

src/InternPool.zig+7
...@@ -67,6 +67,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index)...@@ -67,6 +67,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index)
67/// Dependencies on the value of a Decl.67/// Dependencies on the value of a Decl.
68/// Value is index into `dep_entries` of the first dependency on this Decl value.68/// Value is index into `dep_entries` of the first dependency on this Decl value.
69decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{},69decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{},
70/// Dependencies on the IES of a runtime function.
71/// Value is index into `dep_entries` of the first dependency on this Decl value.
72func_ies_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index) = .{},
70/// Dependencies on the full set of names in a ZIR namespace.73/// Dependencies on the full set of names in a ZIR namespace.
71/// Key refers to a `struct_decl`, `union_decl`, etc.74/// Key refers to a `struct_decl`, `union_decl`, etc.
72/// Value is index into `dep_entries` of the first dependency on this namespace.75/// Value is index into `dep_entries` of the first dependency on this namespace.
...@@ -167,6 +170,7 @@ pub const Depender = enum(u32) {...@@ -167,6 +170,7 @@ pub const Depender = enum(u32) {
167pub const Dependee = union(enum) {170pub const Dependee = union(enum) {
168 src_hash: TrackedInst.Index,171 src_hash: TrackedInst.Index,
169 decl_val: DeclIndex,172 decl_val: DeclIndex,
173 func_ies: Index,
170 namespace: TrackedInst.Index,174 namespace: TrackedInst.Index,
171 namespace_name: NamespaceNameKey,175 namespace_name: NamespaceNameKey,
172};176};
...@@ -212,6 +216,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI...@@ -212,6 +216,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
212 const first_entry = switch (dependee) {216 const first_entry = switch (dependee) {
213 .src_hash => |x| ip.src_hash_deps.get(x),217 .src_hash => |x| ip.src_hash_deps.get(x),
214 .decl_val => |x| ip.decl_val_deps.get(x),218 .decl_val => |x| ip.decl_val_deps.get(x),
219 .func_ies => |x| ip.func_ies_deps.get(x),
215 .namespace => |x| ip.namespace_deps.get(x),220 .namespace => |x| ip.namespace_deps.get(x),
216 .namespace_name => |x| ip.namespace_name_deps.get(x),221 .namespace_name => |x| ip.namespace_name_deps.get(x),
217 } orelse return .{222 } orelse return .{
...@@ -251,6 +256,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: Depender, depend...@@ -251,6 +256,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: Depender, depend
251 const gop = try switch (tag) {256 const gop = try switch (tag) {
252 .src_hash => ip.src_hash_deps,257 .src_hash => ip.src_hash_deps,
253 .decl_val => ip.decl_val_deps,258 .decl_val => ip.decl_val_deps,
259 .func_ies => ip.func_ies_deps,
254 .namespace => ip.namespace_deps,260 .namespace => ip.namespace_deps,
255 .namespace_name => ip.namespace_name_deps,261 .namespace_name => ip.namespace_name_deps,
256 }.getOrPut(gpa, dependee_payload);262 }.getOrPut(gpa, dependee_payload);
...@@ -4324,6 +4330,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {...@@ -4324,6 +4330,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
43244330
4325 ip.src_hash_deps.deinit(gpa);4331 ip.src_hash_deps.deinit(gpa);
4326 ip.decl_val_deps.deinit(gpa);4332 ip.decl_val_deps.deinit(gpa);
4333 ip.func_ies_deps.deinit(gpa);
4327 ip.namespace_deps.deinit(gpa);4334 ip.namespace_deps.deinit(gpa);
4328 ip.namespace_name_deps.deinit(gpa);4335 ip.namespace_name_deps.deinit(gpa);
43294336
src/Module.zig+37-20
...@@ -2661,10 +2661,9 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -2661,10 +2661,9 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2661 }2661 }
2662 // If this is a Decl and was not previously PO, we must recursively2662 // If this is a Decl and was not previously PO, we must recursively
2663 // mark dependencies on its tyval as PO.2663 // mark dependencies on its tyval as PO.
2664 if (opt_po_entry == null) switch (depender.unwrap()) {2664 if (opt_po_entry == null) {
2665 .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index),2665 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
2666 .func => {},2666 }
2667 };
2668 }2667 }
2669}2668}
26702669
...@@ -2694,15 +2693,19 @@ fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {...@@ -2694,15 +2693,19 @@ fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
2694 // as no longer PO.2693 // as no longer PO.
2695 switch (depender.unwrap()) {2694 switch (depender.unwrap()) {
2696 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }),2695 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }),
2697 .func => {},2696 .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }),
2698 }2697 }
2699 }2698 }
2700}2699}
27012700
2702/// Given a Decl which is newly outdated or PO, mark all dependers which depend2701/// Given a Depender which is newly outdated or PO, mark all Dependers which may
2703/// on its tyval as PO.2702/// in turn be PO, due to a dependency on the original Depender's tyval or IES.
2704fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void {2703fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void {
2705 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });2704 var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) {
2705 .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced
2706 .func => |func_index| .{ .func_ies = func_index },
2707 });
2708
2706 while (it.next()) |po| {2709 while (it.next()) |po| {
2707 if (zcu.outdated.getPtr(po)) |po_dep_count| {2710 if (zcu.outdated.getPtr(po)) |po_dep_count| {
2708 // This dependency is already outdated, but it now has one more PO2711 // This dependency is already outdated, but it now has one more PO
...@@ -2719,14 +2722,9 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v...@@ -2719,14 +2722,9 @@ fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !v
2719 continue;2722 continue;
2720 }2723 }
2721 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);2724 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
2722 // If this ia a Decl, we must recursively mark dependencies2725 // This Depender was not already PO, so we must recursively mark its dependers as also PO.
2723 // on its tyval as PO.2726 try zcu.markTransitiveDependersPotentiallyOutdated(po);
2724 switch (po.unwrap()) {
2725 .decl => |po_decl| try zcu.markDeclDependenciesPotentiallyOutdated(po_decl),
2726 .func => {},
2727 }
2728 }2727 }
2729 // TODO: repeat the above for `decl_ty` dependencies when they are introduced
2730}2728}
27312729
2732pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender {2730pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender {
...@@ -2852,10 +2850,7 @@ pub fn flushRetryableFailures(zcu: *Zcu) !void {...@@ -2852,10 +2850,7 @@ pub fn flushRetryableFailures(zcu: *Zcu) !void {
2852 // This Depender was not marked PO, but is now outdated. Mark it as2850 // This Depender was not marked PO, but is now outdated. Mark it as
2853 // such, then recursively mark transitive dependencies as PO.2851 // such, then recursively mark transitive dependencies as PO.
2854 try zcu.outdated.put(gpa, depender, 0);2852 try zcu.outdated.put(gpa, depender, 0);
2855 switch (depender.unwrap()) {2853 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
2856 .decl => |decl| try zcu.markDeclDependenciesPotentiallyOutdated(decl),
2857 .func => {},
2858 }
2859 }2854 }
2860 zcu.retryable_failures.clearRetainingCapacity();2855 zcu.retryable_failures.clearRetainingCapacity();
2861}2856}
...@@ -3142,11 +3137,19 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError...@@ -3142,11 +3137,19 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError
3142 // decl now refers to a different function, making this one orphaned. If3137 // decl now refers to a different function, making this one orphaned. If
3143 // that's the case, we should remove this function from the binary.3138 // that's the case, we should remove this function from the binary.
3144 if (decl.val.ip_index != func_index) {3139 if (decl.val.ip_index != func_index) {
3140 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3145 ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));3141 ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
3146 ip.remove(func_index);3142 ip.remove(func_index);
3147 @panic("TODO: remove orphaned function from binary");3143 @panic("TODO: remove orphaned function from binary");
3148 }3144 }
31493145
3146 // We'll want to remember what the IES used to be before the update for
3147 // dependency invalidation purposes.
3148 const old_resolved_ies = if (func.analysis(ip).inferred_error_set)
3149 func.resolvedErrorSet(ip).*
3150 else
3151 .none;
3152
3150 switch (decl.analysis) {3153 switch (decl.analysis) {
3151 .unreferenced => unreachable,3154 .unreferenced => unreachable,
3152 .in_progress => unreachable,3155 .in_progress => unreachable,
...@@ -3203,6 +3206,20 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError...@@ -3203,6 +3206,20 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError
3203 };3206 };
3204 defer air.deinit(gpa);3207 defer air.deinit(gpa);
32053208
3209 const invalidate_ies_deps = i: {
3210 if (!was_outdated) break :i false;
3211 if (!func.analysis(ip).inferred_error_set) break :i true;
3212 const new_resolved_ies = func.resolvedErrorSet(ip).*;
3213 break :i new_resolved_ies != old_resolved_ies;
3214 };
3215 if (invalidate_ies_deps) {
3216 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
3217 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3218 } else if (was_outdated) {
3219 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
3220 try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index });
3221 }
3222
3206 const comp = zcu.comp;3223 const comp = zcu.comp;
32073224
3208 const dump_air = build_options.enable_debug_extensions and comp.verbose_air;3225 const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
src/Sema.zig+6
...@@ -36462,8 +36462,14 @@ fn resolveInferredErrorSet(...@@ -36462,8 +36462,14 @@ fn resolveInferredErrorSet(
36462 const ip = &mod.intern_pool;36462 const ip = &mod.intern_pool;
36463 const func_index = ip.iesFuncIndex(ies_index);36463 const func_index = ip.iesFuncIndex(ies_index);
36464 const func = mod.funcInfo(func_index);36464 const func = mod.funcInfo(func_index);
36465
36466 try sema.declareDependency(.{ .func_ies = func_index });
36467
36468 // TODO: during an incremental update this might not be `.none`, but the
36469 // function might be out-of-date!
36465 const resolved_ty = func.resolvedErrorSet(ip).*;36470 const resolved_ty = func.resolvedErrorSet(ip).*;
36466 if (resolved_ty != .none) return resolved_ty;36471 if (resolved_ty != .none) return resolved_ty;
36472
36467 if (func.analysis(ip).state == .in_progress)36473 if (func.analysis(ip).state == .in_progress)
36468 return sema.fail(block, src, "unable to resolve inferred error set", .{});36474 return sema.fail(block, src, "unable to resolve inferred error set", .{});
3646936475