Commit 71baa5e769

mlugg <mlugg@mlugg.co.uk>
2025-06-12 14:53:41
compiler: improve progress output
Update the estimated total items for the codegen and link progress nodes earlier. Rather than waiting for the main thread to dispatch the tasks, we can add the item to the estimated total as soon as we queue the main task. The only difference is we need to complete it even in error cases.
1 parent 5bb5aaf
src/Sema/LowerZon.zig
@@ -195,6 +195,7 @@ fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!Inter
                     codegen_type: {
                         if (pt.zcu.comp.config.use_llvm) break :codegen_type;
                         if (self.block.ownerModule().strip) break :codegen_type;
+                        pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
                         try pt.zcu.comp.queueJob(.{ .link_type = wip.index });
                     }
                     break :ty wip.finish(ip, new_namespace_index);
src/Zcu/PerThread.zig
@@ -1321,6 +1321,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
         }
 
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_nav = nav_id });
     }
 
@@ -1717,6 +1718,8 @@ fn analyzeFuncBody(
     }
 
     // This job depends on any resolve_type_fully jobs queued up before it.
+    zcu.codegen_prog_node.increaseEstimatedTotalItems(1);
+    comp.link_prog_node.increaseEstimatedTotalItems(1);
     try comp.queueJob(.{ .codegen_func = .{
         .func = func_index,
         .air = air,
@@ -1799,6 +1802,7 @@ fn createFileRootStruct(
     codegen_type: {
         if (file.mod.?.strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     zcu.setFileRootType(file_index, wip_ty.index);
@@ -3827,6 +3831,7 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!
     const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);
     if (result.new_nav.unwrap()) |nav| {
         // This job depends on any resolve_type_fully jobs queued up before it.
+        pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try pt.zcu.comp.queueJob(.{ .link_nav = nav });
         if (pt.zcu.comp.debugIncremental()) try pt.zcu.incremental_debug_state.newNav(pt.zcu, nav);
     }
@@ -3974,6 +3979,7 @@ fn recreateStructType(
     codegen_type: {
         if (file.mod.?.strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
 
@@ -4066,6 +4072,7 @@ fn recreateUnionType(
     codegen_type: {
         if (file.mod.?.strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
 
src/Compilation.zig
@@ -848,6 +848,8 @@ const Job = union(enum) {
     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
     /// all types are resolved before the linker task is queued.
     /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately.
+    /// Before queueing this `Job`, increase the estimated total item count for both
+    /// `comp.zcu.?.codegen_prog_node` and `comp.link_prog_node`.
     codegen_func: struct {
         func: InternPool.Index,
         /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`.
@@ -857,12 +859,15 @@ const Job = union(enum) {
     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
     /// all types are resolved before the linker task is queued.
     /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
+    /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
     link_nav: InternPool.Nav.Index,
     /// Queue a `link.ZcuTask` to emit debug information for this container type.
     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
     /// all types are resolved before the linker task is queued.
     /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
+    /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
     link_type: InternPool.Index,
+    /// Before queueing this `Job`, increase the estimated total item count for `comp.link_prog_node`.
     update_line_number: InternPool.TrackedInst.Index,
     /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
     /// This may be its first time being analyzed, or it may be outdated.
@@ -4592,11 +4597,17 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
             const zcu = comp.zcu.?;
             const gpa = zcu.gpa;
             var air = func.air;
-            errdefer air.deinit(gpa);
+            errdefer {
+                zcu.codegen_prog_node.completeOne();
+                comp.link_prog_node.completeOne();
+                air.deinit(gpa);
+            }
             if (!air.typesFullyResolved(zcu)) {
                 // Type resolution failed in a way which affects this function. This is a transitive
                 // failure, but it doesn't need recording, because this function semantically depends
                 // on the failed type, so when it is changed the function is updated.
+                zcu.codegen_prog_node.completeOne();
+                comp.link_prog_node.completeOne();
                 air.deinit(gpa);
                 return;
             }
@@ -4606,7 +4617,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
                 .value = undefined,
             };
             assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended
-            zcu.codegen_prog_node.increaseEstimatedTotalItems(1);
             // This value is used as a heuristic to avoid queueing too much AIR/MIR at once (hence
             // using a lot of memory). If this would cause too many AIR bytes to be in-flight, we
             // will block on the `dispatchZcuLinkTask` call below.
@@ -4640,6 +4650,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
             if (nav.analysis != null) {
                 const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
                 if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
+                    comp.link_prog_node.completeOne();
                     return;
                 }
             }
@@ -4648,6 +4659,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
                 // Type resolution failed in a way which affects this `Nav`. This is a transitive
                 // failure, but it doesn't need recording, because this `Nav` semantically depends
                 // on the failed type, so when it is changed the `Nav` will be updated.
+                comp.link_prog_node.completeOne();
                 return;
             }
             comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index });
@@ -4659,6 +4671,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
                 // Type resolution failed in a way which affects this type. This is a transitive
                 // failure, but it doesn't need recording, because this type semantically depends
                 // on the failed type, so when that is changed, this type will be updated.
+                comp.link_prog_node.completeOne();
                 return;
             }
             comp.dispatchZcuLinkTask(tid, .{ .link_type = ty });
@@ -7460,7 +7473,6 @@ pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) vo
 /// The reason for the double-queue here is that the first queue ensures any
 /// resolve_type_fully tasks are complete before this dispatch function is called.
 fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void {
-    comp.link_prog_node.increaseEstimatedTotalItems(1);
     if (!comp.separateCodegenThreadOk()) {
         assert(tid == 0);
         if (task == .link_func) {
src/Sema.zig
@@ -2992,6 +2992,7 @@ fn zirStructDecl(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     try sema.declareDependency(.{ .interned = wip_ty.index });
@@ -3266,6 +3267,7 @@ fn zirEnumDecl(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     return Air.internedToRef(wip_ty.index);
@@ -3385,6 +3387,7 @@ fn zirUnionDecl(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     try sema.declareDependency(.{ .interned = wip_ty.index });
@@ -3473,6 +3476,7 @@ fn zirOpaqueDecl(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     try sema.addTypeReferenceEntry(src, wip_ty.index);
@@ -20105,6 +20109,7 @@ fn structInitAnon(
             codegen_type: {
                 if (zcu.comp.config.use_llvm) break :codegen_type;
                 if (block.ownerModule().strip) break :codegen_type;
+                zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
                 try zcu.comp.queueJob(.{ .link_type = wip.index });
             }
             if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
@@ -21417,6 +21422,7 @@ fn reifyEnum(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     return Air.internedToRef(wip_ty.index);
@@ -21671,6 +21677,7 @@ fn reifyUnion(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     try sema.declareDependency(.{ .interned = wip_ty.index });
@@ -22026,6 +22033,7 @@ fn reifyStruct(
         if (zcu.comp.config.use_llvm) break :codegen_type;
         if (block.ownerModule().strip) break :codegen_type;
         // This job depends on any resolve_type_fully jobs queued up before it.
+        zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
     }
     try sema.declareDependency(.{ .interned = wip_ty.index });