| author | |
| committer | |
| log | 1ecbc16172649c5f11c2c5173a42f04a3c7e5d2b |
| tree | befb3f8c6a8a7a038d791049d468c004f1b4ee2c |
| parent | ebb8683ef1298e5d45f8d9912cf161954cc7ca99 |
| signature | Commit is signed but in an unrecognized format. |
8 files changed, 98 insertions(+), 104 deletions(-)
src/Compilation.zig+1-1| ... | ... | @@ -4427,7 +4427,7 @@ fn performAllTheWork( |
| 4427 | 4427 | comp.windows_libs_num_done = @intCast(comp.windows_libs.count()); |
| 4428 | 4428 | try misc_group.await(io); |
| 4429 | 4429 | |
| 4430 | comp.link_queue.wait(io); | |
| 4430 | try comp.link_queue.wait(io); | |
| 4431 | 4431 | } |
| 4432 | 4432 | |
| 4433 | 4433 | fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void { |
src/Zcu/PerThread.zig+9-8| ... | ... | @@ -138,6 +138,15 @@ pub fn update( |
| 138 | 138 | const gpa = comp.gpa; |
| 139 | 139 | const io = comp.io; |
| 140 | 140 | |
| 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 | 151 | const tracy_trace = traceNamed(@src(), "astgen"); |
| 143 | 152 | defer tracy_trace.end(); |
| ... | ... | @@ -269,14 +278,6 @@ pub fn update( |
| 269 | 278 | |
| 270 | 279 | try zcu.flushRetryableFailures(); |
| 271 | 280 | |
| 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 | 281 | zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); |
| 281 | 282 | if (comp.bin_file != null) { |
| 282 | 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 | 86 | |
| 87 | 87 | pub const LinkerOp = if (enabled) struct { |
| 88 | 88 | lf: *link.File, |
| 89 | tid: Zcu.PerThread.Id, | |
| 90 | 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 | 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 { | |
| 96 | std.debug.assert(current.?.lf == lf and current.?.tid == tid); | |
| 94 | pub fn stop(lf: *link.File) void { | |
| 95 | std.debug.assert(current.?.lf == lf); | |
| 97 | 96 | current = null; |
| 98 | 97 | } |
| 99 | 98 | } else struct { |
| 100 | 99 | const current: ?noreturn = null; |
| 101 | 100 | // Dummy implementation |
| 102 | pub fn start(_: *link.File, _: Zcu.PerThread.Id) void {} | |
| 103 | pub fn stop(_: *link.File, _: Zcu.PerThread.Id) void {} | |
| 101 | pub fn start(_: *link.File) void {} | |
| 102 | pub fn stop(_: *link.File) void {} | |
| 104 | 103 | }; |
| 105 | 104 | |
| 106 | 105 | fn dumpCrashContext() Io.Writer.Error!void { |
| ... | ... | @@ -132,7 +131,7 @@ fn dumpCrashContext() Io.Writer.Error!void { |
| 132 | 131 | try dumpCrashContextSema(anal, w, &S.crash_heap); |
| 133 | 132 | } else if (LinkerOp.current) |linker_op| { |
| 134 | 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 | 135 | .unimplemented => try w.writeAll("(backend does not support link snapshots)"), |
| 137 | 136 | .needs_extensions => try w.writeAll("(build with -Ddebug-extensions to dump linker state)"), |
| 138 | 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 | 950 | } |
| 951 | 951 | } |
| 952 | 952 | |
| 953 | pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool { | |
| 953 | fn idle(base: *File) Error!bool { | |
| 954 | 954 | switch (base.tag) { |
| 955 | 955 | else => return false, |
| 956 | 956 | inline .elf2, .coff, .macho2 => |tag| { |
| 957 | 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 | 974 | /// Commit pending changes and write headers. Takes into account final output mode. |
| 975 | 975 | /// `arena` has the lifetime of the call to `Compilation.update`. |
| 976 | 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); | |
| 978 | defer crash_report.LinkerOp.stop(base, tid); | |
| 977 | crash_report.LinkerOp.start(base); | |
| 978 | defer crash_report.LinkerOp.stop(base); | |
| 979 | 979 | |
| 980 | 980 | const comp = base.comp; |
| 981 | 981 | const io = comp.io; |
| ... | ... | @@ -1026,8 +1026,8 @@ pub const File = struct { |
| 1026 | 1026 | ) Error!void { |
| 1027 | 1027 | assert(pt.zcu.llvm_object == null); |
| 1028 | 1028 | |
| 1029 | crash_report.LinkerOp.start(base, pt.tid); | |
| 1030 | defer crash_report.LinkerOp.stop(base, pt.tid); | |
| 1029 | crash_report.LinkerOp.start(base); | |
| 1030 | defer crash_report.LinkerOp.stop(base); | |
| 1031 | 1031 | |
| 1032 | 1032 | switch (base.tag) { |
| 1033 | 1033 | .lld => unreachable, |
| ... | ... | @@ -1115,7 +1115,7 @@ pub const File = struct { |
| 1115 | 1115 | enabled, |
| 1116 | 1116 | }; |
| 1117 | 1117 | |
| 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 | 1119 | if (!build_options.enable_debug_extensions) return .not_built; |
| 1120 | 1120 | switch (base.tag) { |
| 1121 | 1121 | .elf, |
| ... | ... | @@ -1129,7 +1129,7 @@ pub const File = struct { |
| 1129 | 1129 | => return .unimplemented, |
| 1130 | 1130 | inline else => |tag| { |
| 1131 | 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 | 1910 | } |
| 1911 | 1911 | } |
| 1912 | 1912 | } |
| 1913 | pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) Error!bool { | |
| 1914 | return if (comp.bin_file) |lf| lf.idle(tid) else false; | |
| 1913 | pub fn doIdleTask(comp: *Compilation) Error!bool { | |
| 1914 | return if (comp.bin_file) |lf| lf.idle() else false; | |
| 1915 | 1915 | } |
| 1916 | 1916 | /// After the main pipeline is done, but before flush, the compilation may need to link one final |
| 1917 | 1917 | /// `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 | 5916 | prog_node: std.Progress.Node, |
| 5917 | 5917 | ) link.Error!void { |
| 5918 | 5918 | _ = arena; |
| 5919 | _ = tid; | |
| 5919 | 5920 | const sub_prog_node = prog_node.start("COFF Flush", 0); |
| 5920 | 5921 | defer sub_prog_node.end(); |
| 5921 | 5922 | |
| 5922 | 5923 | const comp = coff.base.comp; |
| 5923 | 5924 | |
| 5924 | 5925 | while (try coff.resolve()) {} |
| 5925 | while (try coff.idle(tid)) {} | |
| 5926 | while (try coff.idle()) {} | |
| 5926 | 5927 | |
| 5927 | 5928 | // This has to occur after all other flushMoved / flushResized have resolved, |
| 5928 | 5929 | // but it will also generate one more set of resizes and moves. |
| ... | ... | @@ -5942,7 +5943,7 @@ pub fn flush( |
| 5942 | 5943 | ), |
| 5943 | 5944 | }; |
| 5944 | 5945 | } |
| 5945 | while (try coff.idle(tid)) {} | |
| 5946 | while (try coff.idle()) {} | |
| 5946 | 5947 | |
| 5947 | 5948 | if (coff.isImage()) |
| 5948 | 5949 | try coff.reportUndefs(); |
| ... | ... | @@ -5957,7 +5958,7 @@ pub fn flush( |
| 5957 | 5958 | }; |
| 5958 | 5959 | |
| 5959 | 5960 | if (coff.options.enable_link_snapshots) |
| 5960 | coff.dumpStderr(tid) catch |err| | |
| 5961 | coff.dumpStderr() catch |err| | |
| 5961 | 5962 | return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err}); |
| 5962 | 5963 | } |
| 5963 | 5964 | |
| ... | ... | @@ -6069,8 +6070,7 @@ fn resolve(coff: *Coff) !bool { |
| 6069 | 6070 | return false; |
| 6070 | 6071 | } |
| 6071 | 6072 | |
| 6072 | pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { | |
| 6073 | _ = tid; | |
| 6073 | pub fn idle(coff: *Coff) !bool { | |
| 6074 | 6074 | // Idle tasks should not modify create / modify nodes, otherwise the output is not reproducible. |
| 6075 | 6075 | coff.mf.nodes_lock.lock(); |
| 6076 | 6076 | defer coff.mf.nodes_lock.unlock(); |
| ... | ... | @@ -7457,7 +7457,7 @@ fn updateExportInner( |
| 7457 | 7457 | |
| 7458 | 7458 | try coff.genPending(pt); |
| 7459 | 7459 | while (try coff.resolve()) {} |
| 7460 | while (try coff.idle(pt.tid)) {} | |
| 7460 | while (try coff.idle()) {} | |
| 7461 | 7461 | |
| 7462 | 7462 | const machine = coff.targetLoad(&coff.headerPtr().machine); |
| 7463 | 7463 | const exported_ni = exported_si.node(coff); |
| ... | ... | @@ -7595,21 +7595,21 @@ fn updateExportInner( |
| 7595 | 7595 | } |
| 7596 | 7596 | } |
| 7597 | 7597 | |
| 7598 | fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void { | |
| 7598 | fn dumpStderr(coff: *Coff) Io.File.Writer.Error!void { | |
| 7599 | 7599 | const comp = coff.base.comp; |
| 7600 | 7600 | const io = comp.io; |
| 7601 | 7601 | var buffer: [512]u8 = undefined; |
| 7602 | 7602 | const stderr = try io.lockStderr(&buffer, null); |
| 7603 | 7603 | defer io.unlockStderr(); |
| 7604 | 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 | 7606 | error.WriteFailed => return stderr.file_writer.err.?, |
| 7607 | 7607 | }; |
| 7608 | 7608 | } |
| 7609 | 7609 | |
| 7610 | pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult { | |
| 7610 | pub fn dump(coff: *Coff, w: *Io.Writer) Io.Writer.Error!link.File.DumpResult { | |
| 7611 | 7611 | if (coff.options.enable_link_snapshots) { |
| 7612 | try coff.printNode(tid, w, .root, 0); | |
| 7612 | try coff.printNode(w, .root, 0); | |
| 7613 | 7613 | try w.writeAll("Section table:\n"); |
| 7614 | 7614 | for (coff.section_table.keys(), coff.section_table.values()) |name, sec| |
| 7615 | 7615 | try coff.printSection(w, name, sec.si); |
| ... | ... | @@ -7762,9 +7762,8 @@ fn printNodeName( |
| 7762 | 7762 | } |
| 7763 | 7763 | } |
| 7764 | 7764 | |
| 7765 | pub fn printNode( | |
| 7765 | fn printNode( | |
| 7766 | 7766 | coff: *Coff, |
| 7767 | tid: Zcu.PerThread.Id, | |
| 7768 | 7767 | w: *Io.Writer, |
| 7769 | 7768 | ni: MappedFile.Node.Index, |
| 7770 | 7769 | indent: usize, |
| ... | ... | @@ -7792,7 +7791,7 @@ pub fn printNode( |
| 7792 | 7791 | // non-leaf, just print children |
| 7793 | 7792 | var child_ni = first_ni; |
| 7794 | 7793 | while (true) { |
| 7795 | try coff.printNode(tid, w, child_ni, indent + 1); | |
| 7794 | try coff.printNode(w, child_ni, indent + 1); | |
| 7796 | 7795 | child_ni = child_ni.next(&coff.mf).unwrap() orelse break; |
| 7797 | 7796 | } |
| 7798 | 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 | 9726 | }; |
| 9727 | 9727 | } |
| 9728 | 9728 | fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { |
| 9729 | const zcu = pt.zcu; | |
| 9730 | const gpa = zcu.gpa; | |
| 9729 | const comp = elf.base.comp; | |
| 9730 | const gpa = comp.gpa; | |
| 9731 | const zcu = comp.zcu.?; | |
| 9731 | 9732 | const ip = &zcu.intern_pool; |
| 9732 | 9733 | |
| 9733 | 9734 | const nav = ip.getNav(nav_index); |
| ... | ... | @@ -9823,7 +9824,7 @@ pub fn updateContainerType( |
| 9823 | 9824 | ), |
| 9824 | 9825 | }; |
| 9825 | 9826 | } |
| 9826 | pub fn updateContainerTypeInner( | |
| 9827 | fn updateContainerTypeInner( | |
| 9827 | 9828 | elf: *Elf, |
| 9828 | 9829 | pt: Zcu.PerThread, |
| 9829 | 9830 | ty: InternPool.Index, |
| ... | ... | @@ -10227,7 +10228,8 @@ pub fn flush( |
| 10227 | 10228 | tid: Zcu.PerThread.Id, |
| 10228 | 10229 | prog_node: std.Progress.Node, |
| 10229 | 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 | 10233 | else => |e| return e, |
| 10232 | 10234 | error.MappedFileIo => return elf.base.comp.link_diags.fail( |
| 10233 | 10235 | "failed to write output file: {t}", |
| ... | ... | @@ -10238,7 +10240,6 @@ pub fn flush( |
| 10238 | 10240 | fn flushInner( |
| 10239 | 10241 | elf: *Elf, |
| 10240 | 10242 | arena: std.mem.Allocator, |
| 10241 | tid: Zcu.PerThread.Id, | |
| 10242 | 10243 | prog_node: std.Progress.Node, |
| 10243 | 10244 | ) Error!void { |
| 10244 | 10245 | const comp = elf.base.comp; |
| ... | ... | @@ -10266,7 +10267,7 @@ fn flushInner( |
| 10266 | 10267 | |
| 10267 | 10268 | try elf.prepareDynamic(); |
| 10268 | 10269 | |
| 10269 | while (try elf.idle(tid)) {} | |
| 10270 | while (try elf.idle()) {} | |
| 10270 | 10271 | |
| 10271 | 10272 | assert(elf.input_pending_index == elf.inputs.items.len); |
| 10272 | 10273 | assert(elf.input_section_pending_index == elf.input_sections.items.len); |
| ... | ... | @@ -10323,13 +10324,11 @@ fn flushInner( |
| 10323 | 10324 | try elf.mf.flush(); |
| 10324 | 10325 | |
| 10325 | 10326 | if (elf.options.enable_link_snapshots) |
| 10326 | elf.dumpStderr(tid) catch |err| | |
| 10327 | elf.dumpStderr() catch |err| | |
| 10327 | 10328 | return diags.fail("dumping link snapshot failed: {t}", .{err}); |
| 10328 | 10329 | } |
| 10329 | 10330 | |
| 10330 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | |
| 10331 | _ = tid; | |
| 10332 | ||
| 10331 | pub fn idle(elf: *Elf) link.Error!bool { | |
| 10333 | 10332 | // This function is called non-deterministically, and so must not affect the layout of any nodes. |
| 10334 | 10333 | elf.mf.nodes_lock.lock(); |
| 10335 | 10334 | defer elf.mf.nodes_lock.unlock(); |
| ... | ... | @@ -12126,19 +12125,15 @@ pub fn updateExports( |
| 12126 | 12125 | export_indices: []const Zcu.Export.Index, |
| 12127 | 12126 | ) link.Error!void { |
| 12128 | 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 | 12129 | else => |e| return e, |
| 12131 | 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 | 12133 | try elf.genPending(pt); |
| 12135 | 12134 | } |
| 12136 | fn updateExportInner( | |
| 12137 | elf: *Elf, | |
| 12138 | pt: Zcu.PerThread, | |
| 12139 | export_index: Zcu.Export.Index, | |
| 12140 | ) Error!void { | |
| 12141 | const zcu = pt.zcu; | |
| 12135 | fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void { | |
| 12136 | const zcu = elf.base.comp.zcu.?; | |
| 12142 | 12137 | const ip = &zcu.intern_pool; |
| 12143 | 12138 | |
| 12144 | 12139 | const @"export" = export_index.ptr(zcu); |
| ... | ... | @@ -12213,21 +12208,21 @@ fn updateExportInner( |
| 12213 | 12208 | }; |
| 12214 | 12209 | } |
| 12215 | 12210 | |
| 12216 | fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void { | |
| 12211 | fn dumpStderr(elf: *Elf) Io.File.Writer.Error!void { | |
| 12217 | 12212 | const comp = elf.base.comp; |
| 12218 | 12213 | const io = comp.io; |
| 12219 | 12214 | var buffer: [512]u8 = undefined; |
| 12220 | 12215 | const stderr = try io.lockStderr(&buffer, null); |
| 12221 | 12216 | defer io.unlockStderr(); |
| 12222 | 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 | 12219 | error.WriteFailed => return stderr.file_writer.err.?, |
| 12225 | 12220 | }; |
| 12226 | 12221 | } |
| 12227 | 12222 | |
| 12228 | pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult { | |
| 12223 | pub fn dump(elf: *Elf, w: *Io.Writer) Io.Writer.Error!link.File.DumpResult { | |
| 12229 | 12224 | if (elf.options.enable_link_snapshots) { |
| 12230 | try elf.printNode(tid, w, .root, 0); | |
| 12225 | try elf.printNode(w, .root, 0); | |
| 12231 | 12226 | return .enabled; |
| 12232 | 12227 | } |
| 12233 | 12228 | return .disabled; |
| ... | ... | @@ -12235,7 +12230,6 @@ pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!lin |
| 12235 | 12230 | |
| 12236 | 12231 | pub fn printNode( |
| 12237 | 12232 | elf: *Elf, |
| 12238 | tid: Zcu.PerThread.Id, | |
| 12239 | 12233 | w: *Io.Writer, |
| 12240 | 12234 | ni: MappedFile.Node.Index, |
| 12241 | 12235 | indent: usize, |
| ... | ... | @@ -12366,7 +12360,7 @@ pub fn printNode( |
| 12366 | 12360 | // non-leaf, just print children |
| 12367 | 12361 | var child_ni = first_ni; |
| 12368 | 12362 | while (true) { |
| 12369 | try elf.printNode(tid, w, child_ni, indent + 1); | |
| 12363 | try elf.printNode(w, child_ni, indent + 1); | |
| 12370 | 12364 | child_ni = child_ni.next(&elf.mf).unwrap() orelse break; |
| 12371 | 12365 | } |
| 12372 | 12366 | return; |
src/link/MachO2.zig+9-11| ... | ... | @@ -1100,10 +1100,11 @@ pub fn flush( |
| 1100 | 1100 | ) link.Error!void { |
| 1101 | 1101 | const diags = &macho.base.comp.link_diags; |
| 1102 | 1102 | _ = arena; |
| 1103 | _ = tid; | |
| 1103 | 1104 | _ = prog_node; |
| 1104 | 1105 | |
| 1105 | 1106 | try macho.updateSegmentLoadAddresses(); |
| 1106 | while (try macho.idle(tid)) {} | |
| 1107 | while (try macho.idle()) {} | |
| 1107 | 1108 | |
| 1108 | 1109 | if (macho.lc.entry_point) |entry_point_lc| { |
| 1109 | 1110 | // TODO: actually find the entry point. |
| ... | ... | @@ -1120,7 +1121,7 @@ pub fn flush( |
| 1120 | 1121 | }; |
| 1121 | 1122 | |
| 1122 | 1123 | if (macho.options.enable_link_snapshots) |
| 1123 | macho.dumpStderr(tid) catch |err| | |
| 1124 | macho.dumpStderr() catch |err| | |
| 1124 | 1125 | return diags.fail("dumping link snapshot failed: {t}", .{err}); |
| 1125 | 1126 | } |
| 1126 | 1127 | 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 | 1129 | _ = pt; |
| 1129 | 1130 | @panic("TODO"); |
| 1130 | 1131 | } |
| 1131 | pub fn idle(macho: *MachO, tid: Zcu.PerThread.Id) link.Error!bool { | |
| 1132 | _ = tid; | |
| 1133 | ||
| 1132 | pub fn idle(macho: *MachO) link.Error!bool { | |
| 1134 | 1133 | macho.mf.nodes_lock.lock(); |
| 1135 | 1134 | defer macho.mf.nodes_lock.unlock(); |
| 1136 | 1135 | |
| ... | ... | @@ -1253,28 +1252,27 @@ pub fn updateExports( |
| 1253 | 1252 | @panic("TODO"); |
| 1254 | 1253 | } |
| 1255 | 1254 | |
| 1256 | fn dumpStderr(macho: *MachO, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void { | |
| 1255 | fn dumpStderr(macho: *MachO) Io.File.Writer.Error!void { | |
| 1257 | 1256 | const comp = macho.base.comp; |
| 1258 | 1257 | const io = comp.io; |
| 1259 | 1258 | var buffer: [512]u8 = undefined; |
| 1260 | 1259 | const stderr = try io.lockStderr(&buffer, null); |
| 1261 | 1260 | defer io.unlockStderr(); |
| 1262 | 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 | 1263 | error.WriteFailed => return stderr.file_writer.err.?, |
| 1265 | 1264 | }; |
| 1266 | 1265 | } |
| 1267 | 1266 | |
| 1268 | pub fn dump(macho: *MachO, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult { | |
| 1267 | pub fn dump(macho: *MachO, w: *Io.Writer) !link.File.DumpResult { | |
| 1269 | 1268 | if (macho.options.enable_link_snapshots) { |
| 1270 | try macho.printNode(tid, w, .root, 0); | |
| 1269 | try macho.printNode(w, .root, 0); | |
| 1271 | 1270 | return .enabled; |
| 1272 | 1271 | } |
| 1273 | 1272 | return .disabled; |
| 1274 | 1273 | } |
| 1275 | 1274 | fn printNode( |
| 1276 | 1275 | macho: *const MachO, |
| 1277 | tid: Zcu.PerThread.Id, | |
| 1278 | 1276 | w: *Io.Writer, |
| 1279 | 1277 | ni: MappedFile.Node.Index, |
| 1280 | 1278 | indent: usize, |
| ... | ... | @@ -1310,7 +1308,7 @@ fn printNode( |
| 1310 | 1308 | // non-leaf, just print children |
| 1311 | 1309 | var child_ni = first_ni; |
| 1312 | 1310 | while (true) { |
| 1313 | try macho.printNode(tid, w, child_ni, indent + 1); | |
| 1311 | try macho.printNode(w, child_ni, indent + 1); | |
| 1314 | 1312 | child_ni = child_ni.next(&macho.mf).unwrap() orelse break; |
| 1315 | 1313 | } |
| 1316 | 1314 | return; |
src/link/Queue.zig+33-30| ... | ... | @@ -17,7 +17,7 @@ |
| 17 | 17 | /// set to `null` by the main thread after it is canceled. It is not otherwise modified; as such, it |
| 18 | 18 | /// may be checked non-atomically. If a task is being queued and this is `null`, tasks must be run |
| 19 | 19 | /// eagerly. |
| 20 | future: ?std.Io.Future(void), | |
| 20 | future: ?std.Io.Future(Io.Cancelable!void), | |
| 21 | 21 | |
| 22 | 22 | /// This is only used if `future == null` during prelink. In that case, it is used to ensure that |
| 23 | 23 | /// only one prelink task is run at a time. |
| ... | ... | @@ -42,15 +42,15 @@ pub const empty: Queue = .{ |
| 42 | 42 | |
| 43 | 43 | pub fn cancel(q: *Queue, io: Io) void { |
| 44 | 44 | if (q.future) |*f| { |
| 45 | f.cancel(io); | |
| 46 | q.future = null; | |
| 45 | defer q.future = null; | |
| 46 | f.cancel(io) catch {}; | |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | pub fn wait(q: *Queue, io: Io) void { | |
| 50 | pub fn wait(q: *Queue, io: Io) Io.Cancelable!void { | |
| 51 | 51 | if (q.future) |*f| { |
| 52 | f.await(io); | |
| 53 | q.future = null; | |
| 52 | defer q.future = null; | |
| 53 | try f.await(io); | |
| 54 | 54 | } |
| 55 | 55 | } |
| 56 | 56 | |
| ... | ... | @@ -107,11 +107,13 @@ pub fn enqueueZcu( |
| 107 | 107 | } else |err| switch (err) { |
| 108 | 108 | error.Canceled => |e| return e, |
| 109 | 109 | error.Closed => { |
| 110 | // The linker is still processing prelink tasks. Wait for those | |
| 111 | // to finish, after which the linker task will exist, and ZCU | |
| 112 | // tasks will be run non-concurrently. This logic exists for | |
| 113 | // backends which do not support `Zcu.Feature.separate_thread`. | |
| 114 | q.wait(io); | |
| 110 | // This path is hit if the backend does not support `Zcu.Feature.separate_thread`. | |
| 111 | // In that case, the linker processes *prelink* tasks on a thread, but once prelink | |
| 112 | // completes we want to move processing to the main thread for all ZCU tasks. To | |
| 113 | // achieve this, `Compilation.update` immmediately closes the ZCU task queue at the | |
| 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 | 150 | } |
| 149 | 151 | } |
| 150 | 152 | |
| 151 | fn runLinkTasks(q: *Queue, comp: *Compilation) void { | |
| 153 | fn runLinkTasks(q: *Queue, comp: *Compilation) Io.Cancelable!void { | |
| 152 | 154 | const io = comp.io; |
| 153 | 155 | const tid: Zcu.PerThread.Id = .acquire(io); |
| 154 | 156 | defer tid.release(io); |
| ... | ... | @@ -159,12 +161,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { |
| 159 | 161 | var task_buf: [128]PrelinkTask = undefined; |
| 160 | 162 | const limit: usize = if (have_idle_tasks) 0 else 1; |
| 161 | 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 | 165 | error.Closed => break :prelink_tasks, |
| 164 | 166 | }; |
| 165 | 167 | if (n == 0) { |
| 166 | 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 | 177 | } else for (task_buf[0..n]) |task| { |
| 169 | 178 | link.doPrelinkTask(comp, task); |
| 170 | 179 | have_idle_tasks = true; |
| ... | ... | @@ -181,8 +190,8 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { |
| 181 | 190 | break :prelink; |
| 182 | 191 | } |
| 183 | 192 | lf.prelink() catch |err| switch (err) { |
| 193 | error.Canceled => |e| return e, | |
| 184 | 194 | error.OutOfMemory => comp.link_diags.setAllocFailure(), |
| 185 | error.Canceled => @panic("TODO"), | |
| 186 | 195 | error.AlreadyReported => {}, |
| 187 | 196 | }; |
| 188 | 197 | } |
| ... | ... | @@ -191,31 +200,25 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { |
| 191 | 200 | var task_buf: [128]ZcuTask = undefined; |
| 192 | 201 | const limit: usize = if (have_idle_tasks) 0 else 1; |
| 193 | 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 | 204 | error.Closed => break :zcu_tasks, |
| 196 | 205 | }; |
| 197 | 206 | if (n == 0) { |
| 198 | 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 | 216 | } else for (task_buf[0..n]) |task| { |
| 201 | 217 | link.doZcuTask(comp, tid, task); |
| 202 | 218 | have_idle_tasks = true; |
| 203 | 219 | } |
| 204 | 220 | } |
| 205 | 221 | } |
| 206 | fn 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 | } | |
| 219 | 222 | |
| 220 | 223 | const std = @import("std"); |
| 221 | 224 | const assert = std.debug.assert; |