Skip to content

Commit f01029c

Browse files
committed
incremental: new AnalUnit to group dependencies on std.builtin decls
This commit reworks how values like the panic handler function are memoized during a compiler invocation. Previously, the value was resolved by whichever analysis requested it first, and cached on `Zcu`. This is problematic for incremental compilation, as after the initial resolution, no dependencies are marked by users of this memoized state. This is arguably acceptable for `std.builtin`, but it's definitely not acceptable for the panic handler/messages, because those can be set by the user (`std.builtin.Panic` checks `@import("root").Panic`). So, here we introduce a new kind of `AnalUnit`, called `memoized_state`. There are 3 such units: * `.{ .memoized_state = .va_list }` resolves the type `std.builtin.VaList` * `.{ .memoized_state = .panic }` resolves `std.Panic` * `.{ .memoized_state = .main }` resolves everything else we want These units essentially "bundle" the resolution of their corresponding declarations, storing the results into fields on `Zcu`. This way, when, for instance, a function wants to call the panic handler, it simply runs `ensureMemoizedStateResolved`, registering one dependency, and pulls the values from the `Zcu`. This "bundling" minimizes dependency edges. The 3 units are separated to allow them to act independently: for instance, the panic handler can use `std.builtin.Type` without triggering a dependency loop.
1 parent fd62912 commit f01029c

File tree

6 files changed

+639
-254
lines changed

6 files changed

+639
-254
lines changed

src/Compilation.zig

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3158,16 +3158,19 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
31583158
if (!refs.contains(anal_unit)) continue;
31593159
}
31603160

3161-
const file_index = switch (anal_unit.unwrap()) {
3162-
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
3163-
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
3164-
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
3165-
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
3166-
};
3161+
report_ok: {
3162+
const file_index = switch (anal_unit.unwrap()) {
3163+
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
3164+
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
3165+
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
3166+
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
3167+
.memoized_state => break :report_ok, // always report std.builtin errors
3168+
};
31673169

3168-
// Skip errors for AnalUnits within files that had a parse failure.
3169-
// We'll try again once parsing succeeds.
3170-
if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
3170+
// Skip errors for AnalUnits within files that had a parse failure.
3171+
// We'll try again once parsing succeeds.
3172+
if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
3173+
}
31713174

