aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig685
1 files changed, 297 insertions, 388 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 2776d2960a..fac1ad4baa 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,15 +40,17 @@ 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.
gpa: Allocator,
/// Arena-allocated memory, mostly used during initialization. However, it can
/// be used for other things requiring the same lifetime as the `Compilation`.
+/// Not thread-safe - lock `mutex` if potentially accessing from multiple
+/// threads at once.
arena: Allocator,
/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
zcu: ?*Zcu,
@@ -76,12 +79,13 @@ implib_emit: ?Path,
docs_emit: ?Path,
root_name: [:0]const u8,
include_compiler_rt: bool,
-objects: []Compilation.LinkObject,
+/// Resolved into known paths, any GNU ld scripts already resolved.
+link_inputs: []const link.Input,
/// Needed only for passing -F args to clang.
framework_dirs: []const []const u8,
-/// These are *always* dynamically linked. Static libraries will be
-/// provided as positional arguments.
-system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
+/// These are only for DLLs dependencies fulfilled by the `.def` files shipped
+/// with Zig. Static libraries are provided as `link.Input` values.
+windows_libs: std.StringArrayHashMapUnmanaged(void),
version: ?std.SemanticVersion,
libc_installation: ?*const LibCInstallation,
skip_linker_dependencies: bool,
@@ -107,6 +111,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
} = .{},
link_diags: link.Diags,
+link_task_queue: ThreadSafeQueue(link.Task) = .empty,
+/// Ensure only 1 simultaneous call to `flushTaskQueue`.
+link_task_queue_safety: std.debug.SafetyLock = .{},
work_queues: [
len: {
@@ -118,14 +125,6 @@ work_queues: [
}
]std.fifo.LinearFifo(Job, .Dynamic),
-codegen_work: if (InternPool.single_threaded) void else struct {
- mutex: std.Thread.Mutex,
- cond: std.Thread.Condition,
- queue: std.fifo.LinearFifo(CodegenJob, .Dynamic),
- job_error: ?JobError,
- done: bool,
-},
-
/// These jobs are to invoke the Clang compiler to create an object file, which
/// gets linked with the Compilation.
c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
@@ -262,6 +261,9 @@ emit_asm: ?EmitLoc,
emit_llvm_ir: ?EmitLoc,
emit_llvm_bc: ?EmitLoc,
+link_task_wait_group: WaitGroup = .{},
+work_queue_progress_node: std.Progress.Node = .none,
+
llvm_opt_bisect_limit: c_int,
file_system_inputs: ?*std.ArrayListUnmanaged(u8),
@@ -339,16 +341,14 @@ pub const RcIncludes = enum {
};
const Job = union(enum) {
- /// Write the constant value for a Decl to the output file.
+ /// Corresponds to the task in `link.Task`.
+ /// Only needed for backends that haven't yet been updated to not race against Sema.
codegen_nav: InternPool.Nav.Index,
- /// Write the machine code for a function to the output file.
- codegen_func: struct {
- /// This will either be a non-generic `func_decl` or a `func_instance`.
- func: InternPool.Index,
- /// This `Air` is owned by the `Job` and allocated with `gpa`.
- /// It must be deinited when the job is processed.
- air: Air,
- },
+ /// Corresponds to the task in `link.Task`.
+ /// Only needed for backends that haven't yet been updated to not race against Sema.
+ codegen_func: link.Task.CodegenFunc,
+ /// Corresponds to the task in `link.Task`.
+ /// Only needed for backends that haven't yet been updated to not race against Sema.
codegen_type: InternPool.Index,
/// The `Cau` must be semantically analyzed (and possibly export itself).
/// This may be its first time being analyzed, or it may be outdated.
@@ -357,9 +357,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.
@@ -373,6 +370,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,
@@ -384,7 +382,7 @@ const Job = union(enum) {
/// one of WASI libc static objects
wasi_libc_crt_file: wasi_libc.CrtFile,
- /// The value is the index into `system_libs`.
+ /// The value is the index into `windows_libs`.
windows_import_lib: usize,
const Tag = @typeInfo(Job).@"union".tag_type.?;
@@ -402,17 +400,6 @@ const Job = union(enum) {
}
};
-const CodegenJob = union(enum) {
- nav: InternPool.Nav.Index,
- func: struct {
- func: InternPool.Index,
- /// This `Air` is owned by the `Job` and allocated with `gpa`.
- /// It must be deinited when the job is processed.
- air: Air,
- },
- type: InternPool.Index,
-};
-
pub const CObject = struct {
/// Relative to cwd. Owned by arena.
src: CSourceFile,
@@ -999,24 +986,6 @@ const CacheUse = union(CacheMode) {
}
};
-pub const LinkObject = struct {
- path: Path,
- must_link: bool = false,
- needed: bool = false,
- // When the library is passed via a positional argument, it will be
- // added as a full path. If it's `-l<lib>`, then just the basename.
- //
- // Consistent with `withLOption` variable name in lld ELF driver.
- loption: bool = false,
-
- pub fn isObject(lo: LinkObject) bool {
- return switch (classifyFileExt(lo.path.sub_path)) {
- .object => true,
- else => false,
- };
- }
-};
-
pub const CreateOptions = struct {
zig_lib_directory: Directory,
local_cache_directory: Directory,
@@ -1061,18 +1030,20 @@ pub const CreateOptions = struct {
/// this flag would be set to disable this machinery to avoid false positives.
disable_lld_caching: bool = false,
cache_mode: CacheMode = .incremental,
- lib_dirs: []const []const u8 = &[0][]const u8{},
+ /// This field is intended to be removed.
+ /// The ELF implementation no longer uses this data, however the MachO and COFF
+ /// implementations still do.
+ lib_directories: []const Directory = &.{},
rpath_list: []const []const u8 = &[0][]const u8{},
symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
c_source_files: []const CSourceFile = &.{},
rc_source_files: []const RcSourceFile = &.{},
manifest_file: ?[]const u8 = null,
rc_includes: RcIncludes = .any,
- link_objects: []LinkObject = &[0]LinkObject{},
+ link_inputs: []const link.Input = &.{},
framework_dirs: []const []const u8 = &[0][]const u8{},
frameworks: []const Framework = &.{},
- system_lib_names: []const []const u8 = &.{},
- system_lib_infos: []const SystemLib = &.{},
+ windows_lib_names: []const []const u8 = &.{},
/// These correspond to the WASI libc emulated subcomponents including:
/// * process clocks
/// * getpid
@@ -1455,12 +1426,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
};
errdefer if (opt_zcu) |zcu| zcu.deinit();
- var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(
- gpa,
- options.system_lib_names,
- options.system_lib_infos,
- );
- errdefer system_libs.deinit(gpa);
+ var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
+ errdefer windows_libs.deinit(gpa);
comp.* = .{
.gpa = gpa,
@@ -1479,13 +1446,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.emit_llvm_ir = options.emit_llvm_ir,
.emit_llvm_bc = options.emit_llvm_bc,
.work_queues = .{std.fifo.LinearFifo(Job, .Dynamic).init(gpa)} ** @typeInfo(std.meta.FieldType(Compilation, .work_queues)).array.len,
- .codegen_work = if (InternPool.single_threaded) {} else .{
- .mutex = .{},
- .cond = .{},
- .queue = std.fifo.LinearFifo(CodegenJob, .Dynamic).init(gpa),
- .job_error = null,
- .done = false,
- },
.c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
.win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{},
.astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa),
@@ -1522,11 +1482,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.libcxx_abi_version = options.libcxx_abi_version,
.root_name = root_name,
.sysroot = sysroot,
- .system_libs = system_libs,
+ .windows_libs = windows_libs,
.version = options.version,
.libc_installation = libc_dirs.libc_installation,
.include_compiler_rt = include_compiler_rt,
- .objects = options.link_objects,
+ .link_inputs = options.link_inputs,
.framework_dirs = options.framework_dirs,
.llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
.skip_linker_dependencies = options.skip_linker_dependencies,
@@ -1564,7 +1524,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.z_max_page_size = options.linker_z_max_page_size,
.darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
.frameworks = options.frameworks,
- .lib_dirs = options.lib_dirs,
+ .lib_directories = options.lib_directories,
.framework_dirs = options.framework_dirs,
.rpath_list = options.rpath_list,
.symbol_wrap_set = options.symbol_wrap_set,
@@ -1776,157 +1736,175 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
.incremental => comp.bin_file != null,
};
- if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {
- if (target.isDarwin()) {
- switch (target.abi) {
- .none,
- .simulator,
- .macabi,
- => {},
- else => return error.LibCUnavailable,
- }
- }
- // 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 (glibc.needsCrtiCrtn(target)) {
- try comp.queueJobs(&[_]Job{
- .{ .glibc_crt_file = .crti_o },
- .{ .glibc_crt_file = .crtn_o },
- });
- }
- 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;
-
- if (musl.needsCrtiCrtn(target)) {
- try comp.queueJobs(&[_]Job{
- .{ .musl_crt_file = .crti_o },
- .{ .musl_crt_file = .crtn_o },
- });
- }
- 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 },
- },
- });
- }
+ if (have_bin_emit and target.ofmt != .c) {
+ if (!comp.skip_linker_dependencies) {
+ // If we need to build libc for the target, add work items for it.
+ // We go through the work queue so that building can be done in parallel.
+ // 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) {
+ // If the "is darwin" check is moved below the libc_installation check below,
+ // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
+ if (target.isDarwin()) {
+ switch (target.abi) {
+ .none, .simulator, .macabi => {},
+ else => return error.LibCUnavailable,
+ }
+ // TODO delete logic from MachO flush() and queue up tasks here instead.
+ } else 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);
- if (comp.wantBuildWasiLibcFromSource()) {
- if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+ const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
+ try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len + 1);
+ inline for (fields) |field| {
+ if (@field(paths, field.name)) |path| {
+ comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path });
+ }
+ }
+ // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
+ comp.link_task_queue.shared.appendAssumeCapacity(.load_host_libc);
+ } else if (target.isMusl() and !target.isWasm()) {
+ if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+
+ if (musl.needsCrtiCrtn(target)) {
+ try comp.queueJobs(&[_]Job{
+ .{ .musl_crt_file = .crti_o },
+ .{ .musl_crt_file = .crtn_o },
+ });
+ }
+ if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
+ try comp.queueJobs(&.{.{ .musl_crt_file = f }});
+ }
+ try comp.queueJobs(&.{.{ .musl_crt_file = switch (comp.config.link_mode) {
+ .static => .libc_a,
+ .dynamic => .libc_so,
+ } }});
+ } else if (target.isGnuLibC()) {
+ if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+
+ if (glibc.needsCrtiCrtn(target)) {
+ try comp.queueJobs(&[_]Job{
+ .{ .glibc_crt_file = .crti_o },
+ .{ .glibc_crt_file = .crtn_o },
+ });
+ }
+ if (!is_dyn_lib) {
+ try comp.queueJob(.{ .glibc_crt_file = .scrt1_o });
+ }
+ try comp.queueJobs(&[_]Job{
+ .{ .glibc_shared_objects = {} },
+ .{ .glibc_crt_file = .libc_nonshared_a },
+ });
+ } else if (target.isWasm() and target.os.tag == .wasi) {
+ 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 },
- });
- }
+ 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;
- 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,
+ });
- 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 if (target.isDarwin()) {
+ switch (target.abi) {
+ .none, .simulator, .macabi => {},
+ else => return error.LibCUnavailable,
+ }
+ } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) {
+ try comp.queueJob(.{ .zig_libc = {} });
+ } else {
+ return error.LibCUnavailable;
+ }
+ }
- // When linking mingw-w64 there are some import libs we always need.
- for (mingw.always_link_libs) |name| {
- try comp.system_libs.put(comp.gpa, name, .{
- .needed = false,
- .weak = false,
- .path = null,
- });
+ // Generate Windows import libs.
+ if (target.os.tag == .windows) {
+ const count = comp.windows_libs.count();
+ for (0..count) |i| {
+ try comp.queueJob(.{ .windows_import_lib = i });
+ }
}
- }
- // Generate Windows import libs.
- if (target.os.tag == .windows) {
- const count = comp.system_libs.count();
- for (0..count) |i| {
- try comp.queueJob(.{ .windows_import_lib = i });
+ if (comp.wantBuildLibUnwindFromSource()) {
+ try comp.queueJob(.{ .libunwind = {} });
+ }
+ if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
+ try comp.queueJob(.libcxx);
+ try comp.queueJob(.libcxxabi);
+ }
+ if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
+ try comp.queueJob(.libtsan);
}
- }
- if (comp.wantBuildLibUnwindFromSource()) {
- try comp.queueJob(.{ .libunwind = {} });
- }
- if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
- try comp.queueJob(.libcxx);
- try comp.queueJob(.libcxxabi);
- }
- if (build_options.have_llvm and comp.config.any_sanitize_thread) {
- try comp.queueJob(.libtsan);
- }
- if (target.isMinGW() and comp.config.any_non_single_threaded) {
- // LLD might drop some symbols as unused during LTO and GCing, therefore,
- // we force mark them for resolution here.
+ if (target.isMinGW() and comp.config.any_non_single_threaded) {
+ // LLD might drop some symbols as unused during LTO and GCing, therefore,
+ // we force mark them for resolution here.
- const tls_index_sym = switch (target.cpu.arch) {
- .x86 => "__tls_index",
- else => "_tls_index",
- };
+ const tls_index_sym = switch (target.cpu.arch) {
+ .x86 => "__tls_index",
+ else => "_tls_index",
+ };
- try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
- }
+ try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
+ }
- if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
- if (is_exe_or_dyn_lib) {
- log.debug("queuing a job to build compiler_rt_lib", .{});
- comp.job_queued_compiler_rt_lib = true;
- } else if (output_mode != .Obj) {
- log.debug("queuing a job to build compiler_rt_obj", .{});
- // In this case we are making a static library, so we ask
- // for a compiler-rt object to put in it.
- comp.job_queued_compiler_rt_obj = true;
+ if (comp.include_compiler_rt and capable_of_building_compiler_rt) {
+ if (is_exe_or_dyn_lib) {
+ log.debug("queuing a job to build compiler_rt_lib", .{});
+ comp.job_queued_compiler_rt_lib = true;
+ } else if (output_mode != .Obj) {
+ log.debug("queuing a job to build compiler_rt_obj", .{});
+ // In this case we are making a static library, so we ask
+ // for a compiler-rt object to put in it.
+ comp.job_queued_compiler_rt_obj = true;
+ }
}
- }
- if (comp.config.any_fuzz and capable_of_building_compiler_rt) {
- if (is_exe_or_dyn_lib) {
+ if (is_exe_or_dyn_lib and comp.config.any_fuzz and capable_of_building_compiler_rt) {
log.debug("queuing a job to build libfuzzer", .{});
comp.job_queued_fuzzer_lib = true;
}
}
- if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and
- !comp.config.link_libc and capable_of_building_zig_libc)
- {
- 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();
for (comp.work_queues) |work_queue| work_queue.deinit();
- if (!InternPool.single_threaded) comp.codegen_work.queue.deinit();
comp.c_object_work_queue.deinit();
comp.win32_resource_work_queue.deinit();
comp.astgen_work_queue.deinit();
comp.embed_file_work_queue.deinit();
- const gpa = comp.gpa;
- comp.system_libs.deinit(gpa);
+ comp.windows_libs.deinit(gpa);
{
var it = comp.crt_files.iterator();
@@ -2559,12 +2537,7 @@ fn addNonIncrementalStuffToCacheManifest(
cache_helpers.addModule(&man.hash, comp.root_mod);
}
- for (comp.objects) |obj| {
- _ = try man.addFilePath(obj.path, null);
- man.hash.add(obj.must_link);
- man.hash.add(obj.needed);
- man.hash.add(obj.loption);
- }
+ try link.hashInputs(man, comp.link_inputs);
for (comp.c_object_table.keys()) |key| {
_ = try man.addFile(key.src.src_path, null);
@@ -2601,7 +2574,7 @@ fn addNonIncrementalStuffToCacheManifest(
man.hash.add(comp.rc_includes);
man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
man.hash.addListOfBytes(comp.framework_dirs);
- try link.hashAddSystemLibs(man, comp.system_libs);
+ man.hash.addListOfBytes(comp.windows_libs.keys());
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
@@ -2620,12 +2593,16 @@ fn addNonIncrementalStuffToCacheManifest(
man.hash.addOptional(opts.image_base);
man.hash.addOptional(opts.gc_sections);
man.hash.add(opts.emit_relocs);
- man.hash.addListOfBytes(opts.lib_dirs);
+ const target = comp.root_mod.resolved_target.result;
+ if (target.ofmt == .macho or target.ofmt == .coff) {
+ // TODO remove this, libraries need to be resolved by the frontend. this is already
+ // done by ELF.
+ for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
+ }
man.hash.addListOfBytes(opts.rpath_list);
man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
if (comp.config.link_libc) {
man.hash.add(comp.libc_installation != null);
- const target = comp.root_mod.resolved_target.result;
if (comp.libc_installation) |libc_installation| {
man.hash.addOptionalBytes(libc_installation.crt_dir);
if (target.abi == .msvc or target.abi == .itanium) {
@@ -3219,18 +3196,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
}));
}
- for (comp.link_diags.msgs.items) |link_err| {
- try bundle.addRootErrorMessage(.{
- .msg = try bundle.addString(link_err.msg),
- .notes_len = @intCast(link_err.notes.len),
- });
- const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
- for (link_err.notes, 0..) |note, i| {
- bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
- .msg = try bundle.addString(note.msg),
- }));
- }
- }
+ try comp.link_diags.addMessagesToBundle(&bundle);
if (comp.zcu) |zcu| {
if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
@@ -3482,6 +3448,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;
@@ -3491,7 +3460,6 @@ pub fn performAllTheWork(
zcu.generation += 1;
};
try comp.performAllTheWorkInner(main_progress_node);
- if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
}
fn performAllTheWorkInner(
@@ -3506,12 +3474,34 @@ fn performAllTheWorkInner(
var work_queue_wait_group: WaitGroup = .{};
defer work_queue_wait_group.wait();
+ comp.link_task_wait_group.reset();
+ defer comp.link_task_wait_group.wait();
+
+ if (comp.link_task_queue.start()) {
+ comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
+ }
+
if (comp.docs_emit != null) {
dev.check(.docs_emit);
comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
}
+ if (comp.job_queued_compiler_rt_lib) {
+ comp.job_queued_compiler_rt_lib = false;
+ comp.link_task_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;
+ comp.link_task_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;
+ comp.link_task_wait_group.spawnManager(buildRt, .{ comp, "fuzzer.zig", .libfuzzer, .Lib, &comp.fuzzer_lib, main_progress_node });
+ }
+
{
const astgen_frame = tracy.namedFrame("astgen");
defer astgen_frame.end();
@@ -3574,22 +3564,18 @@ fn performAllTheWorkInner(
}
while (comp.c_object_work_queue.readItem()) |c_object| {
- comp.thread_pool.spawnWg(&work_queue_wait_group, workerUpdateCObject, .{
+ comp.thread_pool.spawnWg(&comp.link_task_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(&comp.link_task_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.zcu) |zcu| {
const pt: Zcu.PerThread = .{ .zcu = zcu, .tid = .main };
if (comp.incremental) {
@@ -3604,18 +3590,12 @@ fn performAllTheWorkInner(
zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
}
- 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});
+ if (!comp.separateCodegenThreadOk()) {
+ // Waits until all input files have been parsed.
+ comp.link_task_wait_group.wait();
+ comp.link_task_wait_group.reset();
+ std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{});
}
- defer if (!InternPool.single_threaded) {
- {
- comp.codegen_work.mutex.lock();
- defer comp.codegen_work.mutex.unlock();
- comp.codegen_work.done = true;
- }
- comp.codegen_work.cond.signal();
- };
work: while (true) {
for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {
@@ -3659,16 +3639,14 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
}
}
assert(nav.status == .resolved);
- try comp.queueCodegenJob(tid, .{ .nav = nav_index });
+ comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
},
.codegen_func => |func| {
- // This call takes ownership of `func.air`.
- try comp.queueCodegenJob(tid, .{ .func = .{
- .func = func.func,
- .air = func.air,
- } });
+ comp.dispatchCodegenTask(tid, .{ .codegen_func = func });
+ },
+ .codegen_type => |ty| {
+ comp.dispatchCodegenTask(tid, .{ .codegen_type = ty });
},
- .codegen_type => |ty| try comp.queueCodegenJob(tid, .{ .type = ty }),
.analyze_func => |func| {
const named_frame = tracy.namedFrame("analyze_func");
defer named_frame.end();
@@ -3715,31 +3693,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();
@@ -3804,7 +3757,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const named_frame = tracy.namedFrame("windows_import_lib");
defer named_frame.end();
- const link_lib = comp.system_libs.keys()[index];
+ const link_lib = comp.windows_libs.keys()[index];
mingw.buildImportLib(comp, link_lib) catch |err| {
// TODO Surface more error details.
comp.lockAndSetMiscFailure(
@@ -3906,66 +3859,20 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
}
}
-fn queueCodegenJob(comp: *Compilation, tid: usize, codegen_job: CodegenJob) !void {
- if (InternPool.single_threaded or
- !comp.zcu.?.backendSupportsFeature(.separate_thread))
- return processOneCodegenJob(tid, comp, codegen_job);
-
- {
- comp.codegen_work.mutex.lock();
- defer comp.codegen_work.mutex.unlock();
- try comp.codegen_work.queue.writeItem(codegen_job);
+/// The reason for the double-queue here is that the first queue ensures any
+/// resolve_type_fully tasks are complete before this dispatch function is called.
+fn dispatchCodegenTask(comp: *Compilation, tid: usize, link_task: link.Task) void {
+ if (comp.separateCodegenThreadOk()) {
+ comp.queueLinkTasks(&.{link_task});
+ } else {
+ link.doTask(comp, tid, link_task);
}
- comp.codegen_work.cond.signal();
}
-fn codegenThread(tid: usize, comp: *Compilation) void {
- comp.codegen_work.mutex.lock();
- defer comp.codegen_work.mutex.unlock();
-
- while (true) {
- if (comp.codegen_work.queue.readItem()) |codegen_job| {
- comp.codegen_work.mutex.unlock();
- defer comp.codegen_work.mutex.lock();
-
- processOneCodegenJob(tid, comp, codegen_job) catch |job_error| {
- comp.codegen_work.job_error = job_error;
- break;
- };
- continue;
- }
-
- if (comp.codegen_work.done) break;
-
- comp.codegen_work.cond.wait(&comp.codegen_work.mutex);
- }
-}
-
-fn processOneCodegenJob(tid: usize, comp: *Compilation, codegen_job: CodegenJob) JobError!void {
- switch (codegen_job) {
- .nav => |nav_index| {
- const named_frame = tracy.namedFrame("codegen_nav");
- defer named_frame.end();
-
- const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
- try pt.linkerUpdateNav(nav_index);
- },
- .func => |func| {
- const named_frame = tracy.namedFrame("codegen_func");
- defer named_frame.end();
-
- const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
- // This call takes ownership of `func.air`.
- try pt.linkerUpdateFunc(func.func, func.air);
- },
- .type => |ty| {
- const named_frame = tracy.namedFrame("codegen_type");
- defer named_frame.end();
-
- const pt: Zcu.PerThread = .{ .zcu = comp.zcu.?, .tid = @enumFromInt(tid) };
- try pt.linkerUpdateContainerType(ty);
- },
- }
+fn separateCodegenThreadOk(comp: *const Compilation) bool {
+ if (InternPool.single_threaded) return false;
+ const zcu = comp.zcu orelse return true;
+ return zcu.backendSupportsFeature(.separate_thread);
}
fn workerDocsCopy(comp: *Compilation) void {
@@ -4717,7 +4624,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
// file and building an object we need to link them together, but with just one it should go
// directly to the output file.
const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
- comp.config.output_mode == .Obj and comp.objects.len == 0;
+ comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
const o_basename_noext = if (direct_o)
comp.root_name
else
@@ -4956,7 +4863,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),
+ });
};
}
@@ -4971,6 +4880,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
.lock = man.toOwnedLock(),
},
};
+
+ comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
}
fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
@@ -6075,8 +5986,8 @@ test "classifyFileExt" {
try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
}
-pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
- return (try crtFilePath(comp, basename)) orelse {
+fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
+ return (try crtFilePath(&comp.crt_files, basename)) orelse {
const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
@@ -6089,40 +6000,11 @@ pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u
return path.toString(arena);
}
-pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path {
- const crt_file = comp.crt_files.get(basename) orelse return null;
+fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Path {
+ const crt_file = crt_files.get(basename) orelse return null;
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,
@@ -6133,7 +6015,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
}
-fn setAllocFailure(comp: *Compilation) void {
+pub fn setAllocFailure(comp: *Compilation) void {
@branchHint(.cold);
log.debug("memory allocation failure", .{});
comp.alloc_failure_occurred = true;
@@ -6370,9 +6252,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.queueLinkTaskMode(crt_file.full_object_path, output_mode);
}
pub fn build_crt_file(
@@ -6479,8 +6363,33 @@ 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.queueLinkTaskMode(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 queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void {
+ comp.queueLinkTasks(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.
+pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.Task) void {
+ if (comp.link_task_queue.enqueue(comp.gpa, tasks) catch |err| switch (err) {
+ error.OutOfMemory => return comp.setAllocFailure(),
+ }) {
+ comp.thread_pool.spawnWgId(&comp.link_task_wait_group, link.flushTaskQueue, .{comp});
+ }
}
pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
@@ -6498,21 +6407,31 @@ pub fn getCrtPaths(
arena: Allocator,
) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
const target = comp.root_mod.resolved_target.result;
+ return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files);
+}
+
+fn getCrtPathsInner(
+ arena: Allocator,
+ target: std.Target,
+ config: Config,
+ libc_installation: ?*const LibCInstallation,
+ crt_files: *std.StringHashMapUnmanaged(CrtFile),
+) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
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,
+ .link_libc = config.link_libc,
+ .output_mode = config.output_mode,
+ .link_mode = config.link_mode,
+ .pie = config.pie,
});
- if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
+ if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
return .{
- .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null,
- .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null,
- .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null,
- .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null,
- .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null,
+ .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
+ .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
+ .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
+ .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
+ .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
};
}
@@ -6522,24 +6441,14 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// then when we create a sub-Compilation for zig libc, it also tries to
// build kernel32.lib.
if (comp.skip_linker_dependencies) return;
+ const target = comp.root_mod.resolved_target.result;
+ if (target.os.tag != .windows or target.ofmt == .c) return;
// This happens when an `extern "foo"` function is referenced.
// If we haven't seen this library yet and we're targeting Windows, we need
// to queue up a work item to produce the DLL import library for this.
- const gop = try comp.system_libs.getOrPut(comp.gpa, lib_name);
- if (!gop.found_existing) {
- gop.value_ptr.* = .{
- .needed = true,
- .weak = false,
- .path = null,
- };
- const target = comp.root_mod.resolved_target.result;
- if (target.os.tag == .windows and target.ofmt != .c) {
- try comp.queueJob(.{
- .windows_import_lib = comp.system_libs.count() - 1,
- });
- }
- }
+ const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
+ if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
}
/// This decides the optimization mode for all zig-provided libraries, including