| author | |
| committer | |
| log | ba71079837a071b53cab289d78e5bacb4925fd25 |
| tree | 1b1096800e87c4128e09098b467b05f06cdd1cb3 |
| parent | 9a511b4b273ad4b7ad9289e18de87421ee47b626 |
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 | } |
| 16 | 16 | ||
| 17 | pub 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 | |||
| 17 | pub fn finish(self: *WaitGroup) void { | 22 | pub 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 | } = .{}, |
| 112 | 112 | ||
| 113 | link_diags: link.Diags, | 113 | link_diags: link.Diags, |
| 114 | link_task_queue: ThreadSafeQueue(link.File.Task) = .empty, | 114 | link_task_queue: ThreadSafeQueue(link.Task) = .empty, |
| 115 | /// Ensure only 1 simultaneous call to `flushTaskQueue`. | ||
| 116 | link_task_queue_safety: std.debug.SafetyLock = .{}, | ||
| 115 | 117 | ||
| 116 | work_queues: [ | 118 | work_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), |
| 125 | 127 | ||
| 126 | codegen_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, which | 128 | /// 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. |
| 136 | c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), | 130 | c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), |
| ... | @@ -267,7 +261,7 @@ emit_asm: ?EmitLoc, | ... | @@ -267,7 +261,7 @@ emit_asm: ?EmitLoc, |
| 267 | emit_llvm_ir: ?EmitLoc, | 261 | emit_llvm_ir: ?EmitLoc, |
| 268 | emit_llvm_bc: ?EmitLoc, | 262 | emit_llvm_bc: ?EmitLoc, |
| 269 | 263 | ||
| 270 | work_queue_wait_group: WaitGroup = .{}, | 264 | link_task_wait_group: WaitGroup = .{}, |
| 271 | work_queue_progress_node: std.Progress.Node = .none, | 265 | work_queue_progress_node: std.Progress.Node = .none, |
| 272 | 266 | ||
| 273 | llvm_opt_bisect_limit: c_int, | 267 | llvm_opt_bisect_limit: c_int, |
| ... | @@ -347,16 +341,14 @@ pub const RcIncludes = enum { | ... | @@ -347,16 +341,14 @@ pub const RcIncludes = enum { |
| 347 | }; | 341 | }; |
| 348 | 342 | ||
| 349 | const Job = union(enum) { | 343 | const 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 | }; |
| 410 | 402 | ||
| 411 | const 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 | |||
| 422 | pub const CObject = struct { | 403 | pub 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 | } |
| 3490 | 3462 | ||
| 3491 | fn performAllTheWorkInner( | 3463 | fn 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 compilation | 3469 | // (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. |
| 3499 | 3471 | ||
| 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(); |
| 3504 | 3474 | ||
| 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 | } |
| 3510 | 3481 | ||
| 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 | } |
| 3516 | 3487 | ||
| 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 | } |
| 3521 | 3492 | ||
| 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 | } |
| 3526 | 3497 | ||
| 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 | } |
| 3531 | 3502 | ||
| 3532 | { | 3503 | { |
| ... | @@ -3591,13 +3562,13 @@ fn performAllTheWorkInner( | ... | @@ -3591,13 +3562,13 @@ fn performAllTheWorkInner( |
| 3591 | } | 3562 | } |
| 3592 | 3563 | ||
| 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 | } |
| 3598 | 3569 | ||
| 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 | } |
| 3619 | 3590 | ||
| 3620 | if (!InternPool.single_threaded) { | 3591 | if (!comp.separateCodegenThreadOk()) { |
| 3621 | comp.codegen_work.done = false; // may be `true` from a prior update | 3592 | // 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 | }; | ||
| 3632 | 3597 | ||
| 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 | } |
| 3896 | 3859 | ||
| 3897 | fn 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 or | 3861 | /// resolve_type_fully tasks are complete before this dispatch function is called. |
| 3899 | !comp.zcu.?.backendSupportsFeature(.separate_thread)) | 3862 | fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void { |
| 3900 | return processOneCodegenJob(tid, comp, codegen_job); | 3863 | if (comp.separateCodegenThreadOk()) { |
| 3901 | 3864 | 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 | |||
| 3910 | fn 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 | } |
| 3931 | 3869 | ||
| 3932 | fn processOneCodegenJob(tid: usize, comp: *Compilation, codegen_job: CodegenJob) JobError!void { | 3870 | fn 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 | } |
| 3958 | 3875 | ||
| 3959 | fn workerDocsCopy(comp: *Compilation) void { | 3876 | fn 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 |
| 6465 | 6382 | ||
| 6466 | /// Only valid to call during `update`. Automatically handles queuing up a | 6383 | /// 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. |
| 6468 | pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void { | 6385 | pub 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 | } |
| 6481 | 6392 |
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 | } |
| 847 | 847 | ||
| 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 | } |
| 1364 | 1366 | ||
| 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 | } |
| 1367 | 1370 | ||
| ... | @@ -2593,7 +2596,7 @@ pub fn populateTestFunctions( | ... | @@ -2593,7 +2596,7 @@ pub fn populateTestFunctions( |
| 2593 | } | 2596 | } |
| 2594 | } | 2597 | } |
| 2595 | 2598 | ||
| 2596 | pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { | 2599 | pub 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. |
| 3163 | pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index { | 3166 | pub 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; |
| 1224 | 1224 | ||
| 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; |
| 1227 | 1227 | ||
| 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, |
| 372 | 372 | ||
| 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 | } |
| 1086 | 1083 | ||
| 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 | }; | ||
| 1363 | 1363 | ||
| 1364 | /// Does all the tasks in the queue. Runs in exactly one separate thread | 1364 | /// Does all the tasks in the queue. Runs in exactly one separate thread |
| 1365 | /// from the rest of compilation. All tasks performed here are | 1365 | /// 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 { | 1367 | pub 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 | } | ||
| 1377 | 1376 | ||
| 1378 | pub const Task = union(enum) { | 1377 | pub const Task = union(enum) { |
| 1379 | /// Loads the objects, shared objects, and archives that are already | 1378 | /// 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 resolving | 1381 | /// Loads the shared objects and archives by resolving |
| 1383 | /// `target_util.libcFullLinkFlags()` against the host libc | 1382 | /// `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 a | 1389 | /// 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 | }; | ||
| 1397 | 1410 | ||
| 1398 | fn doTask(base: *File, task: Task) void { | 1411 | pub 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_diags | 1417 | 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 | } |
| 1508 | 1538 | ||
| 1509 | pub fn spawnLld( | 1539 | pub fn spawnLld( |
| 1510 | comp: *Compilation, | 1540 | comp: *Compilation, |