31723175
std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
31733176
error_msg.msg,
@@ -3391,7 +3394,7 @@ pub fn addModuleErrorMsg(
33913394
const ref = maybe_ref orelse break;
33923395
const gop = try seen.getOrPut(gpa, ref.referencer);
33933396
if (gop.found_existing) break;
3394-
if (ref_traces.items.len < max_references) {
3397+
if (ref_traces.items.len < max_references) skip: {
33953398
const src = ref.src.upgrade(zcu);
33963399
const source = try src.file_scope.getSource(gpa);
33973400
const span = try src.span(gpa);
@@ -3403,6 +3406,7 @@ pub fn addModuleErrorMsg(
34033406
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
34043407
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
34053408
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
3409+
.memoized_state => break :skip,
34063410
};
34073411
try ref_traces.append(gpa, .{
34083412
.decl_name = try eb.addString(name),
@@ -3670,6 +3674,7 @@ fn performAllTheWorkInner(
36703674
if (try zcu.findOutdatedToAnalyze()) |outdated| {
36713675
try comp.queueJob(switch (outdated.unwrap()) {
36723676
.func => |f| .{ .analyze_func = f },
3677+
.memoized_state,
36733678
.@"comptime",
36743679
.nav_ty,
36753680
.nav_val,
@@ -3737,6 +3742,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
37373742
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
37383743
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
37393744
.type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
3745+
.memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
37403746
.func => unreachable,
37413747
};
37423748
maybe_err catch |err| switch (err) {

src/InternPool.zig

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ namespace_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index)
4949
/// Dependencies on the (non-)existence of some name in a namespace.
5050
/// Value is index into `dep_entries` of the first dependency on this name.
5151
namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.Index),
52+
// Dependencies on the value of fields memoized on `Zcu` (`panic_messages` etc).
53+
// If set, these are indices into `dep_entries` of the first dependency on this state.
54+
memoized_state_main_deps: DepEntry.Index.Optional,
55+
memoized_state_panic_deps: DepEntry.Index.Optional,
56+
memoized_state_va_list_deps: DepEntry.Index.Optional,
5257

5358
/// Given a `Depender`, points to an entry in `dep_entries` whose `depender`
5459
/// matches. The `next_dependee` field can be used to iterate all such entries
@@ -87,6 +92,9 @@ pub const empty: InternPool = .{
8792
.interned_deps = .empty,
8893
.namespace_deps = .empty,
8994
.namespace_name_deps = .empty,
95+
.memoized_state_main_deps = .none,
96+
.memoized_state_panic_deps = .none,
97+
.memoized_state_va_list_deps = .none,
9098
.first_dependency = .empty,
9199
.dep_entries = .empty,
92100
.free_dep_entries = .empty,
@@ -385,6 +393,7 @@ pub const AnalUnit = packed struct(u64) {
385393
nav_ty,
386394
type,
387395
func,
396+
memoized_state,
388397
};
389398

390399
pub const Unwrapped = union(Kind) {
@@ -399,6 +408,8 @@ pub const AnalUnit = packed struct(u64) {
399408
type: InternPool.Index,
400409
/// This `AnalUnit` analyzes the body of the given runtime function.
401410
func: InternPool.Index,
411+
/// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
412+
memoized_state: MemoizedStateStage,
402413
};
403414

404415
pub fn unwrap(au: AnalUnit) Unwrapped {
@@ -434,6 +445,16 @@ pub const AnalUnit = packed struct(u64) {
434445
};
435446
};
436447

448+
pub const MemoizedStateStage = enum(u32) {
449+
/// Everything other than panics and `VaList`.
450+
main,
451+
/// Everything within `std.builtin.Panic`.
452+
/// Since the panic handler is user-provided, this must be able to reference the other memoized state.
453+
panic,
454+
/// Specifically `std.builtin.VaList`. See `Zcu.BuiltinDecl.stage`.
455+
va_list,
456+
};
457+
437458
pub const ComptimeUnit = extern struct {
438459
zir_index: TrackedInst.Index,
439460
namespace: NamespaceIndex,
@@ -769,6 +790,7 @@ pub const Dependee = union(enum) {
769790
interned: Index,
770791
namespace: TrackedInst.Index,
771792
namespace_name: NamespaceNameKey,
793+
memoized_state: MemoizedStateStage,
772794
};
773795

774796
pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: AnalUnit) void {
@@ -819,6 +841,11 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
819841
.interned => |x| ip.interned_deps.get(x),
820842
.namespace => |x| ip.namespace_deps.get(x),
821843
.namespace_name => |x| ip.namespace_name_deps.get(x),
844+
.memoized_state => |stage| switch (stage) {
845+
.main => ip.memoized_state_main_deps.unwrap(),
846+
.panic => ip.memoized_state_panic_deps.unwrap(),
847+
.va_list => ip.memoized_state_va_list_deps.unwrap(),
848+
},
822849
} orelse return .{
823850
.ip = ip,
824851
.next_entry = .none,
@@ -848,6 +875,33 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
848875
// This block should allocate an entry and prepend it to the relevant `*_deps` list.
849876
// The `next` field should be correctly initialized; all other fields may be undefined.
850877
const new_index: DepEntry.Index = switch (dependee) {
878+
.memoized_state => |stage| new_index: {
879+
const deps = switch (stage) {
880+
.main => &ip.memoized_state_main_deps,
881+
.panic => &ip.memoized_state_panic_deps,
882+
.va_list => &ip.memoized_state_va_list_deps,
883+
};
884+
885+
if (deps.unwrap()) |first| {
886+
if (ip.dep_entries.items[@intFromEnum(first)].depender == .none) {
887+
// Dummy entry, so we can reuse it rather than allocating a new one!
888+
break :new_index first;
889+
}
890+
}
891+
892+
// Prepend a new dependency.
893+
const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
894+
break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
895+
} else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
896+
if (deps.unwrap()) |old_first| {
897+
ptr.next = old_first.toOptional();
898+
ip.dep_entries.items[@intFromEnum(old_first)].prev = new_index.toOptional();
899+
} else {
900+
ptr.next = .none;
901+
}
902+
deps.* = new_index.toOptional();
903+
break :new_index new_index;
904+
},
851905
inline else => |dependee_payload, tag| new_index: {
852906
const gop = try switch (tag) {
853907
.file => ip.file_deps,
@@ -857,6 +911,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
857911
.interned => ip.interned_deps,
858912
.namespace => ip.namespace_deps,
859913
.namespace_name => ip.namespace_name_deps,
914+
.memoized_state => comptime unreachable,
860915
}.getOrPut(gpa, dependee_payload);
861916

862917
if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {

0 commit comments

Comments
 (0)