aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig842
1 files changed, 585 insertions, 257 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 81a2bff37b..39e10becec 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -26,6 +26,10 @@ const Module = @import("Module.zig");
const Cache = @import("Cache.zig");
const stage1 = @import("stage1.zig");
const translate_c = @import("translate_c.zig");
+const c_codegen = @import("codegen/c.zig");
+const ThreadPool = @import("ThreadPool.zig");
+const WaitGroup = @import("WaitGroup.zig");
+const libtsan = @import("libtsan.zig");
/// General-purpose allocator. Used for both temporary and long-term storage.
gpa: *Allocator,
@@ -41,8 +45,13 @@ link_error_flags: link.File.ErrorFlags = .{},
work_queue: std.fifo.LinearFifo(Job, .Dynamic),
+/// 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),
+
/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
-failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
+/// This data is accessed by multiple threads and is protected by `mutex`.
+failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{},
keep_source_files_loaded: bool,
use_clang: bool,
@@ -74,7 +83,7 @@ zig_lib_directory: Directory,
local_cache_directory: Directory,
global_cache_directory: Directory,
libc_include_dir_list: []const []const u8,
-rand: *std.rand.Random,
+thread_pool: *ThreadPool,
/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
@@ -85,6 +94,9 @@ libcxxabi_static_lib: ?CRTFile = null,
/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libunwind_static_lib: ?CRTFile = null,
+/// Populated when we build the TSAN static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+tsan_static_lib: ?CRTFile = null,
/// Populated when we build the libssp static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libssp_static_lib: ?CRTFile = null,
@@ -112,16 +124,21 @@ owned_link_dir: ?std.fs.Dir,
/// Don't use this for anything other than stage1 compatibility.
color: @import("main.zig").Color = .auto,
+/// This mutex guards all `Compilation` mutable state.
+mutex: std.Thread.Mutex = .{},
+
test_filter: ?[]const u8,
test_name_prefix: ?[]const u8,
test_evented_io: bool,
+debug_compiler_runtime_libs: bool,
-emit_h: ?EmitLoc,
emit_asm: ?EmitLoc,
emit_llvm_ir: ?EmitLoc,
emit_analysis: ?EmitLoc,
emit_docs: ?EmitLoc,
+work_queue_wait_group: WaitGroup,
+
pub const InnerError = Module.InnerError;
pub const CRTFile = struct {
@@ -144,6 +161,8 @@ pub const CSourceFile = struct {
const Job = union(enum) {
/// Write the machine code for a Decl to the output file.
codegen_decl: *Module.Decl,
+ /// Render the .h file snippet for the Decl.
+ emit_h_decl: *Module.Decl,
/// The Decl needs to be analyzed and possibly export itself.
/// It may have already be analyzed, or it may have been determined
/// to be outdated; in this case perform semantic analysis again.
@@ -151,9 +170,6 @@ const Job = union(enum) {
/// 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: *Module.Decl,
- /// Invoke the Clang compiler to create an object file, which gets linked
- /// with the Compilation.
- c_object: *CObject,
/// one of the glibc static objects
glibc_crt_file: glibc.CRTFile,
@@ -167,6 +183,7 @@ const Job = union(enum) {
libunwind: void,
libcxx: void,
libcxxabi: void,
+ libtsan: void,
libssp: void,
compiler_rt_lib: void,
compiler_rt_obj: void,
@@ -198,13 +215,29 @@ pub const CObject = struct {
},
/// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
failure,
+ /// A transient failure happened when trying to compile the C Object; it may
+ /// succeed if we try again. There may be a corresponding ErrorMsg in
+ /// Compilation.failed_c_objects. If there is not, the failure is out of memory.
+ failure_retryable,
},
+ pub const ErrorMsg = struct {
+ msg: []const u8,
+ line: u32,
+ column: u32,
+
+ pub fn destroy(em: *ErrorMsg, gpa: *Allocator) void {
+ gpa.free(em.msg);
+ gpa.destroy(em);
+ em.* = undefined;
+ }
+ };
+
/// Returns if there was failure.
pub fn clearStatus(self: *CObject, gpa: *Allocator) bool {
switch (self.status) {
.new => return false,
- .failure => {
+ .failure, .failure_retryable => {
self.status = .new;
return true;
},
@@ -223,6 +256,11 @@ pub const CObject = struct {
}
};
+/// To support incremental compilation, errors are stored in various places
+/// so that they can be created and destroyed appropriately. This structure
+/// is used to collect all the errors from the various places into one
+/// convenient place for API users to consume. It is allocated into 1 heap
+/// and freed all at once.
pub const AllErrors = struct {
arena: std.heap.ArenaAllocator.State,
list: []const Message,
@@ -234,23 +272,32 @@ pub const AllErrors = struct {
column: usize,
byte_offset: usize,
msg: []const u8,
+ notes: []Message = &.{},
},
plain: struct {
msg: []const u8,
},
- pub fn renderToStdErr(self: Message) void {
- switch (self) {
+ pub fn renderToStdErr(msg: Message) void {
+ return msg.renderToStdErrInner("error");
+ }
+
+ fn renderToStdErrInner(msg: Message, kind: []const u8) void {
+ switch (msg) {
.src => |src| {
- std.debug.print("{s}:{d}:{d}: error: {s}\n", .{
+ std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{
src.src_path,
src.line + 1,
src.column + 1,
+ kind,
src.msg,
});
+ for (src.notes) |note| {
+ note.renderToStdErrInner("note");
+ }
},
.plain => |plain| {
- std.debug.print("error: {s}\n", .{plain.msg});
+ std.debug.print("{s}: {s}\n", .{ kind, plain.msg });
},
}
}
@@ -261,20 +308,38 @@ pub const AllErrors = struct {
}
fn add(
+ module: *Module,
arena: *std.heap.ArenaAllocator,
errors: *std.ArrayList(Message),
- sub_file_path: []const u8,
- source: []const u8,
- simple_err_msg: ErrorMsg,
+ module_err_msg: Module.ErrorMsg,
) !void {
- const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
+ const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len);
+ for (notes) |*note, i| {
+ const module_note = module_err_msg.notes[i];
+ const source = try module_note.src_loc.file_scope.getSource(module);
+ const loc = std.zig.findLineColumn(source, module_note.src_loc.byte_offset);
+ const sub_file_path = module_note.src_loc.file_scope.sub_file_path;
+ note.* = .{
+ .src = .{
+ .src_path = try arena.allocator.dupe(u8, sub_file_path),
+ .msg = try arena.allocator.dupe(u8, module_note.msg),
+ .byte_offset = module_note.src_loc.byte_offset,
+ .line = loc.line,
+ .column = loc.column,
+ },
+ };
+ }
+ const source = try module_err_msg.src_loc.file_scope.getSource(module);
+ const loc = std.zig.findLineColumn(source, module_err_msg.src_loc.byte_offset);
+ const sub_file_path = module_err_msg.src_loc.file_scope.sub_file_path;
try errors.append(.{
.src = .{
.src_path = try arena.allocator.dupe(u8, sub_file_path),
- .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
- .byte_offset = simple_err_msg.byte_offset,
+ .msg = try arena.allocator.dupe(u8, module_err_msg.msg),
+ .byte_offset = module_err_msg.src_loc.byte_offset,
.line = loc.line,
.column = loc.column,
+ .notes = notes,
},
});
}
@@ -331,7 +396,7 @@ pub const InitOptions = struct {
root_name: []const u8,
root_pkg: ?*Package,
output_mode: std.builtin.OutputMode,
- rand: *std.rand.Random,
+ thread_pool: *ThreadPool,
dynamic_linker: ?[]const u8 = null,
/// `null` means to not emit a binary file.
emit_bin: ?EmitLoc,
@@ -375,8 +440,11 @@ pub const InitOptions = struct {
want_pie: ?bool = null,
want_sanitize_c: ?bool = null,
want_stack_check: ?bool = null,
+ want_red_zone: ?bool = null,
want_valgrind: ?bool = null,
+ want_tsan: ?bool = null,
want_compiler_rt: ?bool = null,
+ want_lto: ?bool = null,
use_llvm: ?bool = null,
use_lld: ?bool = null,
use_clang: ?bool = null,
@@ -400,6 +468,11 @@ pub const InitOptions = struct {
disable_c_depfile: bool = false,
linker_z_nodelete: bool = false,
linker_z_defs: bool = false,
+ linker_tsaware: bool = false,
+ linker_nxcompat: bool = false,
+ linker_dynamicbase: bool = false,
+ major_subsystem_version: ?u32 = null,
+ minor_subsystem_version: ?u32 = null,
clang_passthrough_mode: bool = false,
verbose_cc: bool = false,
verbose_link: bool = false,
@@ -411,7 +484,12 @@ pub const InitOptions = struct {
verbose_llvm_cpu_features: bool = false,
is_test: bool = false,
test_evented_io: bool = false,
- is_compiler_rt_or_libc: bool = false,
+ debug_compiler_runtime_libs: bool = false,
+ /// Normally when you create a `Compilation`, Zig will automatically build
+ /// and link in required dependencies, such as compiler-rt and libc. When
+ /// building such dependencies themselves, this flag must be set to avoid
+ /// infinite recursion.
+ skip_linker_dependencies: bool = false,
parent_compilation_link_libc: bool = false,
stack_size_override: ?u64 = null,
image_base_override: ?u64 = null,
@@ -481,7 +559,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.Lib => is_dyn_lib,
.Exe => true,
};
- const needs_c_symbols = !options.is_compiler_rt_or_libc and
+ const needs_c_symbols = !options.skip_linker_dependencies and
(is_exe_or_dyn_lib or (options.target.isWasm() and options.output_mode != .Obj));
const comp: *Compilation = comp: {
@@ -504,6 +582,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (options.root_pkg == null)
break :blk false;
+ // If we are outputting .c code we must use Zig backend.
+ if (ofmt == .c)
+ break :blk false;
+
// If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
// to compile zig code.
if (build_options.is_stage1)
@@ -526,6 +608,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (ofmt == .c)
break :blk false;
+ if (options.want_lto) |lto| {
+ if (lto) {
+ break :blk true;
+ }
+ }
+
// Our linker can't handle objects or most advanced options yet.
if (options.link_objects.len != 0 or
options.c_source_files.len != 0 or
@@ -557,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
};
const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: {
- const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: {
+ const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: {
// TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out.
// See https://github.com/ziglang/zig/issues/6996
const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11;
@@ -571,7 +659,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
break :outer opts;
} else .{};
- const link_libc = options.link_libc or target_util.osRequiresLibC(options.target);
+ const lto = blk: {
+ if (options.want_lto) |explicit| {
+ if (!use_lld)
+ return error.LtoUnavailableWithoutLld;
+ break :blk explicit;
+ } else if (!use_lld) {
+ break :blk false;
+ } else if (options.c_source_files.len == 0) {
+ break :blk false;
+ } else if (darwin_options.system_linker_hack) {
+ break :blk false;
+ } else switch (options.output_mode) {
+ .Lib, .Obj => break :blk false,
+ .Exe => switch (options.optimize_mode) {
+ .Debug => break :blk false,
+ .ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true,
+ },
+ }
+ };
+
+ const tsan = options.want_tsan orelse false;
+
+ const link_libc = options.link_libc or target_util.osRequiresLibC(options.target) or tsan;
const must_dynamic_link = dl: {
if (target_util.cannotDynamicLink(options.target))
@@ -633,12 +743,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
);
const must_pie = target_util.requiresPIE(options.target);
- const pie = if (options.want_pie) |explicit| pie: {
+ const pie: bool = if (options.want_pie) |explicit| pie: {
if (!explicit and must_pie) {
return error.TargetRequiresPIE;
}
break :pie explicit;
- } else must_pie;
+ } else must_pie or tsan;
const must_pic: bool = b: {
if (target_util.requiresPIC(options.target, link_libc))
@@ -657,6 +767,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
break :pic explicit;
} else pie or must_pic;
+ // TSAN is implemented in C++ so it requires linking libc++.
+ const link_libcpp = options.link_libcpp or tsan;
+
// Make a decision on whether to use Clang for translate-c and compiling C files.
const use_clang = if (options.use_clang) |explicit| explicit else blk: {
if (build_options.have_llvm) {
@@ -706,11 +819,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
}
assert(mem.endsWith(u8, buf.items, ","));
buf.items[buf.items.len - 1] = 0;
- buf.shrink(buf.items.len);
+ buf.shrinkAndFree(buf.items.len);
break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
} else null;
const strip = options.strip or !target_util.hasDebugInfo(options.target);
+ const red_zone = options.want_red_zone orelse target_util.hasRedZone(options.target);
// We put everything into the cache hash that *cannot be modified during an incremental update*.
// For example, one cannot change the target between updates, but one can change source files,
@@ -739,12 +853,15 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
cache.hash.add(ofmt);
cache.hash.add(pic);
cache.hash.add(pie);
+ cache.hash.add(lto);
+ cache.hash.add(tsan);
cache.hash.add(stack_check);
+ cache.hash.add(red_zone);
cache.hash.add(link_mode);
cache.hash.add(options.function_sections);
cache.hash.add(strip);
cache.hash.add(link_libc);
- cache.hash.add(options.link_libcpp);
+ cache.hash.add(link_libcpp);
cache.hash.add(options.output_mode);
cache.hash.add(options.machine_code_model);
cache.hash.addOptionalEmitLoc(options.emit_bin);
@@ -772,7 +889,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
hash.add(single_threaded);
hash.add(dll_export_fns);
hash.add(options.is_test);
- hash.add(options.is_compiler_rt_or_libc);
+ hash.add(options.skip_linker_dependencies);
hash.add(options.parent_compilation_link_libc);
const digest = hash.final();
@@ -795,33 +912,27 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
const root_scope = rs: {
if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
- const struct_payload = try gpa.create(Type.Payload.EmptyStruct);
const root_scope = try gpa.create(Module.Scope.File);
- struct_payload.* = .{ .scope = &root_scope.root_container };
+ const struct_ty = try Type.Tag.empty_struct.create(
+ gpa,
+ &root_scope.root_container,
+ );
root_scope.* = .{
// TODO this is duped so it can be freed in Container.deinit
.sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
.source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
+ .tree = undefined,
.status = .never_loaded,
.pkg = root_pkg,
.root_container = .{
.file_scope = root_scope,
.decls = .{},
- .ty = Type.initPayload(&struct_payload.base),
+ .ty = struct_ty,
},
};
- break :rs &root_scope.base;
+ break :rs root_scope;
} else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
- const root_scope = try gpa.create(Module.Scope.ZIRModule);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .decls = .{},
- };
- break :rs &root_scope.base;
+ return error.ZirFilesUnsupported;
} else {
unreachable;
}
@@ -834,9 +945,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.root_pkg = root_pkg,
.root_scope = root_scope,
.zig_cache_artifact_directory = zig_cache_artifact_directory,
+ .emit_h = options.emit_h,
};
break :blk module;
- } else null;
+ } else blk: {
+ if (options.emit_h != null) return error.NoZigModuleForCHeader;
+ break :blk null;
+ };
errdefer if (module) |zm| zm.deinit();
const error_return_tracing = !strip and switch (options.optimize_mode) {
@@ -888,10 +1003,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
};
};
- if (!use_llvm and options.emit_h != null) {
- fatal("TODO implement support for -femit-h in the self-hosted backend", .{});
- }
-
var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};
errdefer system_libs.deinit(gpa);
try system_libs.ensureCapacity(gpa, options.system_libs.len);
@@ -913,7 +1024,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.use_llvm = use_llvm,
.system_linker_hack = darwin_options.system_linker_hack,
.link_libc = link_libc,
- .link_libcpp = options.link_libcpp,
+ .link_libcpp = link_libcpp,
.objects = options.link_objects,
.frameworks = options.frameworks,
.framework_dirs = options.framework_dirs,
@@ -929,6 +1040,11 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
.z_nodelete = options.linker_z_nodelete,
.z_defs = options.linker_z_defs,
+ .tsaware = options.linker_tsaware,
+ .nxcompat = options.linker_nxcompat,
+ .dynamicbase = options.linker_dynamicbase,
+ .major_subsystem_version = options.major_subsystem_version,
+ .minor_subsystem_version = options.minor_subsystem_version,
.stack_size_override = options.stack_size_override,
.image_base_override = options.image_base_override,
.include_compiler_rt = include_compiler_rt,
@@ -944,15 +1060,18 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.libc_installation = libc_dirs.libc_installation,
.pic = pic,
.pie = pie,
+ .lto = lto,
.valgrind = valgrind,
+ .tsan = tsan,
.stack_check = stack_check,
+ .red_zone = red_zone,
.single_threaded = single_threaded,
.verbose_link = options.verbose_link,
.machine_code_model = options.machine_code_model,
.dll_export_fns = dll_export_fns,
.error_return_tracing = error_return_tracing,
.llvm_cpu_features = llvm_cpu_features,
- .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc,
+ .skip_linker_dependencies = options.skip_linker_dependencies,
.parent_compilation_link_libc = options.parent_compilation_link_libc,
.each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
.disable_lld_caching = options.disable_lld_caching,
@@ -967,12 +1086,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.local_cache_directory = options.local_cache_directory,
.global_cache_directory = options.global_cache_directory,
.bin_file = bin_file,
- .emit_h = options.emit_h,
.emit_asm = options.emit_asm,
.emit_llvm_ir = options.emit_llvm_ir,
.emit_analysis = options.emit_analysis,
.emit_docs = options.emit_docs,
.work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
+ .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
.keep_source_files_loaded = options.keep_source_files_loaded,
.use_clang = use_clang,
.clang_argv = options.clang_argv,
@@ -981,7 +1100,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.self_exe_path = options.self_exe_path,
.libc_include_dir_list = libc_dirs.libc_include_dir_list,
.sanitize_c = sanitize_c,
- .rand = options.rand,
+ .thread_pool = options.thread_pool,
.clang_passthrough_mode = options.clang_passthrough_mode,
.clang_preprocessor_mode = options.clang_preprocessor_mode,
.verbose_cc = options.verbose_cc,
@@ -999,11 +1118,16 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.test_filter = options.test_filter,
.test_name_prefix = options.test_name_prefix,
.test_evented_io = options.test_evented_io,
+ .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
+ .work_queue_wait_group = undefined,
};
break :comp comp;
};
errdefer comp.destroy();
+ try comp.work_queue_wait_group.init();
+ errdefer comp.work_queue_wait_group.deinit();
+
if (comp.bin_file.options.module) |mod| {
try comp.work_queue.writeItem(.{ .generate_builtin_zig = {} });
}
@@ -1021,7 +1145,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
}
- if (comp.bin_file.options.emit != null and !comp.bin_file.options.is_compiler_rt_or_libc) {
+ if (comp.bin_file.options.emit != null and !comp.bin_file.options.skip_linker_dependencies) {
// 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()) {
@@ -1074,12 +1198,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (comp.wantBuildLibUnwindFromSource()) {
try comp.work_queue.writeItem(.{ .libunwind = {} });
}
- if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and
- comp.bin_file.options.link_libcpp)
- {
+ if (build_options.have_llvm and is_exe_or_dyn_lib and comp.bin_file.options.link_libcpp) {
try comp.work_queue.writeItem(.libcxx);
try comp.work_queue.writeItem(.libcxxabi);
}
+ if (build_options.have_llvm and comp.bin_file.options.tsan) {
+ try comp.work_queue.writeItem(.libtsan);
+ }
// The `is_stage1` condition is here only because stage2 cannot yet build compiler-rt.
// Once it is capable this condition should be removed.
@@ -1137,6 +1262,7 @@ pub fn destroy(self: *Compilation) void {
const gpa = self.gpa;
self.work_queue.deinit();
+ self.c_object_work_queue.deinit();
{
var it = self.crt_files.iterator();
@@ -1166,6 +1292,10 @@ pub fn destroy(self: *Compilation) void {
crt_file.deinit(gpa);
}
+ if (self.glibc_so_files) |*glibc_file| {
+ glibc_file.deinit(gpa);
+ }
+
for (self.c_object_table.items()) |entry| {
entry.key.destroy(gpa);
}
@@ -1180,6 +1310,8 @@ pub fn destroy(self: *Compilation) void {
self.cache_parent.manifest_dir.close();
if (self.owned_link_dir) |*dir| dir.close();
+ self.work_queue_wait_group.deinit();
+
// This destroys `self`.
self.arena_state.promote(gpa).deinit();
}
@@ -1193,42 +1325,35 @@ pub fn update(self: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
+ self.c_object_cache_digest_set.clearRetainingCapacity();
+
// For compiling C objects, we rely on the cache hash system to avoid duplicating work.
// Add a Job for each C object.
- try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
+ try self.c_object_work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
for (self.c_object_table.items()) |entry| {
- self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
+ self.c_object_work_queue.writeItemAssumeCapacity(entry.key);
}
- const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm;
+ const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm;
if (!use_stage1) {
if (self.bin_file.options.module) |module| {
+ module.compile_log_text.shrinkAndFree(module.gpa, 0);
module.generation += 1;
// TODO Detect which source files changed.
// Until then we simulate a full cache miss. Source files could have been loaded for any reason;
// to force a refresh we unload now.
- if (module.root_scope.cast(Module.Scope.File)) |zig_file| {
- zig_file.unload(module.gpa);
- module.failed_root_src_file = null;
- module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- error.OutOfMemory => return error.OutOfMemory,
- else => |e| {
- module.failed_root_src_file = e;
- },
- };
- } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- zir_module.unload(module.gpa);
- module.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- else => |e| return e,
- };
- }
+ module.root_scope.unload(module.gpa);
+ module.failed_root_src_file = null;
+ module.analyzeContainer(&module.root_scope.root_container) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ error.OutOfMemory => return error.OutOfMemory,
+ else => |e| {
+ module.failed_root_src_file = e;
+ },
+ };
// TODO only analyze imports if they are still referenced
for (module.import_table.items()) |entry| {
@@ -1266,9 +1391,14 @@ pub fn update(self: *Compilation) !void {
// This is needed before reading the error flags.
try self.bin_file.flush(self);
-
self.link_error_flags = self.bin_file.errorFlags();
+ if (!use_stage1) {
+ if (self.bin_file.options.module) |module| {
+ try link.File.C.flushEmitH(module);
+ }
+ }
+
// If there are any errors, we anticipate the source files being loaded
// to report error messages. Otherwise we unload all source files to save memory.
if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
@@ -1294,7 +1424,8 @@ pub fn totalErrorCount(self: *Compilation) usize {
var total: usize = self.failed_c_objects.items().len;
if (self.bin_file.options.module) |module| {
- total += module.failed_decls.items().len +
+ total += module.failed_decls.count() +
+ module.emit_h_failed_decls.count() +
module.failed_exports.items().len +
module.failed_files.items().len +
@boolToInt(module.failed_root_src_file != null);
@@ -1302,7 +1433,14 @@ pub fn totalErrorCount(self: *Compilation) usize {
// The "no entry point found" error only counts if there are no other errors.
if (total == 0) {
- return @boolToInt(self.link_error_flags.no_entry_point_found);
+ total += @boolToInt(self.link_error_flags.no_entry_point_found);
+ }
+
+ // Compile log errors only count if there are no other errors.
+ if (total == 0) {
+ if (self.bin_file.options.module) |module| {
+ total += @boolToInt(module.compile_log_decls.items().len != 0);
+ }
}
return total;
@@ -1318,26 +1456,32 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
for (self.failed_c_objects.items()) |entry| {
const c_object = entry.key;
const err_msg = entry.value;
- try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*);
+ // TODO these fields will need to be adjusted when we have proper
+ // C error reporting bubbling up.
+ try errors.append(.{
+ .src = .{
+ .src_path = try arena.allocator.dupe(u8, c_object.src.src_path),
+ .msg = try std.fmt.allocPrint(&arena.allocator, "unable to build C object: {s}", .{
+ err_msg.msg,
+ }),
+ .byte_offset = 0,
+ .line = err_msg.line,
+ .column = err_msg.column,
+ },
+ });
}
if (self.bin_file.options.module) |module| {
for (module.failed_files.items()) |entry| {
- const scope = entry.key;
- const err_msg = entry.value;
- const source = try scope.getSource(module);
- try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
+ try AllErrors.add(module, &arena, &errors, entry.value.*);
}
for (module.failed_decls.items()) |entry| {
- const decl = entry.key;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(module);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ try AllErrors.add(module, &arena, &errors, entry.value.*);
+ }
+ for (module.emit_h_failed_decls.items()) |entry| {
+ try AllErrors.add(module, &arena, &errors, entry.value.*);
}
for (module.failed_exports.items()) |entry| {
- const decl = entry.key.owner_decl;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(module);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ try AllErrors.add(module, &arena, &errors, entry.value.*);
}
if (module.failed_root_src_file) |err| {
const file_path = try module.root_pkg.root_src_directory.join(&arena.allocator, &[_][]const u8{
@@ -1358,6 +1502,28 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
});
}
+ if (self.bin_file.options.module) |module| {
+ const compile_log_items = module.compile_log_decls.items();
+ if (errors.items.len == 0 and compile_log_items.len != 0) {
+ // First one will be the error; subsequent ones will be notes.
+ const err_msg = Module.ErrorMsg{
+ .src_loc = compile_log_items[0].value,
+ .msg = "found compile log statement",
+ .notes = try self.gpa.alloc(Module.ErrorMsg, compile_log_items.len - 1),
+ };
+ defer self.gpa.free(err_msg.notes);
+
+ for (compile_log_items[1..]) |entry, i| {
+ err_msg.notes[i] = .{
+ .src_loc = entry.value,
+ .msg = "also here",
+ };
+ }
+
+ try AllErrors.add(module, &arena, &errors, err_msg);
+ }
+ }
+
assert(errors.items.len == self.totalErrorCount());
return AllErrors{
@@ -1366,15 +1532,32 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
};
}
+pub fn getCompileLogOutput(self: *Compilation) []const u8 {
+ const module = self.bin_file.options.module orelse return &[0]u8{};
+ return module.compile_log_text.items;
+}
+
pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
- var progress: std.Progress = .{};
- var main_progress_node = try progress.start("", null);
+ // If the terminal is dumb, we dont want to show the user all the
+ // output.
+ var progress: std.Progress = .{ .dont_print_on_dumb = true };
+ var main_progress_node = try progress.start("", 0);
defer main_progress_node.end();
if (self.color == .off) progress.terminal = null;
var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
defer c_comp_progress_node.end();
+ self.work_queue_wait_group.reset();
+ defer self.work_queue_wait_group.wait();
+
+ while (self.c_object_work_queue.readItem()) |c_object| {
+ self.work_queue_wait_group.start();
+ try self.thread_pool.spawn(workerUpdateCObject, .{
+ self, c_object, &c_comp_progress_node, &self.work_queue_wait_group,
+ });
+ }
+
while (self.work_queue.readItem()) |work_item| switch (work_item) {
.codegen_decl => |decl| switch (decl.analysis) {
.unreferenced => unreachable,
@@ -1388,49 +1571,106 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
=> continue,
.complete, .codegen_failure_retryable => {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
const module = self.bin_file.options.module.?;
- if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
- switch (payload.func.analysis) {
- .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
+ if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| {
+ const func = payload.data;
+ switch (func.state) {
+ .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
error.AnalysisFail => {
- assert(payload.func.analysis != .in_progress);
+ assert(func.state != .in_progress);
continue;
},
error.OutOfMemory => return error.OutOfMemory,
},
.in_progress => unreachable,
+ .inline_only => unreachable, // don't queue work for this
.sema_failure, .dependency_failure => continue,
.success => {},
}
- // Here we tack on additional allocations to the Decl's arena. The allocations are
- // lifetime annotations in the ZIR.
+ // Here we tack on additional allocations to the Decl's arena. The allocations
+ // are lifetime annotations in the ZIR.
var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
- log.debug("analyze liveness of {}\n", .{decl.name});
- try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success);
+ log.debug("analyze liveness of {s}", .{decl.name});
+ try liveness.analyze(module.gpa, &decl_arena.allocator, func.body);
+
+ if (std.builtin.mode == .Debug and self.verbose_ir) {
+ func.dump(module.*);
+ }
}
+ log.debug("calling updateDecl on '{s}', type={}", .{
+ decl.name, decl.typed_value.most_recent.typed_value.ty,
+ });
assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => {
- decl.analysis = .dependency_failure;
+ decl.analysis = .codegen_failure;
+ continue;
},
else => {
try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
- module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
module.gpa,
- decl.src(),
- "unable to codegen: {}",
+ decl.srcLoc(),
+ "unable to codegen: {s}",
.{@errorName(err)},
));
decl.analysis = .codegen_failure_retryable;
+ continue;
+ },
+ };
+ },
+ },
+ .emit_h_decl => |decl| switch (decl.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => unreachable,
+
+ .sema_failure,
+ .dependency_failure,
+ .sema_failure_retryable,
+ => continue,
+
+ // emit-h only requires semantic analysis of the Decl to be complete,
+ // it does not depend on machine code generation to succeed.
+ .codegen_failure, .codegen_failure_retryable, .complete => {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
+ const module = self.bin_file.options.module.?;
+ const emit_loc = module.emit_h.?;
+ const tv = decl.typed_value.most_recent.typed_value;
+ const emit_h = decl.getEmitH(module);
+ const fwd_decl = &emit_h.fwd_decl;
+ fwd_decl.shrinkRetainingCapacity(0);
+
+ var dg: c_codegen.DeclGen = .{
+ .module = module,
+ .error_msg = null,
+ .decl = decl,
+ .fwd_decl = fwd_decl.toManaged(module.gpa),
+ };
+ defer dg.fwd_decl.deinit();
+
+ c_codegen.genHeader(&dg) catch |err| switch (err) {
+ error.AnalysisFail => {
+ try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?);
+ continue;
},
+ else => |e| return e,
};
+
+ fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
+ fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len);
},
},
.analyze_decl => |decl| {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
const module = self.bin_file.options.module.?;
module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
@@ -1438,80 +1678,73 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
};
},
.update_line_number => |decl| {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
const module = self.bin_file.options.module.?;
self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
- module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
module.gpa,
- decl.src(),
- "unable to update line number: {}",
+ decl.srcLoc(),
+ "unable to update line number: {s}",
.{@errorName(err)},
));
decl.analysis = .codegen_failure_retryable;
};
},
- .c_object => |c_object| {
- self.updateCObject(c_object, &c_comp_progress_node) catch |err| switch (err) {
- error.AnalysisFail => continue,
- else => {
- try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
- self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
- self.gpa,
- 0,
- "unable to build C object: {s}",
- .{@errorName(err)},
- ));
- c_object.status = .{ .failure = {} };
- },
- };
- },
.glibc_crt_file => |crt_file| {
glibc.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build glibc CRT file: {}", .{@errorName(err)});
+ fatal("unable to build glibc CRT file: {s}", .{@errorName(err)});
};
},
.glibc_shared_objects => {
glibc.buildSharedObjects(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build glibc shared objects: {}", .{@errorName(err)});
+ fatal("unable to build glibc shared objects: {s}", .{@errorName(err)});
};
},
.musl_crt_file => |crt_file| {
musl.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build musl CRT file: {}", .{@errorName(err)});
+ fatal("unable to build musl CRT file: {s}", .{@errorName(err)});
};
},
.mingw_crt_file => |crt_file| {
mingw.buildCRTFile(self, crt_file) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)});
+ fatal("unable to build mingw-w64 CRT file: {s}", .{@errorName(err)});
};
},
.windows_import_lib => |index| {
const link_lib = self.bin_file.options.system_libs.items()[index].key;
mingw.buildImportLib(self, link_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)});
+ fatal("unable to generate DLL import .lib file: {s}", .{@errorName(err)});
};
},
.libunwind => {
libunwind.buildStaticLib(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build libunwind: {}", .{@errorName(err)});
+ fatal("unable to build libunwind: {s}", .{@errorName(err)});
};
},
.libcxx => {
libcxx.buildLibCXX(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build libcxx: {}", .{@errorName(err)});
+ fatal("unable to build libcxx: {s}", .{@errorName(err)});
};
},
.libcxxabi => {
libcxx.buildLibCXXABI(self) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build libcxxabi: {}", .{@errorName(err)});
+ fatal("unable to build libcxxabi: {s}", .{@errorName(err)});
+ };
+ },
+ .libtsan => {
+ libtsan.buildTsan(self) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build TSAN library: {s}", .{@errorName(err)});
};
},
.compiler_rt_lib => {
@@ -1529,20 +1762,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
.libssp => {
self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build libssp: {}", .{@errorName(err)});
+ fatal("unable to build libssp: {s}", .{@errorName(err)});
};
},
.zig_libc => {
self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)});
+ fatal("unable to build zig's multitarget libc: {s}", .{@errorName(err)});
};
},
.generate_builtin_zig => {
// This Job is only queued up if there is a zig module.
self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| {
// TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to update builtin.zig file: {}", .{@errorName(err)});
+ fatal("unable to update builtin.zig file: {s}", .{@errorName(err)});
};
},
.stage1_module => {
@@ -1550,13 +1783,13 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
unreachable;
self.updateStage1Module(main_progress_node) catch |err| {
- fatal("unable to build stage1 zig object: {}", .{@errorName(err)});
+ fatal("unable to build stage1 zig object: {s}", .{@errorName(err)});
};
},
};
}
-pub fn obtainCObjectCacheManifest(comp: *Compilation) Cache.Manifest {
+pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {
var man = comp.cache_parent.obtain();
// Only things that need to be added on top of the base hash, and only things
@@ -1622,11 +1855,11 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
tmp_dir_sub_path, cimport_basename,
});
- const out_dep_path = try std.fmt.allocPrint(arena, "{}.d", .{out_h_path});
+ const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path});
try zig_cache_tmp_dir.writeFile(cimport_basename, c_src);
if (comp.verbose_cimport) {
- log.info("C import source: {}", .{out_h_path});
+ log.info("C import source: {s}", .{out_h_path});
}
var argv = std.ArrayList([]const u8).init(comp.gpa);
@@ -1651,7 +1884,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
- const tree = translate_c.translate(
+ var tree = translate_c.translate(
comp.gpa,
new_argv.ptr,
new_argv.ptr + new_argv.len,
@@ -1670,10 +1903,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
};
},
};
- defer tree.deinit();
+ defer tree.deinit(comp.gpa);
if (comp.verbose_cimport) {
- log.info("C import .d file: {}", .{out_dep_path});
+ log.info("C import .d file: {s}", .{out_dep_path});
}
const dep_basename = std.fs.path.basename(out_dep_path);
@@ -1688,12 +1921,13 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
defer out_zig_file.close();
- var bos = std.io.bufferedOutStream(out_zig_file.writer());
- _ = try std.zig.render(comp.gpa, bos.writer(), tree);
- try bos.flush();
+ const formatted = try tree.render(comp.gpa);
+ defer comp.gpa.free(formatted);
+
+ try out_zig_file.writeAll(formatted);
man.writeManifest() catch |err| {
- log.warn("failed to write cache manifest for C import: {}", .{@errorName(err)});
+ log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
};
break :digest digest;
@@ -1703,7 +1937,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
"o", &digest, cimport_zig_basename,
});
if (comp.verbose_cimport) {
- log.info("C import output: {}\n", .{out_zig_path});
+ log.info("C import output: {s}", .{out_zig_path});
}
return CImportResult{
.out_zig_path = out_zig_path,
@@ -1711,6 +1945,49 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
};
}
+fn workerUpdateCObject(
+ comp: *Compilation,
+ c_object: *CObject,
+ progress_node: *std.Progress.Node,
+ wg: *WaitGroup,
+) void {
+ defer wg.finish();
+
+ comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
+ error.AnalysisFail => return,
+ else => {
+ comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) {
+ // Swallowing this error is OK because it's implied to be OOM when
+ // there is a missing failed_c_objects error message.
+ error.OutOfMemory => {},
+ };
+ },
+ };
+}
+
+fn reportRetryableCObjectError(
+ comp: *Compilation,
+ c_object: *CObject,
+ err: anyerror,
+) error{OutOfMemory}!void {
+ c_object.status = .failure_retryable;
+
+ const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg);
+ errdefer comp.gpa.destroy(c_obj_err_msg);
+ const msg = try std.fmt.allocPrint(comp.gpa, "unable to build C object: {s}", .{@errorName(err)});
+ errdefer comp.gpa.free(msg);
+ c_obj_err_msg.* = .{
+ .msg = msg,
+ .line = 0,
+ .column = 0,
+ };
+ {
+ const lock = comp.mutex.acquire();
+ defer lock.release();
+ try comp.failed_c_objects.putNoClobber(comp.gpa, c_object, c_obj_err_msg);
+ }
+}
+
fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *std.Progress.Node) !void {
if (!build_options.have_llvm) {
return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
@@ -1723,7 +2000,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
if (c_object.clearStatus(comp.gpa)) {
// There was previous failure.
- comp.failed_c_objects.removeAssertDiscard(c_object);
+ const lock = comp.mutex.acquire();
+ defer lock.release();
+ // If the failure was OOM, there will not be an entry here, so we do
+ // not assert discard.
+ _ = comp.failed_c_objects.swapRemove(c_object);
}
var man = comp.obtainCObjectCacheManifest();
@@ -1750,8 +2031,16 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
}
{
- const gop = try comp.c_object_cache_digest_set.getOrPut(comp.gpa, man.hash.peekBin());
- if (gop.found_existing) {
+ const is_collision = blk: {
+ const bin_digest = man.hash.peekBin();
+
+ const lock = comp.mutex.acquire();
+ defer lock.release();
+
+ const gop = try comp.c_object_cache_digest_set.getOrPut(comp.gpa, bin_digest);
+ break :blk gop.found_existing;
+ };
+ if (is_collision) {
return comp.failCObj(
c_object,
"the same source file was already added to the same compilation with the same flags",
@@ -1767,7 +2056,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
const c_source_basename = std.fs.path.basename(c_object.src.src_path);
c_comp_progress_node.activate();
- var child_progress_node = c_comp_progress_node.start(c_source_basename, null);
+ var child_progress_node = c_comp_progress_node.start(c_source_basename, 0);
child_progress_node.activate();
defer child_progress_node.end();
@@ -1823,7 +2112,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
child.stderr_behavior = .Inherit;
const term = child.spawnAndWait() catch |err| {
- return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
};
switch (term) {
.Exited => |code| {
@@ -1851,7 +2140,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
const term = child.wait() catch |err| {
- return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
};
switch (term) {
@@ -1859,12 +2148,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
if (code != 0) {
// TODO parse clang stderr and turn it into an error message
// and then call failCObjWithOwnedErrorMsg
- log.err("clang failed with stderr: {}", .{stderr});
- return comp.failCObj(c_object, "clang exited with code {}", .{code});
+ log.err("clang failed with stderr: {s}", .{stderr});
+ return comp.failCObj(c_object, "clang exited with code {d}", .{code});
}
},
else => {
- log.err("clang terminated with stderr: {}", .{stderr});
+ log.err("clang terminated with stderr: {s}", .{stderr});
return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
},
}
@@ -1876,7 +2165,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
// Just to save disk space, we delete the file because it is never needed again.
zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
- log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
+ log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) });
};
}
@@ -1892,7 +2181,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename);
man.writeManifest() catch |err| {
- log.warn("failed to write cache manifest when compiling '{}': {}", .{ 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) });
};
break :blk digest;
};
@@ -1909,9 +2198,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
const s = std.fs.path.sep_str;
- const rand_int = comp.rand.int(u64);
+ const rand_int = std.crypto.random.int(u64);
if (comp.local_cache_directory.path) |p| {
- return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
+ return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
} else {
return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
}
@@ -1932,7 +2221,7 @@ pub fn addTranslateCCArgs(
/// Add common C compiler args between translate-c and C object compilation.
pub fn addCCArgs(
- comp: *Compilation,
+ comp: *const Compilation,
arena: *Allocator,
argv: *std.ArrayList([]const u8),
ext: FileExt,
@@ -1956,12 +2245,6 @@ pub fn addCCArgs(
try argv.append("-ffunction-sections");
}
- try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2);
- for (comp.bin_file.options.framework_dirs) |framework_dir| {
- argv.appendAssumeCapacity("-iframework");
- argv.appendAssumeCapacity(framework_dir);
- }
-
if (comp.bin_file.options.link_libcpp) {
const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
comp.zig_lib_directory.path.?, "libcxx", "include",
@@ -1992,6 +2275,9 @@ pub fn addCCArgs(
"-nostdinc",
"-fno-spell-checking",
});
+ if (comp.bin_file.options.lto) {
+ try argv.append("-flto");
+ }
// According to Rich Felker libc headers are supposed to go before C language headers.
// However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
@@ -2027,7 +2313,7 @@ pub fn addCCArgs(
}
const mcmodel = comp.bin_file.options.machine_code_model;
if (mcmodel != .default) {
- try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)}));
+ try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)}));
}
switch (target.os.tag) {
@@ -2079,9 +2365,20 @@ pub fn addCCArgs(
try argv.append("-fomit-frame-pointer");
}
- if (comp.sanitize_c) {
+ if (comp.sanitize_c and !comp.bin_file.options.tsan) {
try argv.append("-fsanitize=undefined");
try argv.append("-fsanitize-trap=undefined");
+ } else if (comp.sanitize_c and comp.bin_file.options.tsan) {
+ try argv.append("-fsanitize=undefined,thread");
+ try argv.append("-fsanitize-trap=undefined");
+ } else if (!comp.sanitize_c and comp.bin_file.options.tsan) {
+ try argv.append("-fsanitize=thread");
+ }
+
+ if (comp.bin_file.options.red_zone) {
+ try argv.append("-mred-zone");
+ } else if (target_util.hasRedZone(target)) {
+ try argv.append("-mno-red-zone");
}
switch (comp.bin_file.options.optimize_mode) {
@@ -2161,50 +2458,40 @@ pub fn addCCArgs(
fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
@setCold(true);
- const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args);
+ const err_msg = blk: {
+ const msg = try std.fmt.allocPrint(comp.gpa, format, args);
+ errdefer comp.gpa.free(msg);
+ const err_msg = try comp.gpa.create(CObject.ErrorMsg);
+ errdefer comp.gpa.destroy(err_msg);
+ err_msg.* = .{
+ .msg = msg,
+ .line = 0,
+ .column = 0,
+ };
+ break :blk err_msg;
+ };
return comp.failCObjWithOwnedErrorMsg(c_object, err_msg);
}
-fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
+fn failCObjWithOwnedErrorMsg(
+ comp: *Compilation,
+ c_object: *CObject,
+ err_msg: *CObject.ErrorMsg,
+) InnerError {
+ @setCold(true);
{
- errdefer err_msg.destroy(comp.gpa);
- try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1);
+ const lock = comp.mutex.acquire();
+ defer lock.release();
+ {
+ errdefer err_msg.destroy(comp.gpa);
+ try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1);
+ }
+ comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
}
- comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
c_object.status = .failure;
return error.AnalysisFail;
}
-pub const ErrorMsg = struct {
- byte_offset: usize,
- msg: []const u8,
-
- pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
- const self = try gpa.create(ErrorMsg);
- errdefer gpa.destroy(self);
- self.* = try init(gpa, byte_offset, format, args);
- return self;
- }
-
- /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
- pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
- self.deinit(gpa);
- gpa.destroy(self);
- }
-
- pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
- return ErrorMsg{
- .byte_offset = byte_offset,
- .msg = try std.fmt.allocPrint(gpa, format, args),
- };
- }
-
- pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
- gpa.free(self.msg);
- self.* = undefined;
- }
-};
-
pub const FileExt = enum {
c,
cpp,
@@ -2327,7 +2614,7 @@ test "classifyFileExt" {
std.testing.expectEqual(FileExt.zir, classifyFileExt("foo.zir"));
}
-fn haveFramePointer(comp: *Compilation) bool {
+fn haveFramePointer(comp: *const Compilation) bool {
// If you complicate this logic make sure you update the parent cache hash.
// Right now it's not in the cache hash because the value depends on optimize_mode
// and strip which are both already part of the hash.
@@ -2371,22 +2658,22 @@ fn detectLibCIncludeDirs(
const s = std.fs.path.sep_str;
const arch_include_dir = try std.fmt.allocPrint(
arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}",
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
.{ zig_lib_dir, arch_name, os_name, abi_name },
);
const generic_include_dir = try std.fmt.allocPrint(
arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}",
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
.{ zig_lib_dir, generic_name },
);
const arch_os_include_dir = try std.fmt.allocPrint(
arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any",
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
.{ zig_lib_dir, @tagName(target.cpu.arch), os_name },
);
const generic_os_include_dir = try std.fmt.allocPrint(
arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any",
+ "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
.{ zig_lib_dir, os_name },
);
@@ -2505,9 +2792,9 @@ fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void {
pub fn dump_argv(argv: []const []const u8) void {
for (argv[0 .. argv.len - 1]) |arg| {
- std.debug.print("{} ", .{arg});
+ std.debug.print("{s} ", .{arg});
}
- std.debug.print("{}\n", .{argv[argv.len - 1]});
+ std.debug.print("{s}\n", .{argv[argv.len - 1]});
}
pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
@@ -2527,6 +2814,11 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
\\pub const arch = Target.current.cpu.arch;
\\/// Deprecated
\\pub const endian = Target.current.cpu.arch.endian();
+ \\
+ \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
+ \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
+ \\pub const zig_version = try @import("std").SemanticVersion.parse("{s}");
+ \\
\\pub const output_mode = OutputMode.{};
\\pub const link_mode = LinkMode.{};
\\pub const is_test = {};
@@ -2538,16 +2830,17 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
\\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
\\
, .{
- @tagName(comp.bin_file.options.output_mode),
- @tagName(comp.bin_file.options.link_mode),
+ build_options.version,
+ std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
+ std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
comp.bin_file.options.is_test,
comp.bin_file.options.single_threaded,
- @tagName(target.abi),
- @tagName(target.cpu.arch),
- generic_arch_name,
- target.cpu.model.name,
- generic_arch_name,
- generic_arch_name,
+ std.zig.fmtId(@tagName(target.abi)),
+ std.zig.fmtId(@tagName(target.cpu.arch)),
+ std.zig.fmtId(generic_arch_name),
+ std.zig.fmtId(target.cpu.model.name),
+ std.zig.fmtId(generic_arch_name),
+ std.zig.fmtId(generic_arch_name),
});
for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
@@ -2569,12 +2862,12 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
\\ .tag = .{},
\\ .version_range = .{{
,
- .{@tagName(target.os.tag)},
+ .{std.zig.fmtId(@tagName(target.os.tag))},
);
switch (target.os.getVersionRange()) {
.none => try buffer.appendSlice(" .none = {} }\n"),
- .semver => |semver| try buffer.outStream().print(
+ .semver => |semver| try buffer.writer().print(
\\ .semver = .{{
\\ .min = .{{
\\ .major = {},
@@ -2597,7 +2890,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
semver.max.minor,
semver.max.patch,
}),
- .linux => |linux| try buffer.outStream().print(
+ .linux => |linux| try buffer.writer().print(
\\ .linux = .{{
\\ .range = .{{
\\ .min = .{{
@@ -2631,7 +2924,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
linux.glibc.minor,
linux.glibc.patch,
}),
- .windows => |windows| try buffer.outStream().print(
+ .windows => |windows| try buffer.writer().print(
\\ .windows = .{{
\\ .min = {s},
\\ .max = {s},
@@ -2649,7 +2942,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
// in. For example, compiler_rt will not export the __chkstk symbol if it
// knows libc will provide it, and likewise c.zig will not export memcpy.
const link_libc = comp.bin_file.options.link_libc or
- (comp.bin_file.options.is_compiler_rt_or_libc and comp.bin_file.options.parent_compilation_link_libc);
+ (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_link_libc);
try buffer.writer().print(
\\pub const object_format = ObjectFormat.{};
@@ -2664,8 +2957,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
\\pub const code_model = CodeModel.{};
\\
, .{
- @tagName(comp.bin_file.options.object_format),
- @tagName(comp.bin_file.options.optimize_mode),
+ std.zig.fmtId(@tagName(comp.bin_file.options.object_format)),
+ std.zig.fmtId(@tagName(comp.bin_file.options.optimize_mode)),
link_libc,
comp.bin_file.options.link_libcpp,
comp.bin_file.options.error_return_tracing,
@@ -2673,7 +2966,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
comp.bin_file.options.pic,
comp.bin_file.options.pie,
comp.bin_file.options.strip,
- @tagName(comp.bin_file.options.machine_code_model),
+ std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
});
if (comp.bin_file.options.is_test) {
@@ -2708,7 +3001,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
for (errors.list) |full_err_msg| {
switch (full_err_msg) {
.src => |src| {
- log.err("{s}:{d}:{d}: {s}\n", .{
+ log.err("{s}:{d}:{d}: {s}", .{
src.src_path,
src.line + 1,
src.column + 1,
@@ -2762,14 +3055,6 @@ fn buildOutputFromZig(
.directory = null, // Put it in the cache directory.
.basename = bin_basename,
};
- const optimize_mode: std.builtin.Mode = blk: {
- if (comp.bin_file.options.is_test)
- break :blk comp.bin_file.options.optimize_mode;
- switch (comp.bin_file.options.optimize_mode) {
- .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast,
- .ReleaseSmall => break :blk .ReleaseSmall,
- }
- };
const sub_compilation = try Compilation.create(comp.gpa, .{
.global_cache_directory = comp.global_cache_directory,
.local_cache_directory = comp.global_cache_directory,
@@ -2778,19 +3063,21 @@ fn buildOutputFromZig(
.root_name = root_name,
.root_pkg = &root_pkg,
.output_mode = fixed_output_mode,
- .rand = comp.rand,
+ .thread_pool = comp.thread_pool,
.libc_installation = comp.bin_file.options.libc_installation,
.emit_bin = emit_bin,
- .optimize_mode = optimize_mode,
+ .optimize_mode = comp.compilerRtOptMode(),
.link_mode = .Static,
.function_sections = true,
.want_sanitize_c = false,
.want_stack_check = false,
+ .want_red_zone = comp.bin_file.options.red_zone,
.want_valgrind = false,
+ .want_tsan = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
- .strip = comp.bin_file.options.strip,
+ .strip = comp.compilerRtStrip(),
.is_native_os = comp.bin_file.options.is_native_os,
.is_native_abi = comp.bin_file.options.is_native_abi,
.self_exe_path = comp.self_exe_path,
@@ -2803,7 +3090,7 @@ fn buildOutputFromZig(
.verbose_cimport = comp.verbose_cimport,
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
- .is_compiler_rt_or_libc = true,
+ .skip_linker_dependencies = true,
.parent_compilation_link_libc = comp.bin_file.options.link_libc,
});
defer sub_compilation.destroy();
@@ -2863,11 +3150,15 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
man.hash.add(comp.bin_file.options.function_sections);
man.hash.add(comp.bin_file.options.is_test);
man.hash.add(comp.bin_file.options.emit != null);
- man.hash.addOptionalEmitLoc(comp.emit_h);
+ man.hash.add(mod.emit_h != null);
+ if (mod.emit_h) |emit_h| {
+ man.hash.addEmitLoc(emit_h);
+ }
man.hash.addOptionalEmitLoc(comp.emit_asm);
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
man.hash.addOptionalEmitLoc(comp.emit_analysis);
man.hash.addOptionalEmitLoc(comp.emit_docs);
+ man.hash.add(comp.test_evented_io);
man.hash.addOptionalBytes(comp.test_filter);
man.hash.addOptionalBytes(comp.test_name_prefix);
@@ -2875,7 +3166,12 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
const prev_hash_state = man.hash.peekBin();
const input_file_count = man.files.items.len;
- if (try man.hit()) {
+ const hit = man.hit() catch |err| {
+ const i = man.failed_file_index orelse return err;
+ const file_path = man.files.items[i].path orelse return err;
+ fatal("unable to build stage1 zig object: {s}: {s}", .{ @errorName(err), file_path });
+ };
+ if (hit) {
const digest = man.final();
// We use an extra hex-encoded byte here to store some flags.
@@ -2885,7 +3181,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
id_symlink_basename,
&prev_digest_buf,
) catch |err| blk: {
- log.debug("stage1 {} new_digest={} error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
+ log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
// Handle this as a cache miss.
break :blk prev_digest_buf[0..0];
};
@@ -2893,7 +3189,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
break :hit;
- log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
+ log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
var flags_bytes: [1]u8 = undefined;
_ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {
log.warn("bad cache stage1 digest: '{s}'", .{prev_digest});
@@ -2916,7 +3212,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
return;
}
- log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
+ log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
man.unhit(prev_hash_state, input_file_count);
}
@@ -2961,10 +3257,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
});
break :blk try directory.join(arena, &[_][]const u8{bin_basename});
} else "";
- if (comp.emit_h != null) {
+ if (mod.emit_h != null) {
log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
}
- const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory);
+ const emit_h_path = try stage1LocPath(arena, mod.emit_h, directory);
const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
@@ -3004,6 +3300,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
.err_color = @enumToInt(comp.color),
.pic = comp.bin_file.options.pic,
.pie = comp.bin_file.options.pie,
+ .lto = comp.bin_file.options.lto,
.link_libc = comp.bin_file.options.link_libc,
.link_libcpp = comp.bin_file.options.link_libcpp,
.strip = comp.bin_file.options.strip,
@@ -3011,8 +3308,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
.dll_export_fns = comp.bin_file.options.dll_export_fns,
.link_mode_dynamic = comp.bin_file.options.link_mode == .Dynamic,
.valgrind_enabled = comp.bin_file.options.valgrind,
+ .tsan_enabled = comp.bin_file.options.tsan,
.function_sections = comp.bin_file.options.function_sections,
.enable_stack_probing = comp.bin_file.options.stack_check,
+ .red_zone = comp.bin_file.options.red_zone,
.enable_time_report = comp.time_report,
.enable_stack_report = comp.stack_report,
.test_is_evented = comp.test_evented_io,
@@ -3060,7 +3359,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
// Update the small file with the digest. If it fails we can continue; it only
// means that the next invocation will have an unnecessary cache miss.
const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
- log.debug("stage1 {} final digest={} flags={x}", .{
+ log.debug("stage1 {s} final digest={} flags={x}", .{
mod.root_pkg.root_src_path, digest, stage1_flags_byte,
});
var digest_plus_flags: [digest.len + 2]u8 = undefined;
@@ -3073,11 +3372,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
});
Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| {
- log.warn("failed to save stage1 hash digest file: {}", .{@errorName(err)});
+ log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)});
};
// Failure here only means an unnecessary cache miss.
man.writeManifest() catch |err| {
- log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
};
// We hang on to this lock so that the output file path can be used without
// other processes clobbering it.
@@ -3151,17 +3450,23 @@ pub fn build_crt_file(
.root_name = root_name,
.root_pkg = null,
.output_mode = output_mode,
- .rand = comp.rand,
+ .thread_pool = comp.thread_pool,
.libc_installation = comp.bin_file.options.libc_installation,
.emit_bin = emit_bin,
- .optimize_mode = comp.bin_file.options.optimize_mode,
+ .optimize_mode = comp.compilerRtOptMode(),
.want_sanitize_c = false,
.want_stack_check = false,
+ .want_red_zone = comp.bin_file.options.red_zone,
.want_valgrind = false,
+ .want_tsan = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
+ .want_lto = switch (output_mode) {
+ .Lib => comp.bin_file.options.lto,
+ .Obj, .Exe => false,
+ },
.emit_h = null,
- .strip = comp.bin_file.options.strip,
+ .strip = comp.compilerRtStrip(),
.is_native_os = comp.bin_file.options.is_native_os,
.is_native_abi = comp.bin_file.options.is_native_abi,
.self_exe_path = comp.self_exe_path,
@@ -3175,7 +3480,7 @@ pub fn build_crt_file(
.verbose_cimport = comp.verbose_cimport,
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
- .is_compiler_rt_or_libc = true,
+ .skip_linker_dependencies = true,
.parent_compilation_link_libc = comp.bin_file.options.link_libc,
});
defer sub_compilation.destroy();
@@ -3196,7 +3501,7 @@ pub fn stage1AddLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// Avoid deadlocking on building import libs such as kernel32.lib
// This can happen when the user uses `build-exe foo.obj -lkernel32` and then
// when we create a sub-Compilation for zig libc, it also tries to build kernel32.lib.
- if (comp.bin_file.options.is_compiler_rt_or_libc) return;
+ if (comp.bin_file.options.skip_linker_dependencies) return;
// This happens when an `extern "foo"` function is referenced by the stage1 backend.
// If we haven't seen this library yet and we're targeting Windows, we need to queue up
@@ -3208,3 +3513,26 @@ pub fn stage1AddLinkLib(comp: *Compilation, lib_name: []const u8) !void {
});
}
}
+
+/// This decides the optimization mode for all zig-provided libraries, including
+/// compiler-rt, libcxx, libc, libunwind, etc.
+pub fn compilerRtOptMode(comp: Compilation) std.builtin.Mode {
+ if (comp.debug_compiler_runtime_libs) {
+ return comp.bin_file.options.optimize_mode;
+ }
+ switch (comp.bin_file.options.optimize_mode) {
+ .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(comp.getTarget()),
+ .ReleaseFast => return .ReleaseFast,
+ .ReleaseSmall => return .ReleaseSmall,
+ }
+}
+
+/// This decides whether to strip debug info for all zig-provided libraries, including
+/// compiler-rt, libcxx, libc, libunwind, etc.
+pub fn compilerRtStrip(comp: Compilation) bool {
+ if (comp.debug_compiler_runtime_libs) {
+ return comp.bin_file.options.strip;
+ } else {
+ return true;
+ }
+}