| author | |
| committer | |
| log | 53af57628965b54af7662d48ca434ef92f482001 |
| tree | ead3960a259631ca831d570b3559a87b5b625c64 |
| parent | 3f9d3859d4d8a55cf6cb055c1a35d99e05cfa253 |
8 files changed, 98 insertions(+), 104 deletions(-)
src/Compilation.zig+1-1| ... | @@ -4428,7 +4428,7 @@ fn performAllTheWork( | ... | @@ -4428,7 +4428,7 @@ fn performAllTheWork( |
| 4428 | comp.windows_libs_num_done = @intCast(comp.windows_libs.count()); | 4428 | comp.windows_libs_num_done = @intCast(comp.windows_libs.count()); |
| 4429 | try misc_group.await(io); | 4429 | try misc_group.await(io); |
| 4430 | 4430 | ||
| 4431 | comp.link_queue.wait(io); | 4431 | try comp.link_queue.wait(io); |
| 4432 | } | 4432 | } |
| 4433 | 4433 | ||
| 4434 | fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node) void { | 4434 | 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,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; |
| 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 | 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( |
| 269 | 278 | ||
| 270 | try zcu.flushRetryableFailures(); | 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 | 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 { |
| 86 | 86 | ||
| 87 | pub const LinkerOp = if (enabled) struct { | 87 | pub 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 implementation | 100 | // 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 | }; |
| 105 | 104 | ||
| 106 | fn dumpCrashContext() Io.Writer.Error!void { | 105 | fn 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 | } |
| 952 | 952 | ||
| 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); |
| 979 | 979 | ||
| 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); |
| 1028 | 1028 | ||
| 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); |
| 1031 | 1031 | ||
| 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 | }; |
| 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 | 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 | } |
| 1913 | pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) Error!bool { | 1913 | pub 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 final | 1916 | /// 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 running | 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,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(); |
| 5921 | 5922 | ||
| 5922 | const comp = coff.base.comp; | 5923 | const comp = coff.base.comp; |
| 5923 | 5924 | ||
| 5924 | while (try coff.resolve()) {} | 5925 | while (try coff.resolve()) {} |
| 5925 | while (try coff.idle(tid)) {} | 5926 | while (try coff.idle()) {} |
| 5926 | 5927 | ||
| 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()) {} |
| 5946 | 5947 | ||
| 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 | }; |
| 5958 | 5959 | ||
| 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 | } |
| 5963 | 5964 | ||
| ... | @@ -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 | } |
| 6071 | 6072 | ||
| 6072 | pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { | 6073 | pub 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( |
| 7457 | 7457 | ||
| 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()) {} |
| 7461 | 7461 | ||
| 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 | } |
| 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 | 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 | } |
| 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 | 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 | } |
| 7764 | 7764 | ||
| 7765 | pub fn printNode( | 7765 | fn 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 children | 7791 | // 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 | } |
| 9728 | fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { | 9728 | fn 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; |
| 9732 | 9733 | ||
| 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 | } |
| 9826 | pub fn updateContainerTypeInner( | 9827 | fn 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( |
| 10238 | fn flushInner( | 10240 | fn 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( |
| 10266 | 10267 | ||
| 10267 | try elf.prepareDynamic(); | 10268 | try elf.prepareDynamic(); |
| 10268 | 10269 | ||
| 10269 | while (try elf.idle(tid)) {} | 10270 | while (try elf.idle()) {} |
| 10270 | 10271 | ||
| 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(); |
| 10324 | 10325 | ||
| 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 | } |
| 10329 | 10330 | ||
| 10330 | pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { | 10331 | pub 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 | } |
| 12136 | fn updateExportInner( | 12135 | fn 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; |
| 12143 | 12138 | ||
| 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 | } |
| 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 | 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 | } |
| 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 | 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 |
| 12235 | 12230 | ||
| 12236 | pub fn printNode( | 12231 | pub 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 children | 12360 | // 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; |
| 1104 | 1105 | ||
| 1105 | try macho.updateSegmentLoadAddresses(); | 1106 | try macho.updateSegmentLoadAddresses(); |
| 1106 | while (try macho.idle(tid)) {} | 1107 | while (try macho.idle()) {} |
| 1107 | 1108 | ||
| 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 | }; |
| 1121 | 1122 | ||
| 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 | } |
| 1126 | pub fn updateErrorData(macho: *MachO, pt: Zcu.PerThread) link.Error!void { | 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,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 | } |
| 1131 | pub fn idle(macho: *MachO, tid: Zcu.PerThread.Id) link.Error!bool { | 1132 | pub 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(); |
| 1136 | 1135 | ||
| ... | @@ -1253,28 +1252,27 @@ pub fn updateExports( | ... | @@ -1253,28 +1252,27 @@ pub fn updateExports( |
| 1253 | @panic("TODO"); | 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 | 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 | } |
| 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 | 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 | } |
| 1275 | fn printNode( | 1274 | fn 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 children | 1308 | // 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, it | 17 | /// 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 run | 18 | /// may be checked non-atomically. If a task is being queued and this is `null`, tasks must be run |
| 19 | /// eagerly. | 19 | /// eagerly. |
| 20 | future: ?std.Io.Future(void), | 20 | future: ?std.Io.Future(Io.Cancelable!void), |
| 21 | 21 | ||
| 22 | /// This is only used if `future == null` during prelink. In that case, it is used to ensure that | 22 | /// 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 = .{ |
| 42 | 42 | ||
| 43 | pub fn cancel(q: *Queue, io: Io) void { | 43 | pub 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 | } |
| 49 | 49 | ||
| 50 | pub fn wait(q: *Queue, io: Io) void { | 50 | pub 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 | } |
| 56 | 56 | ||
| ... | @@ -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 those | 110 | // 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 ZCU | 111 | // In that case, the linker processes *prelink* tasks on a thread, but once prelink |
| 112 | // tasks will be run non-concurrently. This logic exists for | 112 | // 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 | } |
| 150 | 152 | ||
| 151 | fn runLinkTasks(q: *Queue, comp: *Compilation) void { | 153 | fn 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 | } |
| 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 | const std = @import("std"); | 223 | const std = @import("std"); |
| 221 | const assert = std.debug.assert; | 224 | const assert = std.debug.assert; |