Commit a7c05c06be

Andrew Kelley <andrew@ziglang.org>
2022-04-17 13:06:51
stage2: expose progress bar API to linker backends
This gives us insight as to what is happening when we are waiting for things such as LLVM emit object and LLD linking.
1 parent 2aeaa1c
src/codegen/llvm.zig
@@ -469,7 +469,12 @@ pub const Object = struct {
         _ = builder.buildRet(is_lt);
     }
 
-    pub fn flushModule(self: *Object, comp: *Compilation) !void {
+    pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
+        var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
+        sub_prog_node.activate();
+        sub_prog_node.context.refresh();
+        defer sub_prog_node.end();
+
         try self.genErrorNameTable(comp);
         try self.genCmpLtErrorsLenFunction(comp);
 
src/link/C.zig
@@ -235,14 +235,18 @@ pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void
     _ = decl;
 }
 
-pub fn flush(self: *C, comp: *Compilation) !void {
-    return self.flushModule(comp);
+pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
+    return self.flushModule(comp, prog_node);
 }
 
-pub fn flushModule(self: *C, comp: *Compilation) !void {
+pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    var sub_prog_node = prog_node.start("Flush Module", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     const gpa = comp.gpa;
     const module = self.base.options.module.?;
 
src/link/Coff.zig
@@ -829,36 +829,40 @@ pub fn updateDeclExports(
     }
 }
 
-pub fn flush(self: *Coff, comp: *Compilation) !void {
+pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (self.base.options.emit == null) {
         if (build_options.have_llvm) {
             if (self.llvm_object) |llvm_object| {
-                return try llvm_object.flushModule(comp);
+                return try llvm_object.flushModule(comp, prog_node);
             }
         }
         return;
     }
     if (build_options.have_llvm and self.base.options.use_lld) {
-        return self.linkWithLLD(comp);
+        return self.linkWithLLD(comp, prog_node);
     } else {
         switch (self.base.options.effectiveOutputMode()) {
             .Exe, .Obj => {},
             .Lib => return error.TODOImplementWritingLibFiles,
         }
-        return self.flushModule(comp);
+        return self.flushModule(comp, prog_node);
     }
 }
 
-pub fn flushModule(self: *Coff, comp: *Compilation) !void {
+pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object| {
-            return try llvm_object.flushModule(comp);
+            return try llvm_object.flushModule(comp, prog_node);
         }
     }
 
+    var sub_prog_node = prog_node.start("COFF Flush", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     if (self.text_section_size_dirty) {
         // Write the new raw size in the .text header
         var buf: [4]u8 = undefined;
@@ -892,7 +896,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
     }
 }
 
-fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
+fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -924,7 +928,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
             }
         }
 
-        try self.flushModule(comp);
+        try self.flushModule(comp, prog_node);
 
         if (fs.path.dirname(full_out_path)) |dirname| {
             break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@@ -933,6 +937,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
         }
     } else null;
 
+    var sub_prog_node = prog_node.start("LLD Link", 0);
+    sub_prog_node.activate();
+    sub_prog_node.context.refresh();
+    defer sub_prog_node.end();
+
     const is_lib = self.base.options.output_mode == .Lib;
     const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
     const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
src/link/Elf.zig
@@ -929,35 +929,39 @@ pub fn populateMissingMetadata(self: *Elf) !void {
     }
 }
 
-pub fn flush(self: *Elf, comp: *Compilation) !void {
+pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (self.base.options.emit == null) {
         if (build_options.have_llvm) {
             if (self.llvm_object) |llvm_object| {
-                return try llvm_object.flushModule(comp);
+                return try llvm_object.flushModule(comp, prog_node);
             }
         }
         return;
     }
     const use_lld = build_options.have_llvm and self.base.options.use_lld;
     if (use_lld) {
-        return self.linkWithLLD(comp);
+        return self.linkWithLLD(comp, prog_node);
     }
     switch (self.base.options.output_mode) {
-        .Exe, .Obj => return self.flushModule(comp),
+        .Exe, .Obj => return self.flushModule(comp, prog_node),
         .Lib => return error.TODOImplementWritingLibFiles,
     }
 }
 
-pub fn flushModule(self: *Elf, comp: *Compilation) !void {
+pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object| {
-            return try llvm_object.flushModule(comp);
+            return try llvm_object.flushModule(comp, prog_node);
         }
     }
 
+    var sub_prog_node = prog_node.start("ELF Flush", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     // TODO This linker code currently assumes there is only 1 compilation unit and it
     // corresponds to the Zig source code.
     const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
@@ -1204,7 +1208,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
     assert(!self.debug_strtab_dirty);
 }
 
-fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
+fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1236,7 +1240,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
             }
         }
 
-        try self.flushModule(comp);
+        try self.flushModule(comp, prog_node);
 
         if (fs.path.dirname(full_out_path)) |dirname| {
             break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@@ -1245,6 +1249,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
         }
     } else null;
 
+    var sub_prog_node = prog_node.start("LLD Link", 0);
+    sub_prog_node.activate();
+    sub_prog_node.context.refresh();
+    defer sub_prog_node.end();
+
     const is_obj = self.base.options.output_mode == .Obj;
     const is_lib = self.base.options.output_mode == .Lib;
     const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
src/link/MachO.zig
@@ -419,33 +419,34 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
     return self;
 }
 
-pub fn flush(self: *MachO, comp: *Compilation) !void {
+pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (self.base.options.emit == null) {
         if (build_options.have_llvm) {
             if (self.llvm_object) |llvm_object| {
-                try llvm_object.flushModule(comp);
+                try llvm_object.flushModule(comp, prog_node);
             }
         }
         return;
     }
+
     if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
         if (build_options.have_llvm) {
-            return self.base.linkAsArchive(comp);
+            return self.base.linkAsArchive(comp, prog_node);
         } else {
             log.err("TODO: non-LLVM archiver for MachO object files", .{});
             return error.TODOImplementWritingStaticLibFiles;
         }
     }
-    try self.flushModule(comp);
+    try self.flushModule(comp, prog_node);
 }
 
-pub fn flushModule(self: *MachO, comp: *Compilation) !void {
+pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
     if (!use_stage1 and self.base.options.output_mode == .Obj)
-        return self.flushObject(comp);
+        return self.flushObject(comp, prog_node);
 
     var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
     defer arena_allocator.deinit();
@@ -482,7 +483,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
 
         const obj_basename = self.base.intermediary_basename orelse break :blk null;
 
-        try self.flushObject(comp);
+        try self.flushObject(comp, prog_node);
 
         if (fs.path.dirname(full_out_path)) |dirname| {
             break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
@@ -491,6 +492,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
         }
     } else null;
 
+    var sub_prog_node = prog_node.start("MachO Flush", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     const is_lib = self.base.options.output_mode == .Lib;
     const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
     const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
@@ -1111,13 +1116,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
     self.cold_start = false;
 }
 
-pub fn flushObject(self: *MachO, comp: *Compilation) !void {
+pub fn flushObject(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (build_options.have_llvm)
         if (self.llvm_object) |llvm_object|
-            return llvm_object.flushModule(comp);
+            return llvm_object.flushModule(comp, prog_node);
 
     return error.TODOImplementWritingObjFiles;
 }
src/link/NvPtx.zig
@@ -97,11 +97,11 @@ pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void {
     return self.llvm_object.freeDecl(decl);
 }
 
-pub fn flush(self: *NvPtx, comp: *Compilation) !void {
-    return self.flushModule(comp);
+pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void {
+    return self.flushModule(comp, prog_node);
 }
 
-pub fn flushModule(self: *NvPtx, comp: *Compilation) !void {
+pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (!build_options.have_llvm) return;
     if (build_options.skip_non_native) {
         @panic("Attempted to compile for architecture that was disabled by build configuration");
@@ -117,5 +117,5 @@ pub fn flushModule(self: *NvPtx, comp: *Compilation) !void {
         };
         hack_comp.bin_file.options.emit = null;
     }
-    return try self.llvm_object.flushModule(hack_comp);
+    return try self.llvm_object.flushModule(hack_comp, prog_node);
 }
src/link/Plan9.zig
@@ -351,7 +351,7 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
     }
 }
 
