aboutsummaryrefslogtreecommitdiff
path: root/src/InternPool.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-01-04 05:09:02 +0000
committermlugg <mlugg@mlugg.co.uk>2025-01-04 07:51:19 +0000
commitf01029c4af7d3015c1c3f7d36bb62f7d5afb53a4 (patch)
tree21fd6696e57bc59fb1c097903e8421562d0862f5 /src/InternPool.zig
parentfd62912787ee4f26b06ba86560e9aa605095ae04 (diff)
downloadzig-f01029c4af7d3015c1c3f7d36bb62f7d5afb53a4.tar.gz
zig-f01029c4af7d3015c1c3f7d36bb62f7d5afb53a4.zip
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.
Diffstat (limited to 'src/InternPool.zig')
-rw-r--r--src/InternPool.zig55
1 files changed, 55 insertions, 0 deletions
diff --git a/src/InternPool.zig b/src/InternPool.zig
index c0b4d2f446..c34e21a4d3 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -49,6 +49,11 @@ namespace_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index)
/// Dependencies on the (non-)existence of some name in a namespace.
/// Value is index into `dep_entries` of the first dependency on this name.
namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.Index),
+// Dependencies on the value of fields memoized on `Zcu` (`panic_messages` etc).
+// If set, these are indices into `dep_entries` of the first dependency on this state.
+memoized_state_main_deps: DepEntry.Index.Optional,
+memoized_state_panic_deps: DepEntry.Index.Optional,
+memoized_state_va_list_deps: DepEntry.Index.Optional,
/// Given a `Depender`, points to an entry in `dep_entries` whose `depender`
/// matches. The `next_dependee` field can be used to iterate all such entries
@@ -87,6 +92,9 @@ pub const empty: InternPool = .{
.interned_deps = .empty,
.namespace_deps = .empty,
.namespace_name_deps = .empty,
+ .memoized_state_main_deps = .none,
+ .memoized_state_panic_deps = .none,
+ .memoized_state_va_list_deps = .none,
.first_dependency = .empty,
.dep_entries = .empty,
.free_dep_entries = .empty,
@@ -385,6 +393,7 @@ pub const AnalUnit = packed struct(u64) {
nav_ty,
type,
func,
+ memoized_state,
};
pub const Unwrapped = union(Kind) {
@@ -399,6 +408,8 @@ pub const AnalUnit = packed struct(u64) {
type: InternPool.Index,
/// This `AnalUnit` analyzes the body of the given runtime function.
func: InternPool.Index,
+ /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
+ memoized_state: MemoizedStateStage,
};
pub fn unwrap(au: AnalUnit) Unwrapped {
@@ -434,6 +445,16 @@ pub const AnalUnit = packed struct(u64) {
};
};
+pub const MemoizedStateStage = enum(u32) {
+ /// Everything other than panics and `VaList`.
+ main,
+ /// Everything within `std.builtin.Panic`.
+ /// Since the panic handler is user-provided, this must be able to reference the other memoized state.
+ panic,
+ /// Specifically `std.builtin.VaList`. See `Zcu.BuiltinDecl.stage`.
+ va_list,
+};
+
pub const ComptimeUnit = extern struct {
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
@@ -769,6 +790,7 @@ pub const Dependee = union(enum) {
interned: Index,
namespace: TrackedInst.Index,
namespace_name: NamespaceNameKey,
+ memoized_state: MemoizedStateStage,
};
pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: AnalUnit) void {
@@ -819,6 +841,11 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.interned => |x| ip.interned_deps.get(x),
.namespace => |x| ip.namespace_deps.get(x),
.namespace_name => |x| ip.namespace_name_deps.get(x),
+ .memoized_state => |stage| switch (stage) {
+ .main => ip.memoized_state_main_deps.unwrap(),
+ .panic => ip.memoized_state_panic_deps.unwrap(),
+ .va_list => ip.memoized_state_va_list_deps.unwrap(),
+ },
} orelse return .{
.ip = ip,
.next_entry = .none,
@@ -848,6 +875,33 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
// This block should allocate an entry and prepend it to the relevant `*_deps` list.
// The `next` field should be correctly initialized; all other fields may be undefined.
const new_index: DepEntry.Index = switch (dependee) {
+ .memoized_state => |stage| new_index: {
+ const deps = switch (stage) {
+ .main => &ip.memoized_state_main_deps,
+ .panic => &ip.memoized_state_panic_deps,
+ .va_list => &ip.memoized_state_va_list_deps,
+ };
+
+ if (deps.unwrap()) |first| {
+ if (ip.dep_entries.items[@intFromEnum(first)].depender == .none) {
+ // Dummy entry, so we can reuse it rather than allocating a new one!
+ break :new_index first;
+ }
+ }
+
+ // Prepend a new dependency.
+ const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
+ break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
+ } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
+ if (deps.unwrap()) |old_first| {
+ ptr.next = old_first.toOptional();
+ ip.dep_entries.items[@intFromEnum(old_first)].prev = new_index.toOptional();
+ } else {
+ ptr.next = .none;
+ }
+ deps.* = new_index.toOptional();
+ break :new_index new_index;
+ },
inline else => |dependee_payload, tag| new_index: {
const gop = try switch (tag) {
.file => ip.file_deps,
@@ -857,6 +911,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
.interned => ip.interned_deps,
.namespace => ip.namespace_deps,
.namespace_name => ip.namespace_name_deps,
+ .memoized_state => comptime unreachable,
}.getOrPut(gpa, dependee_payload);
if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {