authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 00:00:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-23 16:27:39-07:00
logba71079837a071b53cab289d78e5bacb4925fd25
tree1b1096800e87c4128e09098b467b05f06cdd1cb3
parent9a511b4b273ad4b7ad9289e18de87421ee47b626

combine codegen work queue and linker task queue

these tasks have some shared data dependencies so they cannot be done simultaneously. Future work should untangle these data dependencies so that more can be done in parallel. for now this commit ensures correctness by making linker input parsing and codegen tasks part of the same queue.

6 files changed, 233 insertions(+), 276 deletions(-)

lib/std/Thread/WaitGroup.zig+5
...@@ -14,6 +14,11 @@ pub fn start(self: *WaitGroup) void {...@@ -14,6 +14,11 @@ pub fn start(self: *WaitGroup) void {
14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));14 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
15}15}
1616
17pub fn startMany(self: *WaitGroup, n: usize) void {
18 const state = self.state.fetchAdd(one_pending * n, .monotonic);
19 assert((state / one_pending) < (std.math.maxInt(usize) / one_pending));
20}
21
17pub fn finish(self: *WaitGroup) void {22pub fn finish(self: *WaitGroup) void {
18 const state = self.state.fetchSub(one_pending, .acq_rel);23 const state = self.state.fetchSub(one_pending, .acq_rel);
19 assert((state / one_pending) > 0);24 assert((state / one_pending) > 0);
src/Compilation.zig+46-135
...@@ -111,7 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa...@@ -111,7 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
111} = .{},111} = .{},
112112
113link_diags: link.Diags,113link_diags: link.Diags,
114link_task_queue: ThreadSafeQueue(link.File.Task) = .empty,114link_task_queue: ThreadSafeQueue(link.Task) = .empty,
115/// Ensure only 1 simultaneous call to `flushTaskQueue`.
116link_task_queue_safety: std.debug.SafetyLock = .{},
115117
116work_queues: [118work_queues: [
117 len: {119 len: {
...@@ -123,14 +125,6 @@ work_queues: [...@@ -123,14 +125,6 @@ work_queues: [
123 }125 }
124]std.fifo.LinearFifo(Job, .Dynamic),126]std.fifo.LinearFifo(Job, .Dynamic),
125127
126codegen_work: if (InternPool.single_threaded) void else struct {
127 mutex: std.Thread.Mutex,
128 cond: std.Thread.Condition,
129 queue: std.fifo.LinearFifo(CodegenJob, .Dynamic),
130 job_error: ?JobError,
131 done: bool,
132},
133
134/// These jobs are to invoke the Clang compiler to create an object file, which128/// These jobs are to invoke the Clang compiler to create an object file, which
135/// gets linked with the Compilation.129/// gets linked with the Compilation.
136c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),130c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
...@@ -267,7 +261,7 @@ emit_asm: ?EmitLoc,...@@ -267,7 +261,7 @@ emit_asm: ?EmitLoc,
267emit_llvm_ir: ?EmitLoc,261emit_llvm_ir: ?EmitLoc,
268emit_llvm_bc: ?EmitLoc,262emit_llvm_bc: ?EmitLoc,
269263
270work_queue_wait_group: WaitGroup = .{},264link_task_wait_group: WaitGroup = .{},
271work_queue_progress_node: std.Progress.Node = .none,265work_queue_progress_node: std.Progress.Node = .none,
272266
273llvm_opt_bisect_limit: c_int,267llvm_opt_bisect_limit: c_int,
...@@ -347,16 +341,14 @@ pub const RcIncludes = enum {...@@ -347,16 +341,14 @@ pub const RcIncludes = enum {
347};341};
348342
349const Job = union(enum) {343const Job = union(enum) {
350 /// Write the constant value for a Decl to the output file.344 /// Corresponds to the task in `link.Task`.
345 /// Only needed for backends that haven't yet been updated to not race against Sema.
351 codegen_nav: InternPool.Nav.Index,346 codegen_nav: InternPool.Nav.Index,
352 /// Write the machine code for a function to the output file.347 /// Corresponds to the task in `link.Task`.
353 codegen_func: struct {348 /// Only needed for backends that haven't yet been updated to not race against Sema.
354 /// This will either be a non-generic `func_decl` or a `func_instance`.349 codegen_func: link.Task.CodegenFunc,
355 func: InternPool.Index,350 /// Corresponds to the task in `link.Task`.
356 /// This `Air` is owned by the `Job` and allocated with `gpa`.351 /// Only needed for backends that haven't yet been updated to not race against Sema.
357 /// It must be deinited when the job is processed.
358 air: Air,
359 },
360 codegen_type: InternPool.Index,352 codegen_type: InternPool.Index,
361 /// The `Cau` must be semantically analyzed (and possibly export itself).353 /// The `Cau` must be semantically analyzed (and possibly export itself).
362 /// This may be its first time being analyzed, or it may be outdated.354 /// This may be its first time being analyzed, or it may be outdated.
...@@ -408,17 +400,6 @@ const Job = union(enum) {...@@ -408,17 +400,6 @@ const Job = union(enum) {
408 }400 }
409};401};
410402
411const CodegenJob = union(enum) {
412 nav: InternPool.Nav.Index,
413 func: struct {
414 func: InternPool.Index,
415 /// This `Air` is owned by the `Job` and allocated with `gpa`.
416 /// It must be deinited when the job is processed.
417 air: Air,
418 },
419 type: InternPool.Index,
420};
421
422pub const CObject = struct {403pub const CObject = struct {
423 /// Relative to cwd. Owned by arena.404 /// Relative to cwd. Owned by arena.
424 src: CSourceFile,405 src: CSourceFile,
...@@ -1465,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1465,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1465 .emit_llvm_ir = options.emit_llvm_ir,1446 .emit_llvm_ir = options.emit_llvm_ir,
1466 .emit_llvm_bc = options.emit_llvm_bc,1447 .emit_llvm_bc = options.emit_llvm_bc,
1467 .work_queues = .{std.fifo.LinearFifo(Job, .Dynamic).init(gpa)} ** @typeInfo(std.meta.FieldType(Compilation, .work_queues)).array.len,1448 .work_queues = .{std.fifo.LinearFifo(Job, .Dynamic).init(gpa)} ** @typeInfo(std.meta.FieldType(Compilation, .work_queues)).array.len,
1468 .codegen_work = if (InternPool.single_threaded) {} else .{
1469 .mutex = .{},
1470 .cond = .{},
1471 .queue = std.fifo.LinearFifo(CodegenJob, .Dynamic).init(gpa),
1472 .job_error = null,
1473 .done = false,
1474 },
1475 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),1449 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
1476 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},1450 .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},
1477 .astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),1451 .astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),
...@@ -1923,7 +1897,6 @@ pub fn destroy(comp: *Compilation) void {...@@ -1923,7 +1897,6 @@ pub fn destroy(comp: *Compilation) void {
1923 if (comp.zcu) |zcu| zcu.deinit();1897 if (comp.zcu) |zcu| zcu.deinit();
1924 comp.cache_use.deinit();1898 comp.cache_use.deinit();
1925 for (comp.work_queues) |work_queue| work_queue.deinit();1899 for (comp.work_queues) |work_queue| work_queue.deinit();
1926 if (!InternPool.single_threaded) comp.codegen_work.queue.deinit();
1927 comp.c_object_work_queue.deinit();1900 comp.c_object_work_queue.deinit();
1928 comp.win32_resource_work_queue.deinit();1901 comp.win32_resource_work_queue.deinit();
1929 comp.astgen_work_queue.deinit();1902 comp.astgen_work_queue.deinit();
...@@ -3485,7 +3458,6 @@ pub fn performAllTheWork(...@@ -3485,7 +3458,6 @@ pub fn performAllTheWork(
3485 zcu.generation += 1;3458 zcu.generation += 1;
3486 };3459 };
3487 try comp.performAllTheWorkInner(main_progress_node);3460 try comp.performAllTheWorkInner(main_progress_node);
3488 if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
3489}3461}
34903462
3491fn performAllTheWorkInner(3463fn performAllTheWorkInner(
...@@ -3497,36 +3469,35 @@ fn performAllTheWorkInner(...@@ -3497,36 +3469,35 @@ fn performAllTheWorkInner(
3497 // (at least for now) single-threaded main work queue. However, C object compilation3469 // (at least for now) single-threaded main work queue. However, C object compilation
3498 // only needs to be finished by the end of this function.3470 // only needs to be finished by the end of this function.
34993471
3500 const work_queue_wait_group = &comp.work_queue_wait_group;3472 var work_queue_wait_group: WaitGroup = .{};
3501
3502 work_queue_wait_group.reset();
3503 defer work_queue_wait_group.wait();3473 defer work_queue_wait_group.wait();
35043474
3505 if (comp.bin_file) |lf| {3475 comp.link_task_wait_group.reset();
3506 if (comp.link_task_queue.start()) {3476 defer comp.link_task_wait_group.wait();
3507 comp.thread_pool.spawnWg(work_queue_wait_group, link.File.flushTaskQueue, .{ lf, main_progress_node });3477
3508 }3478 if (comp.link_task_queue.start()) {
3479 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
3509 }3480 }
35103481
3511 if (comp.docs_emit != null) {3482 if (comp.docs_emit != null) {
3512 dev.check(.docs_emit);3483 dev.check(.docs_emit);
3513 comp.thread_pool.spawnWg(work_queue_wait_group, workerDocsCopy, .{comp});3484 comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
3514 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });3485 work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
3515 }3486 }
35163487
3517 if (comp.job_queued_compiler_rt_lib) {3488 if (comp.job_queued_compiler_rt_lib) {
3518 comp.job_queued_compiler_rt_lib = false;3489 comp.job_queued_compiler_rt_lib = false;
3519 work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node });3490 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node });
3520 }3491 }
35213492
3522 if (comp.job_queued_compiler_rt_obj) {3493 if (comp.job_queued_compiler_rt_obj) {
3523 comp.job_queued_compiler_rt_obj = false;3494 comp.job_queued_compiler_rt_obj = false;
3524 work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node });3495 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node });
3525 }3496 }
35263497
3527 if (comp.job_queued_fuzzer_lib) {3498 if (comp.job_queued_fuzzer_lib) {
3528 comp.job_queued_fuzzer_lib = false;3499 comp.job_queued_fuzzer_lib = false;
3529 work_queue_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node });3500 comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node });
3530 }3501 }
35313502
3532 {3503 {
...@@ -3591,13 +3562,13 @@ fn performAllTheWorkInner(...@@ -3591,13 +3562,13 @@ fn performAllTheWorkInner(
3591 }3562 }
35923563
3593 while (comp.c_object_work_queue.readItem()) |c_object| {3564 while (comp.c_object_work_queue.readItem()) |c_object| {
3594 comp.thread_pool.spawnWg(work_queue_wait_group, workerUpdateCObject, .{3565 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
3595 comp, c_object, main_progress_node,3566 comp, c_object, main_progress_node,
3596 });3567 });
3597 }3568 }
35983569
3599 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {3570 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {
3600 comp.thread_pool.spawnWg(work_queue_wait_group, workerUpdateWin32Resource, .{3571 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
3601 comp, win32_resource, main_progress_node,3572 comp, win32_resource, main_progress_node,
3602 });3573 });
3603 }3574 }
...@@ -3617,18 +3588,12 @@ fn performAllTheWorkInner(...@@ -3617,18 +3588,12 @@ fn performAllTheWorkInner(
3617 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);3588 zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
3618 }3589 }
36193590
3620 if (!InternPool.single_threaded) {3591 if (!comp.separateCodegenThreadOk()) {
3621 comp.codegen_work.done = false; // may be `true` from a prior update3592 // Waits until all input files have been parsed.
3622 comp.thread_pool.spawnWgId(work_queue_wait_group, codegenThread, .{comp});3593 comp.link_task_wait_group.wait();
3594 comp.link_task_wait_group.reset();
3595 std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{});
3623 }3596 }
3624 defer if (!InternPool.single_threaded) {
3625 {
3626 comp.codegen_work.mutex.lock();
3627 defer comp.codegen_work.mutex.unlock();
3628 comp.codegen_work.done = true;
3629 }
3630 comp.codegen_work.cond.signal();
3631 };
36323597
3633 work: while (true) {3598 work: while (true) {
3634 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {3599 for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {
...@@ -3672,16 +3637,14 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3672,16 +3637,14 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3672 }3637 }
3673 }3638 }
3674 assert(nav.status == .resolved);3639 assert(nav.status == .resolved);
3675 try comp.queueCodegenJob(tid, .{ .nav = nav_index });3640 comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
3676 },3641 },
3677 .codegen_func => |func| {3642 .codegen_func => |func| {
3678 // This call takes ownership of `func.air`.3643 comp.dispatchCodegenTask(tid, .{ .codegen_func = func });
3679 try comp.queueCodegenJob(tid, .{ .func = .{3644 },
3680 .func = func.func,3645 .codegen_type => |ty| {
3681 .air = func.air,3646 comp.dispatchCodegenTask(tid, .{ .codegen_type = ty });
3682 } });
3683 },3647 },
3684 .codegen_type => |ty| try comp.queueCodegenJob(tid, .{ .type = ty }),
3685 .analyze_func => |func| {3648 .analyze_func => |func| {
3686 const named_frame = tracy.namedFrame("analyze_func");3649 const named_frame = tracy.namedFrame("analyze_func");
3687 defer named_frame.end();3650 defer named_frame.end();
...@@ -3894,66 +3857,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre...@@ -3894,66 +3857,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
3894 }3857 }
3895}3858}
38963859
3897fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void {3860/// The reason for the double-queue here is that the first queue ensures any
3898 if (InternPool.single_threaded or3861/// resolve_type_fully tasks are complete before this dispatch function is called.
3899 !comp.zcu.?.backendSupportsFeature(.separate_thread))3862fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void {
3900 return processOneCodegenJob(tid, comp, codegen_job);3863 if (comp.separateCodegenThreadOk()) {
39013864 comp.queueLinkTasks(&.{link_task});
3902 {3865 } else {
3903 comp.codegen_work.mutex.lock();3866 link.doTask(comp, tid, link_task);
3904 defer comp.codegen_work.mutex.unlock();
3905 try comp.codegen_work.queue.writeItem(codegen_job);
3906 }
3907 comp.codegen_work.cond.signal();
3908}
3909
3910fn codegenThread(tid: usize, comp: *Compilation) void {
3911 comp.codegen_work.mutex.lock();
3912 defer comp.codegen_work.mutex.unlock();
3913
3914 while (true) {
3915 if (comp.codegen_work.queue.readItem()) |codegen_job| {
3916 comp.codegen_work.mutex.unlock();
3917 defer comp.codegen_work.mutex.lock();
3918
3919 processOneCodegenJob(tid, comp, codegen_job) catch |job_error| {
3920 comp.codegen_work.job_error = job_error;
3921 break;
3922 };
3923 continue;
3924 }
3925
3926 if (comp.codegen_work.done) break;
3927
3928 comp.codegen_work.cond.wait(&comp.codegen_work.mutex);
3929 }3867 }
3930}3868}
39313869
3932fn processOneCodegenJob(tid: usize, comp: *Compilation, codegen_job: CodegenJob) JobError!void {3870fn separateCodegenThreadOk(comp: *const Compilation) bool {
3933 switch (codegen_job) {3871 if (InternPool.single_threaded) return false;
3934 .nav => |nav_index| {3872 const zcu = comp.zcu orelse return true;
3935 const named_frame = tracy.namedFrame("codegen_nav");3873 return zcu.backendSupportsFeature(.separate_thread);
3936 defer named_frame.end();
3937
3938 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3939 try pt.linkerUpdateNav(nav_index);
3940 },
3941 .func => |func| {
3942 const named_frame = tracy.namedFrame("codegen_func");
3943 defer named_frame.end();
3944
3945 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3946 // This call takes ownership of `func.air`.
3947 try pt.linkerUpdateFunc(func.func, func.air);
3948 },
3949 .type => |ty| {
3950 const named_frame = tracy.namedFrame("codegen_type");
3951 defer named_frame.end();
3952
3953 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
3954 try pt.linkerUpdateContainerType(ty);
3955 },
3956 }
3957}3874}
39583875
3959fn workerDocsCopy(comp: *Compilation) void {3876fn workerDocsCopy(comp: *Compilation) void {
...@@ -6465,17 +6382,11 @@ pub fn queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builti...@@ -6465,17 +6382,11 @@ pub fn queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builti
64656382
6466/// Only valid to call during `update`. Automatically handles queuing up a6383/// Only valid to call during `update`. Automatically handles queuing up a
6467/// linker worker task if there is not already one.6384/// linker worker task if there is not already one.
6468pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void {6385pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.Task) void {
6469 const use_lld = build_options.have_llvm and comp.config.use_lld;
6470 if (use_lld) return;
6471 const target = comp.root_mod.resolved_target.result;
6472 if (target.ofmt != .elf) return;
6473 if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) {6386 if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) {
6474 error.OutOfMemory => return comp.setAllocFailure(),6387 error.OutOfMemory => return comp.setAllocFailure(),
6475 }) {6388 }) {
6476 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, link.File.flushTaskQueue, .{6389 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
6477 comp.bin_file.?, comp.work_queue_progress_node,
6478 });
6479 }6390 }
6480}6391}
64816392
src/Sema.zig+7
...@@ -2899,6 +2899,7 @@ fn zirStructDecl(...@@ -2899,6 +2899,7 @@ fn zirStructDecl(
2899 codegen_type: {2899 codegen_type: {
2900 if (zcu.comp.config.use_llvm) break :codegen_type;2900 if (zcu.comp.config.use_llvm) break :codegen_type;
2901 if (block.ownerModule().strip) break :codegen_type;2901 if (block.ownerModule().strip) break :codegen_type;
2902 // This job depends on any resolve_type_fully jobs queued up before it.
2902 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });2903 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
2903 }2904 }
2904 try sema.declareDependency(.{ .interned = wip_ty.index });2905 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -3149,6 +3150,7 @@ fn zirEnumDecl(...@@ -3149,6 +3150,7 @@ fn zirEnumDecl(
3149 codegen_type: {3150 codegen_type: {
3150 if (zcu.comp.config.use_llvm) break :codegen_type;3151 if (zcu.comp.config.use_llvm) break :codegen_type;
3151 if (block.ownerModule().strip) break :codegen_type;3152 if (block.ownerModule().strip) break :codegen_type;
3153 // This job depends on any resolve_type_fully jobs queued up before it.
3152 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3154 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3153 }3155 }
3154 return Air.internedToRef(wip_ty.index);3156 return Air.internedToRef(wip_ty.index);
...@@ -3272,6 +3274,7 @@ fn zirUnionDecl(...@@ -3272,6 +3274,7 @@ fn zirUnionDecl(
3272 codegen_type: {3274 codegen_type: {
3273 if (zcu.comp.config.use_llvm) break :codegen_type;3275 if (zcu.comp.config.use_llvm) break :codegen_type;
3274 if (block.ownerModule().strip) break :codegen_type;3276 if (block.ownerModule().strip) break :codegen_type;
3277 // This job depends on any resolve_type_fully jobs queued up before it.
3275 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3278 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3276 }3279 }
3277 try sema.declareDependency(.{ .interned = wip_ty.index });3280 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -3357,6 +3360,7 @@ fn zirOpaqueDecl(...@@ -3357,6 +3360,7 @@ fn zirOpaqueDecl(
3357 codegen_type: {3360 codegen_type: {
3358 if (zcu.comp.config.use_llvm) break :codegen_type;3361 if (zcu.comp.config.use_llvm) break :codegen_type;
3359 if (block.ownerModule().strip) break :codegen_type;3362 if (block.ownerModule().strip) break :codegen_type;
3363 // This job depends on any resolve_type_fully jobs queued up before it.
3360 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });3364 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
3361 }3365 }
3362 try sema.addTypeReferenceEntry(src, wip_ty.index);3366 try sema.addTypeReferenceEntry(src, wip_ty.index);
...@@ -22456,6 +22460,7 @@ fn reifyEnum(...@@ -22456,6 +22460,7 @@ fn reifyEnum(
22456 codegen_type: {22460 codegen_type: {
22457 if (zcu.comp.config.use_llvm) break :codegen_type;22461 if (zcu.comp.config.use_llvm) break :codegen_type;
22458 if (block.ownerModule().strip) break :codegen_type;22462 if (block.ownerModule().strip) break :codegen_type;
22463 // This job depends on any resolve_type_fully jobs queued up before it.
22459 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });22464 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
22460 }22465 }
22461 return Air.internedToRef(wip_ty.index);22466 return Air.internedToRef(wip_ty.index);
...@@ -22713,6 +22718,7 @@ fn reifyUnion(...@@ -22713,6 +22718,7 @@ fn reifyUnion(
22713 codegen_type: {22718 codegen_type: {
22714 if (zcu.comp.config.use_llvm) break :codegen_type;22719 if (zcu.comp.config.use_llvm) break :codegen_type;
22715 if (block.ownerModule().strip) break :codegen_type;22720 if (block.ownerModule().strip) break :codegen_type;
22721 // This job depends on any resolve_type_fully jobs queued up before it.
22716 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });22722 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
22717 }22723 }
22718 try sema.declareDependency(.{ .interned = wip_ty.index });22724 try sema.declareDependency(.{ .interned = wip_ty.index });
...@@ -22997,6 +23003,7 @@ fn reifyStruct(...@@ -22997,6 +23003,7 @@ fn reifyStruct(
22997 codegen_type: {23003 codegen_type: {
22998 if (zcu.comp.config.use_llvm) break :codegen_type;23004 if (zcu.comp.config.use_llvm) break :codegen_type;
22999 if (block.ownerModule().strip) break :codegen_type;23005 if (block.ownerModule().strip) break :codegen_type;
23006 // This job depends on any resolve_type_fully jobs queued up before it.
23000 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });23007 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
23001 }23008 }
23002 try sema.declareDependency(.{ .interned = wip_ty.index });23009 try sema.declareDependency(.{ .interned = wip_ty.index });
src/Zcu/PerThread.zig+5-1
...@@ -845,6 +845,7 @@ fn ensureFuncBodyAnalyzedInner(...@@ -845,6 +845,7 @@ fn ensureFuncBodyAnalyzedInner(
845 return .{ .ies_outdated = ies_outdated };845 return .{ .ies_outdated = ies_outdated };
846 }846 }
847847
848 // This job depends on any resolve_type_fully jobs queued up before it.
848 try comp.queueJob(.{ .codegen_func = .{849 try comp.queueJob(.{ .codegen_func = .{
849 .func = func_index,850 .func = func_index,
850 .air = air,851 .air = air,
...@@ -1016,6 +1017,7 @@ fn createFileRootStruct(...@@ -1016,6 +1017,7 @@ fn createFileRootStruct(
1016 codegen_type: {1017 codegen_type: {
1017 if (zcu.comp.config.use_llvm) break :codegen_type;1018 if (zcu.comp.config.use_llvm) break :codegen_type;
1018 if (file.mod.strip) break :codegen_type;1019 if (file.mod.strip) break :codegen_type;
1020 // This job depends on any resolve_type_fully jobs queued up before it.
1019 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });1021 try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
1020 }1022 }
1021 zcu.setFileRootType(file_index, wip_ty.index);1023 zcu.setFileRootType(file_index, wip_ty.index);
...@@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {...@@ -1362,6 +1364,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
1362 if (file.mod.strip) break :queue_codegen;1364 if (file.mod.strip) break :queue_codegen;
1363 }1365 }
13641366
1367 // This job depends on any resolve_type_fully jobs queued up before it.
1365 try zcu.comp.queueJob(.{ .codegen_nav = nav_index });1368 try zcu.comp.queueJob(.{ .codegen_nav = nav_index });
1366 }1369 }
13671370
...@@ -2593,7 +2596,7 @@ pub fn populateTestFunctions(...@@ -2593,7 +2596,7 @@ pub fn populateTestFunctions(
2593 }2596 }
2594}2597}
25952598
2596pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {2599pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{OutOfMemory}!void {
2597 const zcu = pt.zcu;2600 const zcu = pt.zcu;
2598 const comp = zcu.comp;2601 const comp = zcu.comp;
2599 const ip = &zcu.intern_pool;2602 const ip = &zcu.intern_pool;
...@@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator....@@ -3163,6 +3166,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.
3163pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {3166pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {
3164 const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);3167 const result = try pt.zcu.intern_pool.getExtern(pt.zcu.gpa, pt.tid, key);
3165 if (result.new_nav.unwrap()) |nav| {3168 if (result.new_nav.unwrap()) |nav| {
3169 // This job depends on any resolve_type_fully jobs queued up before it.
3166 try pt.zcu.comp.queueJob(.{ .codegen_nav = nav });3170 try pt.zcu.comp.queueJob(.{ .codegen_nav = nav });
3167 }3171 }
3168 return result.index;3172 return result.index;
src/glibc.zig+1-1
...@@ -1222,7 +1222,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {...@@ -1222,7 +1222,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
1222 assert(comp.glibc_so_files == null);1222 assert(comp.glibc_so_files == null);
1223 comp.glibc_so_files = so_files;1223 comp.glibc_so_files = so_files;
12241224
1225 var task_buffer: [libs.len]link.File.Task = undefined;1225 var task_buffer: [libs.len]link.Task = undefined;
1226 var task_buffer_i: usize = 0;1226 var task_buffer_i: usize = 0;
12271227
1228 {1228 {
src/link.zig+169-139
...@@ -370,9 +370,6 @@ pub const File = struct {...@@ -370,9 +370,6 @@ pub const File = struct {
370 lock: ?Cache.Lock = null,370 lock: ?Cache.Lock = null,
371 child_pid: ?std.process.Child.Id = null,371 child_pid: ?std.process.Child.Id = null,
372372
373 /// Ensure only 1 simultaneous call to `flushTaskQueue`.
374 task_queue_safety: std.debug.SafetyLock = .{},
375
376 pub const OpenOptions = struct {373 pub const OpenOptions = struct {
377 symbol_count_hint: u64 = 32,374 symbol_count_hint: u64 = 32,
378 program_code_size_hint: u64 = 256 * 1024,375 program_code_size_hint: u64 = 256 * 1024,
...@@ -1085,6 +1082,8 @@ pub const File = struct {...@@ -1085,6 +1082,8 @@ pub const File = struct {
1085 }1082 }
10861083
1087 pub fn loadInput(base: *File, input: Input) anyerror!void {1084 pub fn loadInput(base: *File, input: Input) anyerror!void {
1085 const use_lld = build_options.have_llvm and base.comp.config.use_lld;
1086 if (use_lld) return;
1088 switch (base.tag) {1087 switch (base.tag) {
1089 inline .elf => |tag| {1088 inline .elf => |tag| {
1090 dev.check(tag.devFeature());1089 dev.check(tag.devFeature());
...@@ -1360,151 +1359,182 @@ pub const File = struct {...@@ -1360,151 +1359,182 @@ pub const File = struct {
1360 pub const Wasm = @import("link/Wasm.zig");1359 pub const Wasm = @import("link/Wasm.zig");
1361 pub const NvPtx = @import("link/NvPtx.zig");1360 pub const NvPtx = @import("link/NvPtx.zig");
1362 pub const Dwarf = @import("link/Dwarf.zig");1361 pub const Dwarf = @import("link/Dwarf.zig");
1362};
13631363
1364 /// Does all the tasks in the queue. Runs in exactly one separate thread1364/// Does all the tasks in the queue. Runs in exactly one separate thread
1365 /// from the rest of compilation. All tasks performed here are1365/// from the rest of compilation. All tasks performed here are
1366 /// single-threaded with respect to one another.1366/// single-threaded with respect to one another.
1367 pub fn flushTaskQueue(base: *File, parent_prog_node: std.Progress.Node) void {1367pub fn flushTaskQueue(tid: usize, comp: *Compilation) void {
1368 const comp = base.comp;1368 comp.link_task_queue_safety.lock();
1369 base.task_queue_safety.lock();1369 defer comp.link_task_queue_safety.unlock();
1370 defer base.task_queue_safety.unlock();1370 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", 0);
1371 const prog_node = parent_prog_node.start("Parse Linker Inputs", 0);1371 defer prog_node.end();
1372 defer prog_node.end();1372 while (comp.link_task_queue.check()) |tasks| {
1373 while (comp.link_task_queue.check()) |tasks| {1373 for (tasks) |task| doTask(comp, tid, task);
1374 for (tasks) |task| doTask(base, task);
1375 }
1376 }1374 }
1375}
13771376
1378 pub const Task = union(enum) {1377pub const Task = union(enum) {
1379 /// Loads the objects, shared objects, and archives that are already1378 /// Loads the objects, shared objects, and archives that are already
1380 /// known from the command line.1379 /// known from the command line.
1381 load_explicitly_provided,1380 load_explicitly_provided,
1382 /// Loads the shared objects and archives by resolving1381 /// Loads the shared objects and archives by resolving
1383 /// `target_util.libcFullLinkFlags()` against the host libc1382 /// `target_util.libcFullLinkFlags()` against the host libc
1384 /// installation.1383 /// installation.
1385 load_host_libc,1384 load_host_libc,
1386 /// Tells the linker to load an object file by path.1385 /// Tells the linker to load an object file by path.
1387 load_object: Path,1386 load_object: Path,
1388 /// Tells the linker to load a static library by path.1387 /// Tells the linker to load a static library by path.
1389 load_archive: Path,1388 load_archive: Path,
1390 /// Tells the linker to load a shared library, possibly one that is a1389 /// Tells the linker to load a shared library, possibly one that is a
1391 /// GNU ld script.1390 /// GNU ld script.
1392 load_dso: Path,1391 load_dso: Path,
1393 /// Tells the linker to load an input which could be an object file,1392 /// Tells the linker to load an input which could be an object file,
1394 /// archive, or shared library.1393 /// archive, or shared library.
1395 load_input: Input,1394 load_input: Input,
1395
1396 /// Write the constant value for a Decl to the output file.
1397 codegen_nav: InternPool.Nav.Index,
1398 /// Write the machine code for a function to the output file.
1399 codegen_func: CodegenFunc,
1400 codegen_type: InternPool.Index,
1401
1402 pub const CodegenFunc = struct {
1403 /// This will either be a non-generic `func_decl` or a `func_instance`.
1404 func: InternPool.Index,
1405 /// This `Air` is owned by the `Job` and allocated with `gpa`.
1406 /// It must be deinited when the job is processed.
1407 air: Air,
1396 };1408 };
1409};
13971410
1398 fn doTask(base: *File, task: Task) void {1411pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
1399 const comp = base.comp;1412 const diags = &comp.link_diags;
1400 switch (task) {1413 switch (task) {
1401 .load_explicitly_provided => {1414 .load_explicitly_provided => if (comp.bin_file) |base| {
1402 for (comp.link_inputs) |input| {1415 for (comp.link_inputs) |input| {
1403 base.loadInput(input) catch |err| switch (err) {
1404 error.LinkFailure => return, // error reported via link_diags
1405 else => |e| switch (input) {
1406 .dso => |dso| comp.link_diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}),
1407 .object => |obj| comp.link_diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
1408 .archive => |obj| comp.link_diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
1409 .res => |res| comp.link_diags.addParseError(res.path, "failed to parse Windows resource: {s}", .{@errorName(e)}),
1410 .dso_exact => comp.link_diags.addError("failed to handle dso_exact: {s}", .{@errorName(e)}),
1411 },
1412 };
1413 }
1414 },
1415 .load_host_libc => {
1416 const target = comp.root_mod.resolved_target.result;
1417 const flags = target_util.libcFullLinkFlags(target);
1418 const crt_dir = comp.libc_installation.?.crt_dir.?;
1419 const sep = std.fs.path.sep_str;
1420 const diags = &comp.link_diags;
1421 for (flags) |flag| {
1422 assert(mem.startsWith(u8, flag, "-l"));
1423 const lib_name = flag["-l".len..];
1424 switch (comp.config.link_mode) {
1425 .dynamic => {
1426 const dso_path = Path.initCwd(
1427 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1428 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
1429 }) catch return diags.setAllocFailure(),
1430 );
1431 base.openLoadDso(dso_path, .{
1432 .preferred_mode = .dynamic,
1433 .search_strategy = .paths_first,
1434 }) catch |err| switch (err) {
1435 error.FileNotFound => {
1436 // Also try static.
1437 const archive_path = Path.initCwd(
1438 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1439 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1440 }) catch return diags.setAllocFailure(),
1441 );
1442 base.openLoadArchive(archive_path, .{
1443 .preferred_mode = .dynamic,
1444 .search_strategy = .paths_first,
1445 }) catch |archive_err| switch (archive_err) {
1446 error.LinkFailure => return, // error reported via diags
1447 else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }),
1448 };
1449 },
1450 error.LinkFailure => return, // error reported via diags
1451 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1452 };
1453 },
1454 .static => {
1455 const path = Path.initCwd(
1456 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1457 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1458 }) catch return diags.setAllocFailure(),
1459 );
1460 // glibc sometimes makes even archive files GNU ld scripts.
1461 base.openLoadArchive(path, .{
1462 .preferred_mode = .static,
1463 .search_strategy = .no_fallback,
1464 }) catch |err| switch (err) {
1465 error.LinkFailure => return, // error reported via diags
1466 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1467 };
1468 },
1469 }
1470 }
1471 },
1472 .load_object => |path| {
1473 base.openLoadObject(path) catch |err| switch (err) {
1474 error.LinkFailure => return, // error reported via link_diags
1475 else => |e| comp.link_diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1476 };
1477 },
1478 .load_archive => |path| {
1479 base.openLoadArchive(path, null) catch |err| switch (err) {
1480 error.LinkFailure => return, // error reported via link_diags
1481 else => |e| comp.link_diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1482 };
1483 },
1484 .load_dso => |path| {
1485 base.openLoadDso(path, .{
1486 .preferred_mode = .dynamic,
1487 .search_strategy = .paths_first,
1488 }) catch |err| switch (err) {
1489 error.LinkFailure => return, // error reported via link_diags
1490 else => |e| comp.link_diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1491 };
1492 },
1493 .load_input => |input| {
1494 base.loadInput(input) catch |err| switch (err) {1416 base.loadInput(input) catch |err| switch (err) {
1495 error.LinkFailure => return, // error reported via link_diags1417 error.LinkFailure => return, // error reported via diags
1496 else => |e| {1418 else => |e| switch (input) {
1497 if (input.path()) |path| {1419 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}),
1498 comp.link_diags.addParseError(path, "failed to parse linker input: {s}", .{@errorName(e)});1420 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
1499 } else {1421 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
1500 comp.link_diags.addError("failed to {s}: {s}", .{ input.taskName(), @errorName(e) });1422 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {s}", .{@errorName(e)}),
1501 }1423 .dso_exact => diags.addError("failed to handle dso_exact: {s}", .{@errorName(e)}),
1502 },1424 },
1503 };1425 };
1504 },1426 }
1505 }1427 },
1428 .load_host_libc => if (comp.bin_file) |base| {
1429 const target = comp.root_mod.resolved_target.result;
1430 const flags = target_util.libcFullLinkFlags(target);
1431 const crt_dir = comp.libc_installation.?.crt_dir.?;
1432 const sep = std.fs.path.sep_str;
1433 for (flags) |flag| {
1434 assert(mem.startsWith(u8, flag, "-l"));
1435 const lib_name = flag["-l".len..];
1436 switch (comp.config.link_mode) {
1437 .dynamic => {
1438 const dso_path = Path.initCwd(
1439 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1440 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
1441 }) catch return diags.setAllocFailure(),
1442 );
1443 base.openLoadDso(dso_path, .{
1444 .preferred_mode = .dynamic,
1445 .search_strategy = .paths_first,
1446 }) catch |err| switch (err) {
1447 error.FileNotFound => {
1448 // Also try static.
1449 const archive_path = Path.initCwd(
1450 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1451 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1452 }) catch return diags.setAllocFailure(),
1453 );
1454 base.openLoadArchive(archive_path, .{
1455 .preferred_mode = .dynamic,
1456 .search_strategy = .paths_first,
1457 }) catch |archive_err| switch (archive_err) {
1458 error.LinkFailure => return, // error reported via diags
1459 else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }),
1460 };
1461 },
1462 error.LinkFailure => return, // error reported via diags
1463 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1464 };
1465 },
1466 .static => {
1467 const path = Path.initCwd(
1468 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1469 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1470 }) catch return diags.setAllocFailure(),
1471 );
1472 // glibc sometimes makes even archive files GNU ld scripts.
1473 base.openLoadArchive(path, .{
1474 .preferred_mode = .static,
1475 .search_strategy = .no_fallback,
1476 }) catch |err| switch (err) {
1477 error.LinkFailure => return, // error reported via diags
1478 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1479 };
1480 },
1481 }
1482 }
1483 },
1484 .load_object => |path| if (comp.bin_file) |base| {
1485 base.openLoadObject(path) catch |err| switch (err) {
1486 error.LinkFailure => return, // error reported via diags
1487 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1488 };
1489 },
1490 .load_archive => |path| if (comp.bin_file) |base| {
1491 base.openLoadArchive(path, null) catch |err| switch (err) {
1492 error.LinkFailure => return, // error reported via link_diags
1493 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1494 };
1495 },
1496 .load_dso => |path| if (comp.bin_file) |base| {
1497 base.openLoadDso(path, .{
1498 .preferred_mode = .dynamic,
1499 .search_strategy = .paths_first,
1500 }) catch |err| switch (err) {
1501 error.LinkFailure => return, // error reported via link_diags
1502 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1503 };
1504 },
1505 .load_input => |input| if (comp.bin_file) |base| {
1506 base.loadInput(input) catch |err| switch (err) {
1507 error.LinkFailure => return, // error reported via link_diags
1508 else => |e| {
1509 if (input.path()) |path| {
1510 diags.addParseError(path, "failed to parse linker input: {s}", .{@errorName(e)});
1511 } else {
1512 diags.addError("failed to {s}: {s}", .{ input.taskName(), @errorName(e) });
1513 }
1514 },
1515 };
1516 },
1517 .codegen_nav => |nav_index| {
1518 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1519 pt.linkerUpdateNav(nav_index) catch |err| switch (err) {
1520 error.OutOfMemory => diags.setAllocFailure(),
1521 };
1522 },
1523 .codegen_func => |func| {
1524 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1525 // This call takes ownership of `func.air`.
1526 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) {
1527 error.OutOfMemory => diags.setAllocFailure(),
1528 };
1529 },
1530 .codegen_type => |ty| {
1531 const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
1532 pt.linkerUpdateContainerType(ty) catch |err| switch (err) {
1533 error.OutOfMemory => diags.setAllocFailure(),
1534 };
1535 },
1506 }1536 }
1507};1537}
15081538
1509pub fn spawnLld(1539pub fn spawnLld(
1510 comp: *Compilation,1540 comp: *Compilation,