-pub fn flush(self: *Plan9, comp: *Compilation) !void {
+pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     assert(!self.base.options.use_lld);
 
     switch (self.base.options.effectiveOutputMode()) {
@@ -360,7 +360,7 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
         .Obj => return error.TODOImplementPlan9Objs,
         .Lib => return error.TODOImplementWritingLibFiles,
     }
-    return self.flushModule(comp);
+    return self.flushModule(comp, prog_node);
 }
 
 pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
@@ -387,7 +387,7 @@ fn declCount(self: *Plan9) usize {
     return self.data_decl_table.count() + fn_decl_count;
 }
 
-pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
+pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (build_options.skip_non_native and builtin.object_format != .plan9) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
@@ -396,6 +396,10 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    var sub_prog_node = prog_node.start("Flush Module", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     log.debug("flushModule", .{});
 
     defer assert(self.hdr.entry != 0x0);
src/link/SpirV.zig
@@ -174,15 +174,15 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
     self.decl_table.swapRemoveAt(index);
 }
 
-pub fn flush(self: *SpirV, comp: *Compilation) !void {
+pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (build_options.have_llvm and self.base.options.use_lld) {
         return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
     } else {
-        return self.flushModule(comp);
+        return self.flushModule(comp, prog_node);
     }
 }
 
-pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
+pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (build_options.skip_non_native) {
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
@@ -190,6 +190,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    var sub_prog_node = prog_node.start("Flush Module", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     const module = self.base.options.module.?;
     const target = comp.getTarget();
 
src/link/Wasm.zig
@@ -1477,32 +1477,36 @@ fn resetState(self: *Wasm) void {
     self.code_section_index = null;
 }
 
-pub fn flush(self: *Wasm, comp: *Compilation) !void {
+pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     if (self.base.options.emit == null) {
         if (build_options.have_llvm) {
             if (self.llvm_object) |llvm_object| {
-                return try llvm_object.flushModule(comp);
+                return try llvm_object.flushModule(comp, prog_node);
             }
         }
         return;
     }
     if (build_options.have_llvm and self.base.options.use_lld) {
-        return self.linkWithLLD(comp);
+        return self.linkWithLLD(comp, prog_node);
     } else {
-        return self.flushModule(comp);
+        return self.flushModule(comp, prog_node);
     }
 }
 
-pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
+pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object| {
-            return try llvm_object.flushModule(comp);
+            return try llvm_object.flushModule(comp, prog_node);
         }
     }
 
+    var sub_prog_node = prog_node.start("WASM Flush", 0);
+    sub_prog_node.activate();
+    defer sub_prog_node.end();
+
     // ensure the error names table is populated when an error name is referenced
     try self.populateErrorNameTable();
 
@@ -2028,7 +2032,7 @@ fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void {
     }
 }
 
-fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
+fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -2060,7 +2064,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
             }
         }
 
-        try self.flushModule(comp);
+        try self.flushModule(comp, prog_node);
 
         if (fs.path.dirname(full_out_path)) |dirname| {
             break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@@ -2069,6 +2073,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
         }
     } else null;
 
+    var sub_prog_node = prog_node.start("LLD Link", 0);
+    sub_prog_node.activate();
+    sub_prog_node.context.refresh();
+    defer sub_prog_node.end();
+
     const is_obj = self.base.options.output_mode == .Obj;
 
     const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj)
src/Compilation.zig
@@ -2084,7 +2084,13 @@ pub fn update(comp: *Compilation) !void {
         }
     }
 
