authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-27 11:42:31+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-29 10:32:09-07:00
log1ecbc16172649c5f11c2c5173a42f04a3c7e5d2b
treebefb3f8c6a8a7a038d791049d468c004f1b4ee2c
parentebb8683ef1298e5d45f8d9912cf161954cc7ca99
signature Commit is signed but in an unrecognized format.

link: delete a few unnecessary `tid` parameters


8 files changed, 98 insertions(+), 104 deletions(-)

src/Compilation.zig+1-1
...@@ -4427,7 +4427,7 @@ fn performAllTheWork(...@@ -4427,7 +4427,7 @@ fn performAllTheWork(
4427 comp.windows_libs_num_done = @intCast(comp.windows_libs.count());4427 comp.windows_libs_num_done = @intCast(comp.windows_libs.count());
4428 try misc_group.await(io);4428 try misc_group.await(io);
44294429
4430 comp.link_queue.wait(io);4430 try comp.link_queue.wait(io);
4431}4431}
44324432
4433fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void {4433fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void {
src/Zcu/PerThread.zig+9-8
...@@ -138,6 +138,15 @@ pub fn update(...@@ -138,6 +138,15 @@ pub fn update(
138 const gpa = comp.gpa;138 const gpa = comp.gpa;
139 const io = comp.io;139 const io = comp.io;
140140
141 if (!zcu.backendSupportsFeature(.separate_thread)) {
142 // We may still be running the linker on a separate thread at the moment, but once prelink
143 // completes, we need that thread to exit so that we can process all ZCU link tasks on the
144 // main thread. We signal this condition by immediately closing the ZCU task queue. It's
145 // important that we do this before enqueueing *any* ZCU link tasks: otherwise those tasks
146 // will run on the linker thread which is meant to be for prelink only!
147 comp.link_queue.finishZcuQueue(comp);
148 }
149
141 {150 {
142 const tracy_trace = traceNamed(@src(), "astgen");151 const tracy_trace = traceNamed(@src(), "astgen");
143 defer tracy_trace.end();152 defer tracy_trace.end();
...@@ -269,14 +278,6 @@ pub fn update(...@@ -269,14 +278,6 @@ pub fn update(
269278
270 try zcu.flushRetryableFailures();279 try zcu.flushRetryableFailures();
271280
272 if (!zcu.backendSupportsFeature(.separate_thread)) {
273 // Close the ZCU task queue. Prelink may still be running, but the closed
274 // queue will cause the linker task to exit once prelink finishes. The
275 // closed queue also communicates to `enqueueZcu` that it should wait for
276 // the linker task to finish and then run ZCU tasks serially.
277 comp.link_queue.finishZcuQueue(comp);
278 }
279
280 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);281 zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
281 if (comp.bin_file != null) {282 if (comp.bin_file != null) {
282 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);283 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
src/crash_report.zig+7-8
...@@ -86,21 +86,20 @@ pub const CodegenFunc = if (enabled) struct {...@@ -86,21 +86,20 @@ pub const CodegenFunc = if (enabled) struct {
8686
87pub const LinkerOp = if (enabled) struct {87pub const LinkerOp = if (enabled) struct {
88 lf: *link.File,88 lf: *link.File,
89 tid: Zcu.PerThread.Id,
90 threadlocal var current: ?LinkerOp = null;89 threadlocal var current: ?LinkerOp = null;
91 pub fn start(lf: *link.File, tid: Zcu.PerThread.Id) void {90 pub fn start(lf: *link.File) void {
92 std.debug.assert(current == null);91 std.debug.assert(current == null);
93 current = .{ .lf = lf, .tid = tid };92 current = .{ .lf = lf };
94 }93 }
95 pub fn stop(lf: *link.File, tid: Zcu.PerThread.Id) void {94 pub fn stop(lf: *link.File) void {
96 std.debug.assert(current.?.lf == lf and current.?.tid == tid);95 std.debug.assert(current.?.lf == lf);
97 current = null;96 current = null;
98 }97 }
99} else struct {98} else struct {
100 const current: ?noreturn = null;99 const current: ?noreturn = null;
101 // Dummy implementation100 // Dummy implementation
102 pub fn start(_: *link.File, _: Zcu.PerThread.Id) void {}101 pub fn start(_: *link.File) void {}
103 pub fn stop(_: *link.File, _: Zcu.PerThread.Id) void {}102 pub fn stop(_: *link.File) void {}
104};103};
105104
106fn dumpCrashContext() Io.Writer.Error!void {105fn dumpCrashContext() Io.Writer.Error!void {
...@@ -132,7 +131,7 @@ fn dumpCrashContext() Io.Writer.Error!void {...@@ -132,7 +131,7 @@ fn dumpCrashContext() Io.Writer.Error!void {
132 try dumpCrashContextSema(anal, w, &S.crash_heap);131 try dumpCrashContextSema(anal, w, &S.crash_heap);
133 } else if (LinkerOp.current) |linker_op| {132 } else if (LinkerOp.current) |linker_op| {
134 try w.writeAll("Linker snapshot:\n");133 try w.writeAll("Linker snapshot:\n");
135 switch (try linker_op.lf.dump(w, linker_op.tid)) {134 switch (try linker_op.lf.dump(w)) {
136 .unimplemented => try w.writeAll("(backend does not support link snapshots)"),135 .unimplemented => try w.writeAll("(backend does not support link snapshots)"),
137 .needs_extensions => try w.writeAll("(build with -Ddebug-extensions to dump linker state)"),136 .needs_extensions => try w.writeAll("(build with -Ddebug-extensions to dump linker state)"),
138 .disabled => try w.writeAll("(run with --debug-link-snapshot to dump linker state)"),137 .disabled => try w.writeAll("(run with --debug-link-snapshot to dump linker state)"),
src/link.zig+10-10
...@@ -950,12 +950,12 @@ pub const File = struct {...@@ -950,12 +950,12 @@ pub const File = struct {
950 }950 }
951 }951 }
952952
953 pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool {953 fn idle(base: *File) Error!bool {
954 switch (base.tag) {954 switch (base.tag) {
955 else => return false,955 else => return false,
956 inline .elf2, .coff, .macho2 => |tag| {956 inline .elf2, .coff, .macho2 => |tag| {
957 dev.check(tag.devFeature());957 dev.check(tag.devFeature());
958 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);958 return @as(*tag.Type(), @fieldParentPtr("base", base)).idle();
959 },959 },
960 }960 }
961 }961 }
...@@ -974,8 +974,8 @@ pub const File = struct {...@@ -974,8 +974,8 @@ pub const File = struct {
974 /// Commit pending changes and write headers. Takes into account final output mode.974 /// Commit pending changes and write headers. Takes into account final output mode.
975 /// `arena` has the lifetime of the call to `Compilation.update`.975 /// `arena` has the lifetime of the call to `Compilation.update`.
976 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void {976 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void {
977 crash_report.LinkerOp.start(base, tid);977 crash_report.LinkerOp.start(base);
978 defer crash_report.LinkerOp.stop(base, tid);978 defer crash_report.LinkerOp.stop(base);
979979
980 const comp = base.comp;980 const comp = base.comp;
981 const io = comp.io;981 const io = comp.io;
...@@ -1026,8 +1026,8 @@ pub const File = struct {...@@ -1026,8 +1026,8 @@ pub const File = struct {
1026 ) Error!void {1026 ) Error!void {
1027 assert(pt.zcu.llvm_object == null);1027 assert(pt.zcu.llvm_object == null);
10281028
1029 crash_report.LinkerOp.start(base, pt.tid);1029 crash_report.LinkerOp.start(base);
1030 defer crash_report.LinkerOp.stop(base, pt.tid);1030 defer crash_report.LinkerOp.stop(base);
10311031
1032 switch (base.tag) {1032 switch (base.tag) {
1033 .lld => unreachable,1033 .lld => unreachable,
...@@ -1115,7 +1115,7 @@ pub const File = struct {...@@ -1115,7 +1115,7 @@ pub const File = struct {
1115 enabled,1115 enabled,
1116 };1116 };
11171117
1118 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !DumpResult {1118 pub fn dump(base: *File, w: *Io.Writer) !DumpResult {
1119 if (!build_options.enable_debug_extensions) return .not_built;1119 if (!build_options.enable_debug_extensions) return .not_built;
1120 switch (base.tag) {1120 switch (base.tag) {
1121 .elf,1121 .elf,
...@@ -1129,7 +1129,7 @@ pub const File = struct {...@@ -1129,7 +1129,7 @@ pub const File = struct {
1129 => return .unimplemented,1129 => return .unimplemented,
1130 inline else => |tag| {1130 inline else => |tag| {
1131 dev.check(tag.devFeature());1131 dev.check(tag.devFeature());
1132 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);1132 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w);
1133 },1133 },
1134 }1134 }
1135 }1135 }
...@@ -1910,8 +1910,8 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void...@@ -1910,8 +1910,8 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void
1910 }1910 }
1911 }1911 }
1912}1912}
1913pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) Error!bool {1913pub fn doIdleTask(comp: *Compilation) Error!bool {
1914 return if (comp.bin_file) |lf| lf.idle(tid) else false;1914 return if (comp.bin_file) |lf| lf.idle() else false;
1915}1915}
1916/// After the main pipeline is done, but before flush, the compilation may need to link one final1916/// After the main pipeline is done, but before flush, the compilation may need to link one final
1917/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running1917/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
src/link/Coff.zig+12-13
...@@ -5916,13 +5916,14 @@ pub fn flush(...@@ -5916,13 +5916,14 @@ pub fn flush(
5916 prog_node: std.Progress.Node,5916 prog_node: std.Progress.Node,
5917) link.Error!void {5917) link.Error!void {
5918 _ = arena;5918 _ = arena;
5919 _ = tid;
5919 const sub_prog_node = prog_node.start("COFF Flush", 0);5920 const sub_prog_node = prog_node.start("COFF Flush", 0);
5920 defer sub_prog_node.end();5921 defer sub_prog_node.end();
59215922
5922 const comp = coff.base.comp;5923 const comp = coff.base.comp;
59235924
5924 while (try coff.resolve()) {}5925 while (try coff.resolve()) {}
5925 while (try coff.idle(tid)) {}5926 while (try coff.idle()) {}
59265927
5927 // This has to occur after all other flushMoved / flushResized have resolved,5928 // This has to occur after all other flushMoved / flushResized have resolved,
5928 // but it will also generate one more set of resizes and moves.5929 // but it will also generate one more set of resizes and moves.
...@@ -5942,7 +5943,7 @@ pub fn flush(...@@ -5942,7 +5943,7 @@ pub fn flush(
5942 ),5943 ),
5943 };5944 };
5944 }5945 }
5945 while (try coff.idle(tid)) {}5946 while (try coff.idle()) {}
59465947
5947 if (coff.isImage())5948 if (coff.isImage())
5948 try coff.reportUndefs();5949 try coff.reportUndefs();
...@@ -5957,7 +5958,7 @@ pub fn flush(...@@ -5957,7 +5958,7 @@ pub fn flush(
5957 };5958 };
59585959
5959 if (coff.options.enable_link_snapshots)5960 if (coff.options.enable_link_snapshots)
5960 coff.dumpStderr(tid) catch |err|5961 coff.dumpStderr() catch |err|
5961 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});5962 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
5962}5963}
59635964
...@@ -6069,8 +6070,7 @@ fn resolve(coff: *Coff) !bool {...@@ -6069,8 +6070,7 @@ fn resolve(coff: *Coff) !bool {
6069 return false;6070 return false;
6070}6071}
60716072
6072pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {6073pub fn idle(coff: *Coff) !bool {
6073 _ = tid;
6074 // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible.6074 // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible.
6075 coff.mf.nodes_lock.lock();6075 coff.mf.nodes_lock.lock();
6076 defer coff.mf.nodes_lock.unlock();6076 defer coff.mf.nodes_lock.unlock();
...@@ -7457,7 +7457,7 @@ fn updateExportInner(...@@ -7457,7 +7457,7 @@ fn updateExportInner(
74577457
7458 try coff.genPending(pt);7458 try coff.genPending(pt);
7459 while (try coff.resolve()) {}7459 while (try coff.resolve()) {}
7460 while (try coff.idle(pt.tid)) {}7460 while (try coff.idle()) {}
74617461
7462 const machine = coff.targetLoad(&coff.headerPtr().machine);7462 const machine = coff.targetLoad(&coff.headerPtr().machine);
7463 const exported_ni = exported_si.node(coff);7463 const exported_ni = exported_si.node(coff);
...@@ -7595,21 +7595,21 @@ fn updateExportInner(...@@ -7595,21 +7595,21 @@ fn updateExportInner(
7595 }7595 }
7596}7596}
75977597
7598fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void {7598fn dumpStderr(coff: *Coff) Io.File.Writer.Error!void {
7599 const comp = coff.base.comp;7599 const comp = coff.base.comp;
7600 const io = comp.io;7600 const io = comp.io;
7601 var buffer: [512]u8 = undefined;7601 var buffer: [512]u8 = undefined;
7602 const stderr = try io.lockStderr(&buffer, null);7602 const stderr = try io.lockStderr(&buffer, null);
7603 defer io.unlockStderr();7603 defer io.unlockStderr();
7604 const w = &stderr.file_writer.interface;7604 const w = &stderr.file_writer.interface;
7605 _ = coff.dump(w, tid) catch |err| switch (err) {7605 _ = coff.dump(w) catch |err| switch (err) {
7606 error.WriteFailed => return stderr.file_writer.err.?,7606 error.WriteFailed => return stderr.file_writer.err.?,
7607 };7607 };
7608}7608}
76097609
7610pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult {7610pub fn dump(coff: *Coff, w: *Io.Writer) Io.Writer.Error!link.File.DumpResult {
7611 if (coff.options.enable_link_snapshots) {7611 if (coff.options.enable_link_snapshots) {
7612 try coff.printNode(tid, w, .root, 0);7612 try coff.printNode(w, .root, 0);
7613 try w.writeAll("Section table:\n");7613 try w.writeAll("Section table:\n");
7614 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|7614 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|
7615 try coff.printSection(w, name, sec.si);7615 try coff.printSection(w, name, sec.si);
...@@ -7762,9 +7762,8 @@ fn printNodeName(...@@ -7762,9 +7762,8 @@ fn printNodeName(
7762 }7762 }
7763}7763}
77647764
7765pub fn printNode(7765fn printNode(
7766 coff: *Coff,7766 coff: *Coff,
7767 tid: Zcu.PerThread.Id,
7768 w: *Io.Writer,7767 w: *Io.Writer,
7769 ni: MappedFile.Node.Index,7768 ni: MappedFile.Node.Index,
7770 indent: usize,7769 indent: usize,
...@@ -7792,7 +7791,7 @@ pub fn printNode(...@@ -7792,7 +7791,7 @@ pub fn printNode(
7792 // non-leaf, just print children7791 // non-leaf, just print children
7793 var child_ni = first_ni;7792 var child_ni = first_ni;
7794 while (true) {7793 while (true) {
7795 try coff.printNode(tid, w, child_ni, indent + 1);7794 try coff.printNode(w, child_ni, indent + 1);
7796 child_ni = child_ni.next(&coff.mf).unwrap() orelse break;7795 child_ni = child_ni.next(&coff.mf).unwrap() orelse break;
7797 }7796 }
7798 return;7797 return;
src/link/Elf2.zig+17-23
...@@ -9726,8 +9726,9 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)...@@ -9726,8 +9726,9 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
9726 };9726 };
9727}9727}
9728fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {9728fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
9729 const zcu = pt.zcu;9729 const comp = elf.base.comp;
9730 const gpa = zcu.gpa;9730 const gpa = comp.gpa;
9731 const zcu = comp.zcu.?;
9731 const ip = &zcu.intern_pool;9732 const ip = &zcu.intern_pool;
97329733
9733 const nav = ip.getNav(nav_index);9734 const nav = ip.getNav(nav_index);
...@@ -9823,7 +9824,7 @@ pub fn updateContainerType(...@@ -9823,7 +9824,7 @@ pub fn updateContainerType(
9823 ),9824 ),
9824 };9825 };
9825}9826}
9826pub fn updateContainerTypeInner(9827fn updateContainerTypeInner(
9827 elf: *Elf,9828 elf: *Elf,
9828 pt: Zcu.PerThread,9829 pt: Zcu.PerThread,
9829 ty: InternPool.Index,9830 ty: InternPool.Index,
...@@ -10227,7 +10228,8 @@ pub fn flush(...@@ -10227,7 +10228,8 @@ pub fn flush(
10227 tid: Zcu.PerThread.Id,10228 tid: Zcu.PerThread.Id,
10228 prog_node: std.Progress.Node,10229 prog_node: std.Progress.Node,
10229) link.Error!void {10230) link.Error!void {
10230 elf.flushInner(arena, tid, prog_node) catch |err| switch (err) {10231 _ = tid;
10232 elf.flushInner(arena, prog_node) catch |err| switch (err) {
10231 else => |e| return e,10233 else => |e| return e,
10232 error.MappedFileIo => return elf.base.comp.link_diags.fail(10234 error.MappedFileIo => return elf.base.comp.link_diags.fail(
10233 "failed to write output file: {t}",10235 "failed to write output file: {t}",
...@@ -10238,7 +10240,6 @@ pub fn flush(...@@ -10238,7 +10240,6 @@ pub fn flush(
10238fn flushInner(10240fn flushInner(
10239 elf: *Elf,10241 elf: *Elf,
10240 arena: std.mem.Allocator,10242 arena: std.mem.Allocator,
10241 tid: Zcu.PerThread.Id,
10242 prog_node: std.Progress.Node,10243 prog_node: std.Progress.Node,
10243) Error!void {10244) Error!void {
10244 const comp = elf.base.comp;10245 const comp = elf.base.comp;
...@@ -10266,7 +10267,7 @@ fn flushInner(...@@ -10266,7 +10267,7 @@ fn flushInner(
1026610267
10267 try elf.prepareDynamic();10268 try elf.prepareDynamic();
1026810269
10269 while (try elf.idle(tid)) {}10270 while (try elf.idle()) {}
1027010271
10271 assert(elf.input_pending_index == elf.inputs.items.len);10272 assert(elf.input_pending_index == elf.inputs.items.len);
10272 assert(elf.input_section_pending_index == elf.input_sections.items.len);10273 assert(elf.input_section_pending_index == elf.input_sections.items.len);
...@@ -10323,13 +10324,11 @@ fn flushInner(...@@ -10323,13 +10324,11 @@ fn flushInner(
10323 try elf.mf.flush();10324 try elf.mf.flush();
1032410325
10325 if (elf.options.enable_link_snapshots)10326 if (elf.options.enable_link_snapshots)
10326 elf.dumpStderr(tid) catch |err|10327 elf.dumpStderr() catch |err|
10327 return diags.fail("dumping link snapshot failed: {t}", .{err});10328 return diags.fail("dumping link snapshot failed: {t}", .{err});
10328}10329}
1032910330
10330pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {10331pub fn idle(elf: *Elf) link.Error!bool {
10331 _ = tid;
10332
10333 // This function is called non-deterministically, and so must not affect the layout of any nodes.10332 // This function is called non-deterministically, and so must not affect the layout of any nodes.
10334 elf.mf.nodes_lock.lock();10333 elf.mf.nodes_lock.lock();
10335 defer elf.mf.nodes_lock.unlock();10334 defer elf.mf.nodes_lock.unlock();
...@@ -12126,19 +12125,15 @@ pub fn updateExports(...@@ -12126,19 +12125,15 @@ pub fn updateExports(
12126 export_indices: []const Zcu.Export.Index,12125 export_indices: []const Zcu.Export.Index,
12127) link.Error!void {12126) link.Error!void {
12128 for (export_indices) |export_index| {12127 for (export_indices) |export_index| {
12129 elf.updateExportInner(pt, export_index) catch |err| switch (err) {12128 elf.updateExportInner(export_index) catch |err| switch (err) {
12130 else => |e| return e,12129 else => |e| return e,
12131 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),12130 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
12132 };12131 };
12133 }12132 }
12134 try elf.genPending(pt);12133 try elf.genPending(pt);
12135}12134}
12136fn updateExportInner(12135fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void {
12137 elf: *Elf,12136 const zcu = elf.base.comp.zcu.?;
12138 pt: Zcu.PerThread,
12139 export_index: Zcu.Export.Index,
12140) Error!void {
12141 const zcu = pt.zcu;
12142 const ip = &zcu.intern_pool;12137 const ip = &zcu.intern_pool;
1214312138
12144 const @"export" = export_index.ptr(zcu);12139 const @"export" = export_index.ptr(zcu);
...@@ -12213,21 +12208,21 @@ fn updateExportInner(...@@ -12213,21 +12208,21 @@ fn updateExportInner(
12213 };12208 };
12214}12209}
1221512210
12216fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void {12211fn dumpStderr(elf: *Elf) Io.File.Writer.Error!void {
12217 const comp = elf.base.comp;12212 const comp = elf.base.comp;
12218 const io = comp.io;12213 const io = comp.io;
12219 var buffer: [512]u8 = undefined;12214 var buffer: [512]u8 = undefined;
12220 const stderr = try io.lockStderr(&buffer, null);12215 const stderr = try io.lockStderr(&buffer, null);
12221 defer io.unlockStderr();12216 defer io.unlockStderr();
12222 const w = &stderr.file_writer.interface;12217 const w = &stderr.file_writer.interface;
12223 _ = elf.dump(w, tid) catch |err| switch (err) {12218 _ = elf.dump(w) catch |err| switch (err) {
12224 error.WriteFailed => return stderr.file_writer.err.?,12219 error.WriteFailed => return stderr.file_writer.err.?,
12225 };12220 };
12226}12221}
1222712222
12228pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult {12223pub fn dump(elf: *Elf, w: *Io.Writer) Io.Writer.Error!link.File.DumpResult {
12229 if (elf.options.enable_link_snapshots) {12224 if (elf.options.enable_link_snapshots) {
12230 try elf.printNode(tid, w, .root, 0);12225 try elf.printNode(w, .root, 0);
12231 return .enabled;12226 return .enabled;
12232 }12227 }
12233 return .disabled;12228 return .disabled;
...@@ -12235,7 +12230,6 @@ pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!lin...@@ -12235,7 +12230,6 @@ pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!lin
1223512230
12236pub fn printNode(12231pub fn printNode(
12237 elf: *Elf,12232 elf: *Elf,
12238 tid: Zcu.PerThread.Id,
12239 w: *Io.Writer,12233 w: *Io.Writer,
12240 ni: MappedFile.Node.Index,12234 ni: MappedFile.Node.Index,
12241 indent: usize,12235 indent: usize,
...@@ -12366,7 +12360,7 @@ pub fn printNode(...@@ -12366,7 +12360,7 @@ pub fn printNode(
12366 // non-leaf, just print children12360 // non-leaf, just print children
12367 var child_ni = first_ni;12361 var child_ni = first_ni;
12368 while (true) {12362 while (true) {
12369 try elf.printNode(tid, w, child_ni, indent + 1);12363 try elf.printNode(w, child_ni, indent + 1);
12370 child_ni = child_ni.next(&elf.mf).unwrap() orelse break;12364 child_ni = child_ni.next(&elf.mf).unwrap() orelse break;
12371 }12365 }
12372 return;12366 return;
src/link/MachO2.zig+9-11
...@@ -1100,10 +1100,11 @@ pub fn flush(...@@ -1100,10 +1100,11 @@ pub fn flush(
1100) link.Error!void {1100) link.Error!void {
1101 const diags = &macho.base.comp.link_diags;1101 const diags = &macho.base.comp.link_diags;
1102 _ = arena;1102 _ = arena;
1103 _ = tid;
1103 _ = prog_node;1104 _ = prog_node;
11041105
1105 try macho.updateSegmentLoadAddresses();1106 try macho.updateSegmentLoadAddresses();
1106 while (try macho.idle(tid)) {}1107 while (try macho.idle()) {}
11071108
1108 if (macho.lc.entry_point) |entry_point_lc| {1109 if (macho.lc.entry_point) |entry_point_lc| {
1109 // TODO: actually find the entry point.1110 // TODO: actually find the entry point.
...@@ -1120,7 +1121,7 @@ pub fn flush(...@@ -1120,7 +1121,7 @@ pub fn flush(
1120 };1121 };
11211122
1122 if (macho.options.enable_link_snapshots)1123 if (macho.options.enable_link_snapshots)
1123 macho.dumpStderr(tid) catch |err|1124 macho.dumpStderr() catch |err|
1124 return diags.fail("dumping link snapshot failed: {t}", .{err});1125 return diags.fail("dumping link snapshot failed: {t}", .{err});
1125}1126}
1126pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void {1127pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void {
...@@ -1128,9 +1129,7 @@ pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void {...@@ -1128,9 +1129,7 @@ pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void {
1128 _ = pt;1129 _ = pt;
1129 @panic("TODO");1130 @panic("TODO");
1130}1131}
1131pub fn idle(macho: *MachO, tid: Zcu.PerThread.Id) link.Error!bool {1132pub fn idle(macho: *MachO) link.Error!bool {
1132 _ = tid;
1133
1134 macho.mf.nodes_lock.lock();1133 macho.mf.nodes_lock.lock();
1135 defer macho.mf.nodes_lock.unlock();1134 defer macho.mf.nodes_lock.unlock();
11361135
...@@ -1253,28 +1252,27 @@ pub fn updateExports(...@@ -1253,28 +1252,27 @@ pub fn updateExports(
1253 @panic("TODO");1252 @panic("TODO");
1254}1253}
12551254
1256fn dumpStderr(macho: *MachO, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void {1255fn dumpStderr(macho: *MachO) Io.File.Writer.Error!void {
1257 const comp = macho.base.comp;1256 const comp = macho.base.comp;
1258 const io = comp.io;1257 const io = comp.io;
1259 var buffer: [512]u8 = undefined;1258 var buffer: [512]u8 = undefined;
1260 const stderr = try io.lockStderr(&buffer, null);1259 const stderr = try io.lockStderr(&buffer, null);
1261 defer io.unlockStderr();1260 defer io.unlockStderr();
1262 const w = &stderr.file_writer.interface;1261 const w = &stderr.file_writer.interface;
1263 _ = macho.dump(w, tid) catch |err| switch (err) {1262 _ = macho.dump(w) catch |err| switch (err) {
1264 error.WriteFailed => return stderr.file_writer.err.?,1263 error.WriteFailed => return stderr.file_writer.err.?,
1265 };1264 };
1266}1265}
12671266
1268pub fn dump(macho: *MachO, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {1267pub fn dump(macho: *MachO, w: *Io.Writer) !link.File.DumpResult {
1269 if (macho.options.enable_link_snapshots) {1268 if (macho.options.enable_link_snapshots) {
1270 try macho.printNode(tid, w, .root, 0);1269 try macho.printNode(w, .root, 0);
1271 return .enabled;1270 return .enabled;
1272 }1271 }
1273 return .disabled;1272 return .disabled;
1274}1273}
1275fn printNode(1274fn printNode(
1276 macho: *const MachO,1275 macho: *const MachO,
1277 tid: Zcu.PerThread.Id,
1278 w: *Io.Writer,1276 w: *Io.Writer,
1279 ni: MappedFile.Node.Index,1277 ni: MappedFile.Node.Index,
1280 indent: usize,1278 indent: usize,
...@@ -1310,7 +1308,7 @@ fn printNode(...@@ -1310,7 +1308,7 @@ fn printNode(
1310 // non-leaf, just print children1308 // non-leaf, just print children
1311 var child_ni = first_ni;1309 var child_ni = first_ni;
1312 while (true) {1310 while (true) {
1313 try macho.printNode(tid, w, child_ni, indent + 1);1311 try macho.printNode(w, child_ni, indent + 1);
1314 child_ni = child_ni.next(&macho.mf).unwrap() orelse break;1312 child_ni = child_ni.next(&macho.mf).unwrap() orelse break;
1315 }1313 }
1316 return;1314 return;
src/link/Queue.zig+33-30
...@@ -17,7 +17,7 @@...@@ -17,7 +17,7 @@
17/// set to `null` by the main thread after it is canceled. It is not otherwise modified; as such, it17/// set to `null` by the main thread after it is canceled. It is not otherwise modified; as such, it
18/// may be checked non-atomically. If a task is being queued and this is `null`, tasks must be run18/// may be checked non-atomically. If a task is being queued and this is `null`, tasks must be run
19/// eagerly.19/// eagerly.
20future: ?std.Io.Future(void),20future: ?std.Io.Future(Io.Cancelable!void),
2121
22/// This is only used if `future == null` during prelink. In that case, it is used to ensure that22/// This is only used if `future == null` during prelink. In that case, it is used to ensure that
23/// only one prelink task is run at a time.23/// only one prelink task is run at a time.
...@@ -42,15 +42,15 @@ pub const empty: Queue = .{...@@ -42,15 +42,15 @@ pub const empty: Queue = .{
4242
43pub fn cancel(q: *Queue, io: Io) void {43pub fn cancel(q: *Queue, io: Io) void {
44 if (q.future) |*f| {44 if (q.future) |*f| {
45 f.cancel(io);45 defer q.future = null;
46 q.future = null;46 f.cancel(io) catch {};
47 }47 }
48}48}
4949
50pub fn wait(q: *Queue, io: Io) void {50pub fn wait(q: *Queue, io: Io) Io.Cancelable!void {
51 if (q.future) |*f| {51 if (q.future) |*f| {
52 f.await(io);52 defer q.future = null;
53 q.future = null;53 try f.await(io);
54 }54 }
55}55}
5656
...@@ -107,11 +107,13 @@ pub fn enqueueZcu(...@@ -107,11 +107,13 @@ pub fn enqueueZcu(
107 } else |err| switch (err) {107 } else |err| switch (err) {
108 error.Canceled => |e| return e,108 error.Canceled => |e| return e,
109 error.Closed => {109 error.Closed => {
110 // The linker is still processing prelink tasks. Wait for those110 // This path is hit if the backend does not support `Zcu.Feature.separate_thread`.
111 // to finish, after which the linker task will exist, and ZCU111 // In that case, the linker processes *prelink* tasks on a thread, but once prelink
112 // tasks will be run non-concurrently. This logic exists for112 // completes we want to move processing to the main thread for all ZCU tasks. To
113 // backends which do not support `Zcu.Feature.separate_thread`.113 // achieve this, `Compilation.update` immmediately closes the ZCU task queue at the
114 q.wait(io);114 // start of the update, causing `runLinkTasks` to exit as soon as prelink completes.
115 // So here we just need to wait for that, then fall back to the single-threaded path.
116 try q.wait(io);
115 },117 },
116 }118 }
117 }119 }
...@@ -148,7 +150,7 @@ pub fn finishZcuQueue(q: *Queue, comp: *Compilation) void {...@@ -148,7 +150,7 @@ pub fn finishZcuQueue(q: *Queue, comp: *Compilation) void {
148 }150 }
149}151}
150152
151fn runLinkTasks(q: *Queue, comp: *Compilation) void {153fn runLinkTasks(q: *Queue, comp: *Compilation) Io.Cancelable!void {
152 const io = comp.io;154 const io = comp.io;
153 const tid: Zcu.PerThread.Id = .acquire(io);155 const tid: Zcu.PerThread.Id = .acquire(io);
154 defer tid.release(io);156 defer tid.release(io);
...@@ -159,12 +161,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {...@@ -159,12 +161,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {
159 var task_buf: [128]PrelinkTask = undefined;161 var task_buf: [128]PrelinkTask = undefined;
160 const limit: usize = if (have_idle_tasks) 0 else 1;162 const limit: usize = if (have_idle_tasks) 0 else 1;
161 const n = q.prelink_queue.get(io, &task_buf, limit) catch |err| switch (err) {163 const n = q.prelink_queue.get(io, &task_buf, limit) catch |err| switch (err) {
162 error.Canceled => return,164 error.Canceled => |e| return e,
163 error.Closed => break :prelink_tasks,165 error.Closed => break :prelink_tasks,
164 };166 };
165 if (n == 0) {167 if (n == 0) {
166 assert(have_idle_tasks);168 assert(have_idle_tasks);
167 have_idle_tasks = runIdleTask(comp, tid);169 have_idle_tasks = link.doIdleTask(comp) catch |err| switch (err) {
170 error.Canceled => |e| return e,
171 error.AlreadyReported => false,
172 error.OutOfMemory => more_idle_tasks: {
173 comp.link_diags.setAllocFailure();
174 break :more_idle_tasks false;
175 },
176 };
168 } else for (task_buf[0..n]) |task| {177 } else for (task_buf[0..n]) |task| {
169 link.doPrelinkTask(comp, task);178 link.doPrelinkTask(comp, task);
170 have_idle_tasks = true;179 have_idle_tasks = true;
...@@ -181,8 +190,8 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {...@@ -181,8 +190,8 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {
181 break :prelink;190 break :prelink;
182 }191 }
183 lf.prelink() catch |err| switch (err) {192 lf.prelink() catch |err| switch (err) {
193 error.Canceled => |e| return e,
184 error.OutOfMemory => comp.link_diags.setAllocFailure(),194 error.OutOfMemory => comp.link_diags.setAllocFailure(),
185 error.Canceled => @panic("TODO"),
186 error.AlreadyReported => {},195 error.AlreadyReported => {},
187 };196 };
188 }197 }
...@@ -191,31 +200,25 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {...@@ -191,31 +200,25 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {
191 var task_buf: [128]ZcuTask = undefined;200 var task_buf: [128]ZcuTask = undefined;
192 const limit: usize = if (have_idle_tasks) 0 else 1;201 const limit: usize = if (have_idle_tasks) 0 else 1;
193 const n = q.zcu_queue.get(io, &task_buf, limit) catch |err| switch (err) {202 const n = q.zcu_queue.get(io, &task_buf, limit) catch |err| switch (err) {
194 error.Canceled => return,203 error.Canceled => |e| return e,
195 error.Closed => break :zcu_tasks,204 error.Closed => break :zcu_tasks,
196 };205 };
197 if (n == 0) {206 if (n == 0) {
198 assert(have_idle_tasks);207 assert(have_idle_tasks);
199 have_idle_tasks = runIdleTask(comp, tid);208 have_idle_tasks = link.doIdleTask(comp) catch |err| switch (err) {
209 error.Canceled => |e| return e,
210 error.AlreadyReported => false,
211 error.OutOfMemory => more_idle_tasks: {
212 comp.link_diags.setAllocFailure();
213 break :more_idle_tasks false;
214 },
215 };
200 } else for (task_buf[0..n]) |task| {216 } else for (task_buf[0..n]) |task| {
201 link.doZcuTask(comp, tid, task);217 link.doZcuTask(comp, tid, task);
202 have_idle_tasks = true;218 have_idle_tasks = true;
203 }219 }
204 }220 }
205}221}
206fn runIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) bool {
207 return link.doIdleTask(comp, tid) catch |err| switch (err) {
208 error.OutOfMemory => have_more: {
209 comp.link_diags.setAllocFailure();
210 break :have_more false;
211 },
212 error.AlreadyReported => false,
213 error.Canceled => {
214 comp.io.recancel();
215 return false;
216 },
217 };
218}
219222
220const std = @import("std");223const std = @import("std");
221const assert = std.debug.assert;224const assert = std.debug.assert;