aboutsummaryrefslogtreecommitdiff
path: root/src/Zcu.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2024-08-16 12:46:52 +0100
committerJacob Young <jacobly0@users.noreply.github.com>2024-08-17 18:50:10 -0400
commit84c2ebd6c6b16752d8d030d5904d0a525283cbf5 (patch)
tree442234d4a737c6fbcf4f165ac18fa2a31cef9ac1 /src/Zcu.zig
parent65cbdefe4d923efd8fd2cfb555cc02a52c5635fc (diff)
downloadzig-84c2ebd6c6b16752d8d030d5904d0a525283cbf5.tar.gz
zig-84c2ebd6c6b16752d8d030d5904d0a525283cbf5.zip
frontend: incremental compilation progress
Another big commit, sorry! This commit makes all fixes necessary for incremental updates of the compiler itself (specifically, adding a breakpoint to `zirCompileLog`) to succeed, at least on the frontend. The biggest change here is a reform to how types are handled. It works like this: * When a type is first created in `zirStructDecl` etc, its namespace is scanned. If the type requires resolution, an `interned` dependency is declared for the containing `AnalUnit`. * `zirThis` also declared an `interned` dependency for its `AnalUnit` on the namespace's owner type. * If the type's namespace changes, the surrounding source declaration changes hash, so `zirStructDecl` etc will be hit again. We check whether the namespace has been scanned this generation, and re-scan it if not. * Namespace lookups also check whether the namespace in question requires a re-scan based on the generation. This is because there's no guarantee that the `zirStructDecl` is re-analyzed before the namespace lookup is re-analyzed. * If a type's structure (essentially its fields) change, then the type's `Cau` is considered outdated. When the type is re-analyzed due to being outdated, or the `zirStructDecl` is re-analyzed by being transitively outdated, or a corresponding `zirThis` is re-analyzed by being transitively outdated, the struct type is recreated at a new `InternPool` index. The namespace's owner is updated (but not re-scanned, since that is handled by the mechanisms above), and the old type, while remaining a valid `Index`, is removed from the map metadata so it will never be found by lookups. `zirStructDecl` and `zirThis` store an `interned` dependency on the *new* type.
Diffstat (limited to 'src/Zcu.zig')
-rw-r--r--src/Zcu.zig61
1 files changed, 34 insertions, 27 deletions
diff --git a/src/Zcu.zig b/src/Zcu.zig
index 8626a147b6..63feb2d00c 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -215,6 +215,8 @@ panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId
panic_func_index: InternPool.Index = .none,
null_stack_trace: InternPool.Index = .none,
+generation: u32 = 0,
+
pub const PerThread = @import("Zcu/PerThread.zig");
pub const PanicId = enum {
@@ -332,6 +334,7 @@ pub const TypeReference = struct {
pub const Namespace = struct {
parent: OptionalIndex,
file_scope: File.Index,
+ generation: u32,
/// Will be a struct, enum, union, or opaque.
owner_type: InternPool.Index,
/// Members of the namespace which are marked `pub`.
@@ -2295,7 +2298,7 @@ pub fn markDependeeOutdated(
marked_po: enum { not_marked_po, marked_po },
dependee: InternPool.Dependee,
) !void {
- log.debug("outdated dependee: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("outdated dependee: {}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
@@ -2303,9 +2306,9 @@ pub fn markDependeeOutdated(
.not_marked_po => {},
.marked_po => {
po_dep_count.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
+ log.debug("outdated {} => already outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
},
@@ -2316,20 +2319,19 @@ pub fn markDependeeOutdated(
const new_po_dep_count = switch (marked_po) {
.not_marked_po => if (opt_po_entry) |e| e.value else 0,
.marked_po => if (opt_po_entry) |e| e.value - 1 else {
- // This dependency has been registered during in-progress analysis, but the unit is
- // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
+ // This `AnalUnit` has already been re-analyzed this update, and registered a dependency
+ // on this thing, but already has sufficiently up-to-date information. Nothing to do.
continue;
},
};
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), new_po_dep_count });
try zcu.outdated.putNoClobber(
zcu.gpa,
depender,
new_po_dep_count,
);
- log.debug("outdated: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated {} => new outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
if (new_po_dep_count == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
// If this is a Decl and was not previously PO, we must recursively
@@ -2342,16 +2344,16 @@ pub fn markDependeeOutdated(
}
pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
- log.debug("up-to-date dependee: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("up-to-date dependee: {}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
// This depender is already outdated, but it now has one
// less PO dependency!
po_dep_count.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
+ log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
continue;
@@ -2365,11 +2367,11 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
};
if (ptr.* > 1) {
ptr.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), ptr.* });
+ log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
continue;
}
- log.debug("up-to-date (po deps = 0): {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("up-to-date {} => {} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
// This dependency is no longer PO, i.e. is known to be up-to-date.
assert(zcu.potentially_outdated.swapRemove(depender));
@@ -2398,7 +2400,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
},
.func => |func_index| .{ .interned = func_index }, // IES
};
- log.debug("marking dependee po: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
var it = ip.dependencyIterator(dependee);
while (it.next()) |po| {
if (zcu.outdated.getPtr(po)) |po_dep_count| {
@@ -2408,17 +2410,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
_ = zcu.outdated_ready.swapRemove(po);
}
po_dep_count.* += 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), po_dep_count.* });
+ log.debug("po {} => {} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
continue;
}
if (zcu.potentially_outdated.getPtr(po)) |n| {
// There is now one more PO dependency.
n.* += 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), n.* });
+ log.debug("po {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
continue;
}
try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), 1 });
+ log.debug("po {} => {} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
// This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
try zcu.markTransitiveDependersPotentiallyOutdated(po);
}
@@ -2443,7 +2445,7 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
if (zcu.outdated_ready.count() > 0) {
const unit = zcu.outdated_ready.keys()[0];
- log.debug("findOutdatedToAnalyze: trivial {}", .{fmtAnalUnit(unit, zcu)});
+ log.debug("findOutdatedToAnalyze: trivial {}", .{zcu.fmtAnalUnit(unit)});
return unit;
}
@@ -2498,10 +2500,15 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
const nav = zcu.funcInfo(func).owner_nav;
std.io.getStdErr().writer().print("outdated: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
}
+ for (zcu.potentially_outdated.keys(), zcu.potentially_outdated.values()) |o, opod| {
+ const func = o.unwrap().func;
+ const nav = zcu.funcInfo(func).owner_nav;
+ std.io.getStdErr().writer().print("po: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
+ }
}
log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
- fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? }), zcu),
+ zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
chosen_cau_dependers,
});
@@ -2744,7 +2751,7 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
const gpa = zcu.gpa;
unit_refs: {
- const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;
+ const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs;
var idx = kv.value;
while (idx != std.math.maxInt(u32)) {
@@ -2758,7 +2765,7 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
}
type_refs: {
- const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse return;
+ const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse break :type_refs;
var idx = kv.value;
while (idx != std.math.maxInt(u32)) {
@@ -3280,7 +3287,7 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const unit = kv.key;
try result.putNoClobber(gpa, unit, kv.value);
- log.debug("handle unit '{}'", .{fmtAnalUnit(unit, zcu)});
+ log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
assert(first_ref_idx != std.math.maxInt(u32));
@@ -3289,8 +3296,8 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const ref = zcu.all_references.items[ref_idx];
if (!result.contains(ref.referenced)) {
log.debug("unit '{}': ref unit '{}'", .{
- fmtAnalUnit(unit, zcu),
- fmtAnalUnit(ref.referenced, zcu),
+ zcu.fmtAnalUnit(unit),
+ zcu.fmtAnalUnit(ref.referenced),
});
try unit_queue.put(gpa, ref.referenced, .{
.referencer = unit,
@@ -3307,7 +3314,7 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const ref = zcu.all_type_references.items[ref_idx];
if (!checked_types.contains(ref.referenced)) {
log.debug("unit '{}': ref type '{}'", .{
- fmtAnalUnit(unit, zcu),
+ zcu.fmtAnalUnit(unit),
Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
});
try type_queue.put(gpa, ref.referenced, .{
@@ -3389,10 +3396,10 @@ pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
return zcu.fileByIndex(file_index);
}
-fn fmtAnalUnit(unit: AnalUnit, zcu: *Zcu) std.fmt.Formatter(formatAnalUnit) {
+pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
return .{ .data = .{ .unit = unit, .zcu = zcu } };
}
-fn fmtDependee(d: InternPool.Dependee, zcu: *Zcu) std.fmt.Formatter(formatDependee) {
+pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Formatter(formatDependee) {
return .{ .data = .{ .dependee = d, .zcu = zcu } };
}