-    try comp.performAllTheWork();
+    // If the terminal is dumb, we dont want to show the user all the output.
+    var progress: std.Progress = .{ .dont_print_on_dumb = true };
+    const main_progress_node = progress.start("", 0);
+    defer main_progress_node.end();
+    if (comp.color == .off) progress.terminal = null;
+
+    try comp.performAllTheWork(main_progress_node);
 
     if (!use_stage1) {
         if (comp.bin_file.options.module) |module| {
@@ -2158,9 +2164,9 @@ pub fn update(comp: *Compilation) !void {
                 .path = dir_path,
             };
 
-            try comp.flush();
+            try comp.flush(main_progress_node);
         } else {
-            try comp.flush();
+            try comp.flush(main_progress_node);
         }
 
         // Failure here only means an unnecessary cache miss.
@@ -2171,7 +2177,7 @@ pub fn update(comp: *Compilation) !void {
         assert(comp.bin_file.lock == null);
         comp.bin_file.lock = man.toOwnedLock();
     } else {
-        try comp.flush();
+        try comp.flush(main_progress_node);
     }
 
     // Unload all source files to save memory.
@@ -2188,8 +2194,8 @@ pub fn update(comp: *Compilation) !void {
     }
 }
 
-fn flush(comp: *Compilation) !void {
-    try comp.bin_file.flush(comp); // This is needed before reading the error flags.
+fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void {
+    try comp.bin_file.flush(comp, prog_node); // This is needed before reading the error flags.
     comp.link_error_flags = comp.bin_file.errorFlags();
 
     const use_stage1 = build_options.omit_stage2 or
@@ -2590,14 +2596,10 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
     return module.compile_log_text.items;
 }
 
-pub fn performAllTheWork(comp: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
-    // If the terminal is dumb, we dont want to show the user all the
-    // output.
-    var progress: std.Progress = .{ .dont_print_on_dumb = true };
-    var main_progress_node = progress.start("", 0);
-    defer main_progress_node.end();
-    if (comp.color == .off) progress.terminal = null;
-
+pub fn performAllTheWork(
+    comp: *Compilation,
+    main_progress_node: *std.Progress.Node,
+) error{ TimerUnsupported, OutOfMemory }!void {
     // Here we queue up all the AstGen tasks first, followed by C object compilation.
     // We wait until the AstGen tasks are all completed before proceeding to the
     // (at least for now) single-threaded main work queue. However, C object compilation
src/link.zig
@@ -573,7 +573,7 @@ pub const File = struct {
 
     /// Commit pending changes and write headers. Takes into account final output mode
     /// and `use_lld`, not only `effectiveOutputMode`.
-    pub fn flush(base: *File, comp: *Compilation) !void {
+    pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
         if (comp.clang_preprocessor_mode == .yes) {
             const emit = base.options.emit orelse return; // -fno-emit-bin
             // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
@@ -591,32 +591,32 @@ pub const File = struct {
 
         const use_lld = build_options.have_llvm and base.options.use_lld;
         if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) {
-            return base.linkAsArchive(comp);
+            return base.linkAsArchive(comp, prog_node);
         }
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).flush(comp),
-            .elf => return @fieldParentPtr(Elf, "base", base).flush(comp),
-            .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
-            .c => return @fieldParentPtr(C, "base", base).flush(comp),
-            .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
-            .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp),
-            .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp),
+            .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node),
+            .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node),
+            .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node),
+            .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node),
+            .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node),
+            .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node),
         }
     }
 
     /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
     /// rather than final output mode.
-    pub fn flushModule(base: *File, comp: *Compilation) !void {
+    pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp),
-            .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp),
-            .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
-            .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
-            .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
-            .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp),
-            .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp),
+            .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node),
+            .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node),
+            .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node),
+            .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node),
+            .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node),
+            .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node),
         }
     }
 
@@ -754,7 +754,7 @@ pub const File = struct {
         }
     }
 
-    pub fn linkAsArchive(base: *File, comp: *Compilation) !void {
+    pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
         const tracy = trace(@src());
         defer tracy.end();
 
@@ -787,9 +787,9 @@ pub const File = struct {
                 }
             }
             if (base.options.object_format == .macho) {
-                try base.cast(MachO).?.flushObject(comp);
+                try base.cast(MachO).?.flushObject(comp, prog_node);
             } else {
-                try base.flushModule(comp);
+                try base.flushModule(comp, prog_node);
             }
             break :blk try fs.path.join(arena, &.{
                 fs.path.dirname(full_out_path_z).?, base.intermediary_basename.?,