diff options
Diffstat (limited to 'src/Compilation.zig')
| -rw-r--r-- | src/Compilation.zig | 298 |
1 files changed, 174 insertions, 124 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index 5d304d8159..387a6acd52 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -10,6 +10,7 @@ const Target = std.Target; const ThreadPool = std.Thread.Pool; const WaitGroup = std.Thread.WaitGroup; const ErrorBundle = std.zig.ErrorBundle; +const Path = Cache.Path; const Value = @import("Value.zig"); const Type = @import("Type.zig"); @@ -39,9 +40,9 @@ const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); -pub const Directory = Cache.Directory; -const Path = Cache.Path; +const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; +pub const Directory = Cache.Directory; pub const Config = @import("Compilation/Config.zig"); /// General-purpose allocator. Used for both temporary and long-term storage. @@ -108,6 +109,7 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa } = .{}, link_diags: link.Diags, +link_task_queue: ThreadSafeQueue(link.File.Task) = .empty, work_queues: [ len: { @@ -263,6 +265,9 @@ emit_asm: ?EmitLoc, emit_llvm_ir: ?EmitLoc, emit_llvm_bc: ?EmitLoc, +work_queue_wait_group: WaitGroup = .{}, +work_queue_progress_node: std.Progress.Node = .none, + llvm_opt_bisect_limit: c_int, file_system_inputs: ?*std.ArrayListUnmanaged(u8), @@ -358,9 +363,6 @@ const Job = union(enum) { /// After analysis, a `codegen_func` job will be queued. /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen. analyze_func: InternPool.Index, - /// The source file containing the Decl has been updated, and so the - /// Decl may need its line number information updated in the debug info. - update_line_number: void, // TODO /// The main source file for the module needs to be analyzed. analyze_mod: *Package.Module, /// Fully resolve the given `struct` or `union` type. @@ -374,6 +376,7 @@ const Job = union(enum) { musl_crt_file: musl.CrtFile, /// one of the mingw-w64 static objects mingw_crt_file: mingw.CrtFile, + /// libunwind.a, usually needed when linking libc libunwind: void, libcxx: void, @@ -1769,68 +1772,107 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil } // If we need to build glibc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. - if (comp.wantBuildGLibCFromSource()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + // If linking against host libc installation, instead queue up jobs + // for loading those files in the linker. + if (comp.config.link_libc and is_exe_or_dyn_lib and target.ofmt != .c) { + if (comp.libc_installation) |lci| { + const basenames = LibCInstallation.CrtBasenames.get(.{ + .target = target, + .link_libc = comp.config.link_libc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pie = comp.config.pie, + }); + const paths = try lci.resolveCrtPaths(arena, basenames, target); + + const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; + try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len); + inline for (fields) |field| { + if (@field(paths, field.name)) |path| { + comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); + } + } + + const flags = target_util.libcFullLinkFlags(target); + try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, flags.len); + for (flags) |flag| { + assert(mem.startsWith(u8, flag, "-l")); + const lib_name = flag["-l".len..]; + const suffix = switch (comp.config.link_mode) { + .static => target.staticLibSuffix(), + .dynamic => target.dynamicLibSuffix(), + }; + const sep = std.fs.path.sep_str; + const lib_path = try std.fmt.allocPrint(arena, "{s}" ++ sep ++ "lib{s}{s}", .{ + lci.crt_dir.?, lib_name, suffix, + }); + const resolved_path = Path.initCwd(lib_path); + comp.link_task_queue.shared.appendAssumeCapacity(switch (comp.config.link_mode) { + .static => .{ .load_archive = resolved_path }, + .dynamic => .{ .load_dso = resolved_path }, + }); + } + } else if (target.isMusl() and !target.isWasm()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - if (glibc.needsCrtiCrtn(target)) { + if (musl.needsCrtiCrtn(target)) { + try comp.queueJobs(&[_]Job{ + .{ .musl_crt_file = .crti_o }, + .{ .musl_crt_file = .crtn_o }, + }); + } try comp.queueJobs(&[_]Job{ - .{ .glibc_crt_file = .crti_o }, - .{ .glibc_crt_file = .crtn_o }, + .{ .musl_crt_file = .crt1_o }, + .{ .musl_crt_file = .scrt1_o }, + .{ .musl_crt_file = .rcrt1_o }, + switch (comp.config.link_mode) { + .static => .{ .musl_crt_file = .libc_a }, + .dynamic => .{ .musl_crt_file = .libc_so }, + }, }); - } - try comp.queueJobs(&[_]Job{ - .{ .glibc_crt_file = .scrt1_o }, - .{ .glibc_crt_file = .libc_nonshared_a }, - .{ .glibc_shared_objects = {} }, - }); - } - if (comp.wantBuildMuslFromSource()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + } else if (target.isGnuLibC()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - if (musl.needsCrtiCrtn(target)) { + if (glibc.needsCrtiCrtn(target)) { + try comp.queueJobs(&[_]Job{ + .{ .glibc_crt_file = .crti_o }, + .{ .glibc_crt_file = .crtn_o }, + }); + } try comp.queueJobs(&[_]Job{ - .{ .musl_crt_file = .crti_o }, - .{ .musl_crt_file = .crtn_o }, + .{ .glibc_crt_file = .scrt1_o }, + .{ .glibc_crt_file = .libc_nonshared_a }, + .{ .glibc_shared_objects = {} }, }); - } - try comp.queueJobs(&[_]Job{ - .{ .musl_crt_file = .crt1_o }, - .{ .musl_crt_file = .scrt1_o }, - .{ .musl_crt_file = .rcrt1_o }, - switch (comp.config.link_mode) { - .static => .{ .musl_crt_file = .libc_a }, - .dynamic => .{ .musl_crt_file = .libc_so }, - }, - }); - } + } else if (target.isWasm() and target.os.tag == .wasi) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - if (comp.wantBuildWasiLibcFromSource()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + for (comp.wasi_emulated_libs) |crt_file| { + try comp.queueJob(.{ + .wasi_libc_crt_file = crt_file, + }); + } + try comp.queueJobs(&[_]Job{ + .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, + .{ .wasi_libc_crt_file = .libc_a }, + }); + } else if (target.isMinGW()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - for (comp.wasi_emulated_libs) |crt_file| { - try comp.queueJob(.{ - .wasi_libc_crt_file = crt_file, + const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; + try comp.queueJobs(&.{ + .{ .mingw_crt_file = .mingw32_lib }, + crt_job, }); + + // When linking mingw-w64 there are some import libs we always need. + try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); + for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); + } else { + return error.LibCUnavailable; } - try comp.queueJobs(&[_]Job{ - .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, - .{ .wasi_libc_crt_file = .libc_a }, - }); } - if (comp.wantBuildMinGWFromSource()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - - const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; - try comp.queueJobs(&.{ - .{ .mingw_crt_file = .mingw32_lib }, - crt_job, - }); - - // When linking mingw-w64 there are some import libs we always need. - try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); - for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); - } // Generate Windows import libs. if (target.os.tag == .windows) { const count = comp.windows_libs.count(); @@ -1885,12 +1927,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil { try comp.queueJob(.{ .zig_libc = {} }); } + + try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); } return comp; } pub fn destroy(comp: *Compilation) void { + const gpa = comp.gpa; + if (comp.bin_file) |lf| lf.destroy(); if (comp.zcu) |zcu| zcu.deinit(); comp.cache_use.deinit(); @@ -1901,7 +1947,6 @@ pub fn destroy(comp: *Compilation) void { comp.astgen_work_queue.deinit(); comp.embed_file_work_queue.deinit(); - const gpa = comp.gpa; comp.windows_libs.deinit(gpa); { @@ -3446,6 +3491,9 @@ pub fn performAllTheWork( comp: *Compilation, main_progress_node: std.Progress.Node, ) JobError!void { + comp.work_queue_progress_node = main_progress_node; + defer comp.work_queue_progress_node = .none; + defer if (comp.zcu) |zcu| { zcu.sema_prog_node.end(); zcu.sema_prog_node = std.Progress.Node.none; @@ -3467,12 +3515,20 @@ fn performAllTheWorkInner( // (at least for now) single-threaded main work queue. However, C object compilation // only needs to be finished by the end of this function. - var work_queue_wait_group: WaitGroup = .{}; + const work_queue_wait_group = &comp.work_queue_wait_group; + + work_queue_wait_group.reset(); defer work_queue_wait_group.wait(); + if (comp.bin_file) |lf| { + if (try comp.link_task_queue.enqueue(comp.gpa, &.{.load_explicitly_provided})) { + comp.thread_pool.spawnWg(work_queue_wait_group, link.File.flushTaskQueue, .{ lf, main_progress_node }); + } + } + if (comp.docs_emit != null) { dev.check(.docs_emit); - comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp}); + comp.thread_pool.spawnWg(work_queue_wait_group, workerDocsCopy, .{comp}); work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node }); } @@ -3538,21 +3594,32 @@ fn performAllTheWorkInner( } while (comp.c_object_work_queue.readItem()) |c_object| { - comp.thread_pool.spawnWg(&work_queue_wait_group, workerUpdateCObject, .{ + comp.thread_pool.spawnWg(work_queue_wait_group, workerUpdateCObject, .{ comp, c_object, main_progress_node, }); } while (comp.win32_resource_work_queue.readItem()) |win32_resource| { - comp.thread_pool.spawnWg(&work_queue_wait_group, workerUpdateWin32Resource, .{ + comp.thread_pool.spawnWg(work_queue_wait_group, workerUpdateWin32Resource, .{ comp, win32_resource, main_progress_node, }); } } - if (comp.job_queued_compiler_rt_lib) work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node }); - if (comp.job_queued_compiler_rt_obj) work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node }); - if (comp.job_queued_fuzzer_lib) work_queue_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node }); + if (comp.job_queued_compiler_rt_lib) { + comp.job_queued_compiler_rt_lib = false; + work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Lib, &comp.compiler_rt_lib, main_progress_node }); + } + + if (comp.job_queued_compiler_rt_obj) { + comp.job_queued_compiler_rt_obj = false; + work_queue_wait_group.spawnManager(buildRt, .{ comp, "compiler_rt.zig", .compiler_rt, .Obj, &comp.compiler_rt_obj, main_progress_node }); + } + + if (comp.job_queued_fuzzer_lib) { + comp.job_queued_fuzzer_lib = false; + work_queue_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node }); + } if (comp.zcu) |zcu| { const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main }; @@ -3570,7 +3637,7 @@ fn performAllTheWorkInner( if (!InternPool.single_threaded) { comp.codegen_work.done = false; // may be `true` from a prior update - comp.thread_pool.spawnWgId(&work_queue_wait_group, codegenThread, .{comp}); + comp.thread_pool.spawnWgId(work_queue_wait_group, codegenThread, .{comp}); } defer if (!InternPool.single_threaded) { { @@ -3679,31 +3746,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre error.AnalysisFail => return, }; }, - .update_line_number => |decl_index| { - const named_frame = tracy.namedFrame("update_line_number"); - defer named_frame.end(); - - if (true) @panic("TODO: update_line_number"); - - const gpa = comp.gpa; - const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) }; - const decl = pt.zcu.declPtr(decl_index); - const lf = comp.bin_file.?; - lf.updateDeclLineNumber(pt, decl_index) catch |err| { - try pt.zcu.failed_analysis.ensureUnusedCapacity(gpa, 1); - pt.zcu.failed_analysis.putAssumeCapacityNoClobber( - InternPool.AnalUnit.wrap(.{ .decl = decl_index }), - try Zcu.ErrorMsg.create( - gpa, - decl.navSrcLoc(pt.zcu), - "unable to update line number: {s}", - .{@errorName(err)}, - ), - ); - decl.analysis = .codegen_failure; - try pt.zcu.retryable_failures.append(gpa, InternPool.AnalUnit.wrap(.{ .decl = decl_index })); - }; - }, .analyze_mod => |mod| { const named_frame = tracy.namedFrame("analyze_mod"); defer named_frame.end(); @@ -4920,7 +4962,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr // the contents were the same, we hit the cache but the manifest is dirty and we need to update // it to prevent doing a full file content comparison the next time around. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); + log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ + c_object.src.src_path, @errorName(err), + }); }; } @@ -4935,6 +4979,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr .lock = man.toOwnedLock(), }, }; + + comp.enqueueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); } fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void { @@ -6058,35 +6104,6 @@ fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []cons return crt_file.full_object_path; } -fn wantBuildLibCFromSource(comp: Compilation) bool { - const is_exe_or_dyn_lib = switch (comp.config.output_mode) { - .Obj => false, - .Lib => comp.config.link_mode == .dynamic, - .Exe => true, - }; - const ofmt = comp.root_mod.resolved_target.result.ofmt; - return comp.config.link_libc and is_exe_or_dyn_lib and - comp.libc_installation == null and ofmt != .c; -} - -fn wantBuildGLibCFromSource(comp: Compilation) bool { - return comp.wantBuildLibCFromSource() and comp.getTarget().isGnuLibC(); -} - -fn wantBuildMuslFromSource(comp: Compilation) bool { - return comp.wantBuildLibCFromSource() and comp.getTarget().isMusl() and - !comp.getTarget().isWasm(); -} - -fn wantBuildWasiLibcFromSource(comp: Compilation) bool { - return comp.wantBuildLibCFromSource() and comp.getTarget().isWasm() and - comp.getTarget().os.tag == .wasi; -} - -fn wantBuildMinGWFromSource(comp: Compilation) bool { - return comp.wantBuildLibCFromSource() and comp.getTarget().isMinGW(); -} - fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { const is_exe_or_dyn_lib = switch (comp.config.output_mode) { .Obj => false, @@ -6334,9 +6351,11 @@ fn buildOutputFromZig( try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); - // Under incremental compilation, `out` may already be populated from a prior update. - assert(out.* == null or comp.incremental); - out.* = try sub_compilation.toCrtFile(); + const crt_file = try sub_compilation.toCrtFile(); + assert(out.* == null); + out.* = crt_file; + + comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); } pub fn build_crt_file( @@ -6443,8 +6462,39 @@ pub fn build_crt_file( try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); - try comp.crt_files.ensureUnusedCapacity(gpa, 1); - comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); + const crt_file = try sub_compilation.toCrtFile(); + comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + + { + comp.mutex.lock(); + defer comp.mutex.unlock(); + try comp.crt_files.ensureUnusedCapacity(gpa, 1); + comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file); + } +} + +pub fn enqueueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void { + comp.enqueueLinkTasks(switch (output_mode) { + .Exe => unreachable, + .Obj => &.{.{ .load_object = path }}, + .Lib => &.{.{ .load_archive = path }}, + }); +} + +/// Only valid to call during `update`. Automatically handles queuing up a +/// linker worker task if there is not already one. +fn enqueueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void { + const use_lld = build_options.have_llvm and comp.config.use_lld; + if (use_lld) return; + const target = comp.root_mod.resolved_target.result; + if (target.ofmt != .elf) return; + if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) { + error.OutOfMemory => return comp.setAllocFailure(), + }) { + comp.thread_pool.spawnWg(&comp.work_queue_wait_group, link.File.flushTaskQueue, .{ + comp.bin_file.?, comp.work_queue_progress_node, + }); + } } pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { |
