From 134e8cf76a6664ebd028fbcbfbd7a1b85ad031f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Nov 2023 22:46:49 -0700 Subject: move is_libcpp_lib_name and is_libc_lib_name to std.Target --- src/Sema.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Sema.zig b/src/Sema.zig index 2f1dc07328..516fee4543 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9075,7 +9075,7 @@ fn handleExternLibName( const comp = mod.comp; const target = mod.getTarget(); log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); - if (target_util.is_libc_lib_name(target, lib_name)) { + if (target.is_libc_lib_name(lib_name)) { if (!comp.bin_file.options.link_libc) { return sema.fail( block, @@ -9086,7 +9086,7 @@ fn handleExternLibName( } break :blk; } - if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (target.is_libcpp_lib_name(lib_name)) { if (!comp.bin_file.options.link_libcpp) { return sema.fail( block, -- cgit v1.2.3 From 12de7e3472cb2292e75578d33a8b8cc91f1ef0b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Dec 2023 15:25:06 -0700 Subject: WIP: move many global settings to become per-Module Much of the logic from Compilation.create() is extracted into Compilation.Config.resolve() which accepts many optional settings and produces concrete settings. This separate step is needed by API users of Compilation so that they can pass the resolved global settings to the Module creation function, which itself needs to resolve per-Module settings. Since the target and other things are no longer global settings, I did not want them stored in link.File (in the `options` field). That options field was already a kludge; those options should be resolved into concrete settings. This commit also starts to work on that, deleting link.Options, moving the fields into Compilation and ObjectFormat-specific structs instead. Some fields were ephemeral and should not have been stored at all, such as symbol_size_hint. The link.File object of Compilation is now a `?*link.File` and `null` when -fno-emit-bin is passed. It is now arena-allocated along with Compilation itself, avoiding some messy cleanup code that was there before. On the command line, it is now possible to configure the standard library itself by using `--mod std` just like any other module. This meant that the CLI needed to create the standard library module rather than having Compilation create it. There are a lot of changes in this commit and it's still not done. I didn't realize how quickly this changeset was going to balloon out of control, and there are still many lines that need to be changed before it even compiles successfully. * introduce std.Build.Cache.HashHelper.oneShot * add error_tracing to std.Build.Module * extract build.zig file generation into src/Builtin.zig * each CSourceFile and RcSourceFile now has a Module owner, which determines some of the C compiler flags. --- CMakeLists.txt | 1 + lib/std/Build/Cache.zig | 14 + lib/std/Build/Module.zig | 4 + src/Builtin.zig | 240 ++++++ src/Compilation.zig | 1457 ++++++++++--------------------- src/Compilation/Config.zig | 382 +++++++++ src/Module.zig | 8 +- src/Package/Module.zig | 409 ++++++++- src/Sema.zig | 2 +- src/codegen/llvm.zig | 38 +- src/link.zig | 525 ++++-------- src/link/Coff.zig | 315 +++---- src/link/Elf.zig | 186 ++-- src/link/MachO.zig | 257 +++--- src/main.zig | 2045 ++++++++++++++++++++++++-------------------- src/target.zig | 61 +- 16 files changed, 3250 insertions(+), 2694 deletions(-) create mode 100644 src/Builtin.zig create mode 100644 src/Compilation/Config.zig (limited to 'src/Sema.zig') diff --git a/CMakeLists.txt b/CMakeLists.txt index b3004d3254..202c5d1861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -521,6 +521,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/Air.zig" "${CMAKE_SOURCE_DIR}/src/AstGen.zig" "${CMAKE_SOURCE_DIR}/src/Compilation.zig" + "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig" "${CMAKE_SOURCE_DIR}/src/Liveness.zig" "${CMAKE_SOURCE_DIR}/src/Module.zig" "${CMAKE_SOURCE_DIR}/src/Package.zig" diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index b7865e730b..18c1ffa07a 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -312,6 +312,20 @@ pub const HashHelper = struct { ) catch unreachable; return out_digest; } + + pub fn oneShot(bytes: []const u8) [hex_digest_len]u8 { + var hasher: Hasher = hasher_init; + hasher.update(bytes); + var bin_digest: BinDigest = undefined; + hasher.final(&bin_digest); + var out_digest: [hex_digest_len]u8 = undefined; + _ = fmt.bufPrint( + &out_digest, + "{s}", + .{fmt.fmtSliceHexLower(&bin_digest)}, + ) catch unreachable; + return out_digest; + } }; pub const Lock = struct { diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 81fab2be5e..9daa89deb8 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -34,6 +34,7 @@ valgrind: ?bool, pic: ?bool, red_zone: ?bool, omit_frame_pointer: ?bool, +error_tracing: ?bool, link_libc: ?bool, link_libcpp: ?bool, @@ -177,6 +178,7 @@ pub const CreateOptions = struct { /// Whether to omit the stack frame pointer. Frees up a register and makes it /// more difficult to obtain stack traces. Has target-dependent effects. omit_frame_pointer: ?bool = null, + error_tracing: ?bool = null, }; pub const Import = struct { @@ -216,6 +218,7 @@ pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*St .pic = options.pic, .red_zone = options.red_zone, .omit_frame_pointer = options.omit_frame_pointer, + .error_tracing = options.error_tracing, .export_symbol_names = &.{}, }; @@ -601,6 +604,7 @@ pub fn appendZigProcessFlags( try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check"); try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector"); try addFlag(zig_args, m.omit_frame_pointer, "-fomit-frame-pointer", "-fno-omit-frame-pointer"); + try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing"); try addFlag(zig_args, m.sanitize_c, "-fsanitize-c", "-fno-sanitize-c"); try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread"); try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind"); diff --git a/src/Builtin.zig b/src/Builtin.zig new file mode 100644 index 0000000000..7224a6fd24 --- /dev/null +++ b/src/Builtin.zig @@ -0,0 +1,240 @@ +target: std.Target, +zig_backend: std.builtin.CompilerBackend, +output_mode: std.builtin.OutputMode, +link_mode: std.builtin.LinkMode, +is_test: bool, +test_evented_io: bool, +single_threaded: bool, +link_libc: bool, +link_libcpp: bool, +optimize_mode: std.builtin.OptimizeMode, +error_tracing: bool, +valgrind: bool, +sanitize_thread: bool, +pic: bool, +pie: bool, +strip: bool, +code_model: std.builtin.CodeModel, +omit_frame_pointer: bool, +wasi_exec_model: std.builtin.WasiExecModel, + +pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 { + var buffer = std.ArrayList(u8).init(allocator); + defer buffer.deinit(); + + const target = opts.target; + const generic_arch_name = target.cpu.arch.genericName(); + const zig_backend = opts.zig_backend; + + @setEvalBranchQuota(4000); + try buffer.writer().print( + \\const std = @import("std"); + \\/// 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 = std.SemanticVersion.parse(zig_version_string) catch unreachable; + \\pub const zig_version_string = "{s}"; + \\pub const zig_backend = std.builtin.CompilerBackend.{}; + \\ + \\pub const output_mode = std.builtin.OutputMode.{}; + \\pub const link_mode = std.builtin.LinkMode.{}; + \\pub const is_test = {}; + \\pub const single_threaded = {}; + \\pub const abi = std.Target.Abi.{}; + \\pub const cpu: std.Target.Cpu = .{{ + \\ .arch = .{}, + \\ .model = &std.Target.{}.cpu.{}, + \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{ + \\ + , .{ + build_options.version, + std.zig.fmtId(@tagName(zig_backend)), + std.zig.fmtId(@tagName(opts.output_mode)), + std.zig.fmtId(@tagName(opts.link_mode)), + opts.is_test, + opts.single_threaded, + 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(), 0..) |feature, index_usize| { + const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); + const is_enabled = target.cpu.features.isEnabled(index); + if (is_enabled) { + try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)}); + } + } + try buffer.writer().print( + \\ }}), + \\}}; + \\pub const os = std.Target.Os{{ + \\ .tag = .{}, + \\ .version_range = .{{ + , + .{std.zig.fmtId(@tagName(target.os.tag))}, + ); + + switch (target.os.getVersionRange()) { + .none => try buffer.appendSlice(" .none = {} },\n"), + .semver => |semver| try buffer.writer().print( + \\ .semver = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + semver.min.major, + semver.min.minor, + semver.min.patch, + + semver.max.major, + semver.max.minor, + semver.max.patch, + }), + .linux => |linux| try buffer.writer().print( + \\ .linux = .{{ + \\ .range = .{{ + \\ .min = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ .max = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}, + \\ .glibc = .{{ + \\ .major = {}, + \\ .minor = {}, + \\ .patch = {}, + \\ }}, + \\ }}}}, + \\ + , .{ + linux.range.min.major, + linux.range.min.minor, + linux.range.min.patch, + + linux.range.max.major, + linux.range.max.minor, + linux.range.max.patch, + + linux.glibc.major, + linux.glibc.minor, + linux.glibc.patch, + }), + .windows => |windows| try buffer.writer().print( + \\ .windows = .{{ + \\ .min = {s}, + \\ .max = {s}, + \\ }}}}, + \\ + , + .{ windows.min, windows.max }, + ), + } + try buffer.appendSlice( + \\}; + \\pub const target: std.Target = .{ + \\ .cpu = cpu, + \\ .os = os, + \\ .abi = abi, + \\ .ofmt = object_format, + \\ + ); + + if (target.dynamic_linker.get()) |dl| { + try buffer.writer().print( + \\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"), + \\}}; + \\ + , .{dl}); + } else { + try buffer.appendSlice( + \\ .dynamic_linker = std.Target.DynamicLinker.none, + \\}; + \\ + ); + } + + // This is so that compiler_rt and libc.zig libraries know whether they + // will eventually be linked with libc. They make different decisions + // about what to export depending on whether another libc will be linked + // 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 = opts.link_libc; + + try buffer.writer().print( + \\pub const object_format = std.Target.ObjectFormat.{}; + \\pub const mode = std.builtin.OptimizeMode.{}; + \\pub const link_libc = {}; + \\pub const link_libcpp = {}; + \\pub const have_error_return_tracing = {}; + \\pub const valgrind_support = {}; + \\pub const sanitize_thread = {}; + \\pub const position_independent_code = {}; + \\pub const position_independent_executable = {}; + \\pub const strip_debug_info = {}; + \\pub const code_model = std.builtin.CodeModel.{}; + \\pub const omit_frame_pointer = {}; + \\ + , .{ + std.zig.fmtId(@tagName(target.ofmt)), + std.zig.fmtId(@tagName(opts.optimize_mode)), + link_libc, + opts.link_libcpp, + opts.error_tracing, + opts.valgrind, + opts.sanitize_thread, + opts.pic, + opts.pie, + opts.strip, + std.zig.fmtId(@tagName(opts.code_model)), + opts.omit_frame_pointer, + }); + + if (target.os.tag == .wasi) { + const wasi_exec_model_fmt = std.zig.fmtId(@tagName(opts.wasi_exec_model)); + try buffer.writer().print( + \\pub const wasi_exec_model = std.builtin.WasiExecModel.{}; + \\ + , .{wasi_exec_model_fmt}); + } + + if (opts.is_test) { + try buffer.appendSlice( + \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later + \\ + ); + if (opts.test_evented_io) { + try buffer.appendSlice( + \\pub const test_io_mode = .evented; + \\ + ); + } else { + try buffer.appendSlice( + \\pub const test_io_mode = .blocking; + \\ + ); + } + } + + return buffer.toOwnedSliceSentinel(0); +} + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const build_options = @import("build_options"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 4f65dc22f2..ca43e2da07 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -38,12 +38,43 @@ const Autodoc = @import("Autodoc.zig"); const Color = @import("main.zig").Color; const resinator = @import("resinator.zig"); +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`. arena: std.heap.ArenaAllocator, -bin_file: *link.File, +/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. +/// TODO: rename to zcu: ?*Zcu +module: ?*Module, +/// All compilations have a root module because this is where some important +/// settings are stored, such as target and optimization mode. This module +/// might not have any .zig code associated with it, however. +root_mod: *Package.Module, + +/// User-specified settings that have all the defaults resolved into concrete values. +config: Config, + +/// This is `null` when `-fno-emit-bin` is used. +bin_file: ?*link.File, + +/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) +sysroot: ?[]const u8, +/// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used. +implib_emit: ?Emit, +/// This is non-null when `-femit-docs` is provided. +docs_emit: ?Emit, +root_name: [:0]const u8, +cache_mode: CacheMode, +include_compiler_rt: bool, +objects: []Compilation.LinkObject, +/// These are *always* dynamically linked. Static libraries will be +/// provided as positional arguments. +system_libs: std.StringArrayHashMapUnmanaged(SystemLib), +version: ?std.SemanticVersion, +libc_installation: ?*const LibCInstallation, + c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = if (build_options.only_core_functionality) {} else .{}, @@ -87,8 +118,6 @@ failed_win32_resources: if (build_options.only_core_functionality) void else std misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, keep_source_files_loaded: bool, -c_frontend: CFrontend, -sanitize_c: bool, /// When this is `true` it means invoking clang as a sub-process is expected to inherit /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. /// Otherwise we attempt to parse the error messages and expose them via the Compilation API. @@ -107,8 +136,6 @@ verbose_llvm_cpu_features: bool, disable_c_depfile: bool, time_report: bool, stack_report: bool, -unwind_tables: bool, -test_evented_io: bool, debug_compiler_runtime_libs: bool, debug_compile_errors: bool, job_queued_compiler_rt_lib: bool = false, @@ -118,7 +145,6 @@ formatted_panics: bool = false, last_update_was_cache_hit: bool = false, c_source_files: []const CSourceFile, -clang_argv: []const []const u8, rc_source_files: []const RcSourceFile, cache_parent: *Cache, /// Path to own executable for invoking `zig clang`. @@ -194,7 +220,29 @@ emit_llvm_bc: ?EmitLoc, work_queue_wait_group: WaitGroup = .{}, astgen_wait_group: WaitGroup = .{}, -pub const default_stack_protector_buffer_size = 4; +pub const Emit = struct { + /// Where the output will go. + directory: Directory, + /// Path to the output file, relative to `directory`. + sub_path: []const u8, + + /// Returns the full path to `basename` if it were in the same directory as the + /// `Emit` sub_path. + pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 { + const full_path = if (emit.directory.path) |p| + try std.fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) + else + emit.sub_path; + + if (std.fs.path.dirname(full_path)) |dirname| { + return try std.fs.path.joinZ(arena, &.{ dirname, basename }); + } else { + return basename; + } + } +}; + +pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Module.SemaError; pub const CRTFile = struct { @@ -208,8 +256,8 @@ pub const CRTFile = struct { } }; -// supported languages for "zig clang -x ". -// Loosely based on llvm-project/clang/include/clang/Driver/Types.def +/// Supported languages for "zig clang -x ". +/// Loosely based on llvm-project/clang/include/clang/Driver/Types.def pub const LangToExt = std.ComptimeStringMap(FileExt, .{ .{ "c", .c }, .{ "c-header", .h }, @@ -226,16 +274,20 @@ pub const LangToExt = std.ComptimeStringMap(FileExt, .{ /// For passing to a C compiler. pub const CSourceFile = struct { + /// Many C compiler flags are determined by settings contained in the owning Module. + owner: *Package.Module, src_path: []const u8, extra_flags: []const []const u8 = &.{}, /// Same as extra_flags except they are not added to the Cache hash. cache_exempt_flags: []const []const u8 = &.{}, - // this field is non-null iff language was explicitly set with "-x lang". + /// This field is non-null if and only if the language was explicitly set + /// with "-x lang". ext: ?FileExt = null, }; /// For passing to resinator. pub const RcSourceFile = struct { + owner: *Package.Module, src_path: []const u8, extra_flags: []const []const u8 = &.{}, }; @@ -742,6 +794,22 @@ pub const EmitLoc = struct { }; pub const cache_helpers = struct { + pub fn addResolvedTarget( + hh: *Cache.HashHelper, + resolved_target: Package.Module.ResolvedTarget, + ) void { + const target = resolved_target.result; + hh.add(target.cpu.arch); + hh.addBytes(target.cpu.model.name); + hh.add(target.cpu.features.ints); + hh.add(target.os.tag); + hh.add(target.os.getVersionRange()); + hh.add(target.abi); + hh.add(target.ofmt); + hh.add(resolved_target.is_native_os); + hh.add(resolved_target.is_native_abi); + } + pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void { hh.addBytes(emit_loc.basename); } @@ -751,7 +819,7 @@ pub const cache_helpers = struct { addEmitLoc(hh, optional_emit_loc orelse return); } - pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void { + pub fn hashCSource(self: *Cache.Manifest, c_source: CSourceFile) !void { _ = try self.addFile(c_source.src_path, null); // Hash the extra flags, with special care to call addFile for file parameters. // TODO this logic can likely be improved by utilizing clang_options_data.zig. @@ -770,8 +838,6 @@ pub const cache_helpers = struct { } }; -pub const CFrontend = enum { clang, aro }; - pub const ClangPreprocessorMode = enum { no, /// This means we are doing `zig cc -E -o `. @@ -798,11 +864,21 @@ pub const InitOptions = struct { zig_lib_directory: Directory, local_cache_directory: Directory, global_cache_directory: Directory, - target: Target, - root_name: []const u8, - main_mod: ?*Package.Module, - output_mode: std.builtin.OutputMode, thread_pool: *ThreadPool, + self_exe_path: ?[]const u8 = null, + + /// Options that have been resolved by calling `resolveDefaults`. + config: Compilation.Config, + + root_mod: *Package.Module, + /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig + /// test`, in which `root_mod` is the test runner, and `main_mod` is the + /// user's source file which has the tests. + main_mod: ?*Package.Module, + /// This is provided so that the API user has a chance to tweak the + /// per-module settings of the standard library. + std_mod: *Package.Module, + root_name: []const u8, sysroot: ?[]const u8 = null, /// `null` means to not emit a binary file. emit_bin: ?EmitLoc, @@ -818,7 +894,6 @@ pub const InitOptions = struct { emit_docs: ?EmitLoc = null, /// `null` means to not emit an import lib. emit_implib: ?EmitLoc = null, - link_mode: ?std.builtin.LinkMode = null, dll_export_fns: ?bool = false, /// Normally when using LLD to link, Zig uses a file named "lld.id" in the /// same directory as the output binary which contains the hash of the link @@ -828,14 +903,12 @@ pub const InitOptions = struct { /// this flag would be set to disable this machinery to avoid false positives. disable_lld_caching: bool = false, cache_mode: CacheMode = .incremental, - optimize_mode: std.builtin.OptimizeMode = .Debug, keep_source_files_loaded: bool = false, - clang_argv: []const []const u8 = &[0][]const u8{}, lib_dirs: []const []const u8 = &[0][]const u8{}, rpath_list: []const []const u8 = &[0][]const u8{}, symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}, - c_source_files: []const CSourceFile = &[0]CSourceFile{}, - rc_source_files: []const RcSourceFile = &[0]RcSourceFile{}, + c_source_files: []const CSourceFile = &.{}, + rc_source_files: []const RcSourceFile = &.{}, manifest_file: ?[]const u8 = null, rc_includes: RcIncludes = .any, link_objects: []LinkObject = &[0]LinkObject{}, @@ -849,40 +922,16 @@ pub const InitOptions = struct { /// * mman /// * signal wasi_emulated_libs: []const wasi_libc.CRTFile = &[0]wasi_libc.CRTFile{}, - link_libc: bool = false, - link_libcpp: bool = false, - link_libunwind: bool = false, - want_pic: ?bool = null, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. - want_pie: ?bool = null, - want_sanitize_c: ?bool = null, - want_stack_check: ?bool = null, - /// null means default. - /// 0 means no stack protector. - /// other number means stack protection with that buffer size. - want_stack_protector: ?u32 = null, - want_red_zone: ?bool = null, - omit_frame_pointer: ?bool = null, - want_valgrind: ?bool = null, - want_tsan: ?bool = null, want_compiler_rt: ?bool = null, want_lto: ?bool = null, - want_unwind_tables: ?bool = null, - use_llvm: ?bool = null, - use_lib_llvm: ?bool = null, - use_lld: ?bool = null, - use_clang: ?bool = null, - single_threaded: ?bool = null, - strip: ?bool = null, formatted_panics: ?bool = null, rdynamic: bool = false, function_sections: bool = false, data_sections: bool = false, no_builtin: bool = false, - is_native_os: bool, - is_native_abi: bool, time_report: bool = false, stack_report: bool = false, link_eh_frame_hdr: bool = false, @@ -893,14 +942,11 @@ pub const InitOptions = struct { linker_gc_sections: ?bool = null, linker_allow_shlib_undefined: ?bool = null, linker_bind_global_refs_locally: ?bool = null, - linker_import_memory: ?bool = null, - linker_export_memory: ?bool = null, linker_import_symbols: bool = false, linker_import_table: bool = false, linker_export_table: bool = false, linker_initial_memory: ?u64 = null, linker_max_memory: ?u64 = null, - linker_shared_memory: bool = false, linker_global_base: ?u64 = null, linker_export_symbol_names: []const []const u8 = &.{}, linker_print_gc_sections: bool = false, @@ -938,8 +984,6 @@ pub const InitOptions = struct { verbose_llvm_bc: ?[]const u8 = null, verbose_cimport: bool = false, verbose_llvm_cpu_features: bool = false, - is_test: bool = false, - test_evented_io: bool = false, debug_compiler_runtime_libs: bool = false, debug_compile_errors: bool = false, /// Normally when you create a `Compilation`, Zig will automatically build @@ -952,23 +996,18 @@ pub const InitOptions = struct { force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{}, stack_size_override: ?u64 = null, image_base_override: ?u64 = null, - self_exe_path: ?[]const u8 = null, version: ?std.SemanticVersion = null, compatibility_version: ?std.SemanticVersion = null, libc_installation: ?*const LibCInstallation = null, - machine_code_model: std.builtin.CodeModel = .default, clang_preprocessor_mode: ClangPreprocessorMode = .no, /// This is for stage1 and should be deleted upon completion of self-hosting. color: Color = .auto, reference_trace: ?u32 = null, - error_tracing: ?bool = null, test_filter: ?[]const u8 = null, test_name_prefix: ?[]const u8 = null, test_runner_path: ?[]const u8 = null, subsystem: ?std.Target.SubSystem = null, dwarf_format: ?std.dwarf.Format = null, - /// WASI-only. Type of WASI execution model ("command" or "reactor"). - wasi_exec_model: ?std.builtin.WasiExecModel = null, /// (Zig compiler development) Enable dumping linker's state as JSON. enable_link_snapshots: bool = false, /// (Darwin) Install name of the dylib @@ -989,77 +1028,93 @@ pub const InitOptions = struct { pdb_source_path: ?[]const u8 = null, /// (Windows) PDB output path pdb_out_path: ?[]const u8 = null, - error_limit: ?Module.ErrorInt = null, + error_limit: ?Compilation.Module.ErrorInt = null, /// (SPIR-V) whether to generate a structured control flow graph or not want_structured_cfg: ?bool = null, }; fn addModuleTableToCacheHash( + gpa: Allocator, + arena: Allocator, hash: *Cache.HashHelper, - arena: *std.heap.ArenaAllocator, - mod_table: Package.Module.Deps, - seen_table: *std.AutoHashMap(*Package.Module, void), + root_mod: *Package.Module, hash_type: union(enum) { path_bytes, files: *Cache.Manifest }, ) (error{OutOfMemory} || std.os.GetCwdError)!void { - const allocator = arena.allocator(); - - const module_indices = try allocator.alloc(u32, mod_table.count()); - // Copy over the hashmap entries to our slice - for (module_indices, 0..) |*module_index, index| module_index.* = @intCast(index); - // Sort the slice by package name - mem.sortUnstable(u32, module_indices, &mod_table, struct { - fn lessThan(deps: *const Package.Module.Deps, lhs: u32, rhs: u32) bool { - const keys = deps.keys(); - return std.mem.lessThan(u8, keys[lhs], keys[rhs]); + var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + try seen_table.put(gpa, root_mod, {}); + + const SortByName = struct { + names: []const []const u8, + + pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { + const lhs_key = ctx.names[lhs_index]; + const rhs_key = ctx.names[rhs_index]; + return mem.lessThan(u8, lhs_key, rhs_key); } - }.lessThan); + }; - for (module_indices) |module_index| { - const module = mod_table.values()[module_index]; - if ((try seen_table.getOrPut(module)).found_existing) continue; + var i: usize = 0; + while (i < seen_table.count()) : (i += 1) { + const mod = seen_table.keys()[i]; + + cache_helpers.addResolvedTarget(hash, mod.resolved_target); + hash.add(mod.optimize_mode); + hash.add(mod.code_model); + hash.add(mod.single_threaded); + hash.add(mod.error_tracing); + hash.add(mod.valgrind); + hash.add(mod.pic); + hash.add(mod.strip); + hash.add(mod.omit_frame_pointer); + hash.add(mod.stack_check); + hash.add(mod.red_zone); + hash.add(mod.sanitize_c); + hash.add(mod.sanitize_thread); - // Finally insert the package name and path to the cache hash. - hash.addBytes(mod_table.keys()[module_index]); switch (hash_type) { .path_bytes => { - hash.addBytes(module.root_src_path); - hash.addOptionalBytes(module.root.root_dir.path); - hash.addBytes(module.root.sub_path); + hash.addBytes(mod.root_src_path); + hash.addOptionalBytes(mod.root.root_dir.path); + hash.addBytes(mod.root.sub_path); }, .files => |man| { - const pkg_zig_file = try module.root.joinString( - allocator, - module.root_src_path, - ); + const pkg_zig_file = try mod.root.joinString(arena, mod.root_src_path); _ = try man.addFile(pkg_zig_file, null); }, } - // Recurse to handle the module's dependencies - try addModuleTableToCacheHash(hash, arena, module.deps, seen_table, hash_type); + + mod.deps.sortUnstable(SortByName{ .names = mod.deps.keys() }); + + hash.addListOfBytes(mod.deps.keys()); + + const deps = mod.deps.values(); + try seen_table.ensureUnusedCapacity(gpa, deps.len); + for (deps) |dep| seen_table.putAssumeCapacity(dep, {}); } } pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { - const is_dyn_lib = switch (options.output_mode) { + const output_mode = options.config.output_mode; + const is_dyn_lib = switch (output_mode) { .Obj, .Exe => false, - .Lib => (options.link_mode orelse .Static) == .Dynamic, + .Lib => options.config.link_mode == .Dynamic, }; - const is_exe_or_dyn_lib = switch (options.output_mode) { + const is_exe_or_dyn_lib = switch (output_mode) { .Obj => false, .Lib => is_dyn_lib, .Exe => true, }; - // WASI-only. Resolve the optional exec-model option, defaults to command. - const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command; - if (options.linker_export_table and options.linker_import_table) { return error.ExportTableAndImportTableConflict; } + const have_zcu = options.root_mod.root_src_path.len != 0; + const comp: *Compilation = comp: { - // For allocations that have the same lifetime as Compilation. This arena is used only during this - // initialization and then is freed in deinit(). + // For allocations that have the same lifetime as Compilation. This + // arena is used only during this initialization and then is freed in + // deinit(). var arena_allocator = std.heap.ArenaAllocator.init(gpa); errdefer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -1069,366 +1124,145 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const comp = try arena.create(Compilation); const root_name = try arena.dupeZ(u8, options.root_name); - // Make a decision on whether to use LLVM or our own backend. - const use_lib_llvm = options.use_lib_llvm orelse build_options.have_llvm; - const use_llvm = blk: { - if (options.use_llvm) |explicit| - break :blk explicit; - - // If emitting to LLVM bitcode object format, must use LLVM backend. - if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) - break :blk true; - - // If we have no zig code to compile, no need for LLVM. - if (options.main_mod == null) - break :blk false; - - // If we cannot use LLVM libraries, then our own backends will be a - // better default since the LLVM backend can only produce bitcode - // and not an object file or executable. - if (!use_lib_llvm) - break :blk false; - - // If LLVM does not support the target, then we can't use it. - if (!target_util.hasLlvmSupport(options.target, options.target.ofmt)) - break :blk false; - - // Prefer LLVM for release builds. - if (options.optimize_mode != .Debug) - break :blk true; - - // At this point we would prefer to use our own self-hosted backend, - // because the compilation speed is better than LLVM. But only do it if - // we are confident in the robustness of the backend. - break :blk !target_util.selfHostedBackendIsAsRobustAsLlvm(options.target); - }; - if (!use_llvm) { - if (options.use_llvm == true) { - return error.ZigCompilerNotBuiltWithLLVMExtensions; - } - if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { - return error.EmittingLlvmModuleRequiresUsingLlvmBackend; - } - } + const use_llvm = options.config.use_llvm; // TODO: once we support incremental compilation for the LLVM backend via // saving the LLVM module into a bitcode file and restoring it, along with // compiler state, the second clause here can be removed so that incremental // cache mode is used for LLVM backend too. We need some fuzz testing before // that can be enabled. - const cache_mode = if ((use_llvm or options.main_mod == null) and !options.disable_lld_caching) + const cache_mode = if ((use_llvm or !have_zcu) and !options.disable_lld_caching) CacheMode.whole else options.cache_mode; - const tsan = options.want_tsan orelse false; - // TSAN is implemented in C++ so it requires linking libc++. - const link_libcpp = options.link_libcpp or tsan; - const link_libc = link_libcpp or options.link_libc or options.link_libunwind or - target_util.osRequiresLibC(options.target); - - const link_libunwind = options.link_libunwind or - (link_libcpp and target_util.libcNeedsLibUnwind(options.target)); - const unwind_tables = options.want_unwind_tables orelse - (link_libunwind or target_util.needUnwindTables(options.target)); - const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables; - const build_id = options.build_id orelse .none; + const any_unwind_tables = options.config.any_unwind_tables; - // Make a decision on whether to use LLD or our own linker. - const use_lld = options.use_lld orelse blk: { - if (options.target.isDarwin()) { - break :blk false; - } - - if (!build_options.have_llvm) - break :blk false; - - if (options.target.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 - options.frameworks.len != 0 or - options.system_lib_names.len != 0 or - options.link_libc or options.link_libcpp or - link_eh_frame_hdr or - options.link_emit_relocs or - options.output_mode == .Lib or - options.linker_script != null or options.version_script != null or - options.emit_implib != null or - build_id != .none or - options.symbol_wrap_set.count() > 0) - { - break :blk true; - } - - if (use_llvm) { - // If stage1 generates an object file, self-hosted linker is not - // yet sophisticated enough to handle that. - break :blk options.main_mod != null; - } - - break :blk false; - }; - - const lto = blk: { - if (options.want_lto) |want_lto| { - if (want_lto and !use_lld and !options.target.isDarwin()) - return error.LtoUnavailableWithoutLld; - break :blk want_lto; - } else if (!use_lld) { - // zig ld LTO support is tracked by - // https://github.com/ziglang/zig/issues/8680 - break :blk false; - } else if (options.c_source_files.len == 0) { - break :blk false; - } else if (options.target.cpu.arch.isRISCV()) { - // Clang and LLVM currently don't support RISC-V target-abi for LTO. - // Compiling with LTO may fail or produce undesired results. - // See https://reviews.llvm.org/D71387 - // See https://reviews.llvm.org/D102582 - 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 must_dynamic_link = dl: { - if (target_util.cannotDynamicLink(options.target)) - break :dl false; - if (is_exe_or_dyn_lib and link_libc and - (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target))) - { - break :dl true; - } - const any_dyn_libs: bool = x: { - if (options.system_lib_names.len != 0) - break :x true; - for (options.link_objects) |obj| { - switch (classifyFileExt(obj.path)) { - .shared_library => break :x true, - else => continue, - } - } - break :x false; - }; - if (any_dyn_libs) { - // When creating a executable that links to system libraries, - // we require dynamic linking, but we must not link static libraries - // or object files dynamically! - break :dl (options.output_mode == .Exe); - } + const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; + const build_id = options.build_id orelse .none; - break :dl false; - }; - const default_link_mode: std.builtin.LinkMode = blk: { - if (must_dynamic_link) { - break :blk .Dynamic; - } else if (is_exe_or_dyn_lib and link_libc and - options.is_native_abi and options.target.abi.isMusl()) - { - // If targeting the system's native ABI and the system's - // libc is musl, link dynamically by default. - break :blk .Dynamic; - } else { - break :blk .Static; - } - }; - const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: { - if (lm == .Static and must_dynamic_link) { - return error.UnableToStaticLink; - } - break :blk lm; - } else default_link_mode; + const link_libc = options.config.link_libc; const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic); const libc_dirs = try detectLibCIncludeDirs( arena, options.zig_lib_directory.path.?, - options.target, - options.is_native_abi, + options.root_mod.resolved_target.result, + options.root_mod.resolved_target.is_native_abi, link_libc, options.libc_installation, ); - const rc_dirs = try detectWin32ResourceIncludeDirs( - arena, - options, - ); - - const sysroot = options.sysroot orelse libc_dirs.sysroot; - - const pie: bool = pie: { - if (is_dyn_lib) { - if (options.want_pie == true) return error.OutputModeForbidsPie; - break :pie false; - } - if (target_util.requiresPIE(options.target)) { - if (options.want_pie == false) return error.TargetRequiresPie; - break :pie true; - } - if (tsan) { - if (options.want_pie == false) return error.TsanRequiresPie; - break :pie true; - } - if (options.want_pie) |want_pie| { - break :pie want_pie; - } - break :pie false; - }; - - const must_pic: bool = b: { - if (target_util.requiresPIC(options.target, link_libc)) - break :b true; - break :b link_mode == .Dynamic; - }; - const pic = if (options.want_pic) |explicit| pic: { - if (!explicit) { - if (must_pic) { - return error.TargetRequiresPIC; - } - if (pie) { - return error.PIERequiresPIC; + // The include directories used when preprocessing .rc files are separate from the + // target. Which include directories are used is determined by `options.rc_includes`. + // + // Note: It should be okay that the include directories used when compiling .rc + // files differ from the include directories used when compiling the main + // binary, since the .res format is not dependent on anything ABI-related. The + // only relevant differences would be things like `#define` constants being + // different in the MinGW headers vs the MSVC headers, but any such + // differences would likely be a MinGW bug. + const rc_dirs = b: { + // Set the includes to .none here when there are no rc files to compile + var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none; + const target = options.root_mod.resolved_target.result; + if (!options.root_mod.resolved_target.is_native_os or target.os.tag != .windows) { + switch (includes) { + // MSVC can't be found when the host isn't Windows, so short-circuit. + .msvc => return error.WindowsSdkNotFound, + // Skip straight to gnu since we won't be able to detect + // MSVC on non-Windows hosts. + .any => includes = .gnu, + .none, .gnu => {}, } } - break :pic explicit; - } else pie or must_pic; - - // Make a decision on whether to use Clang or Aro for translate-c and compiling C files. - const c_frontend: CFrontend = blk: { - if (options.use_clang) |want_clang| { - break :blk if (want_clang) .clang else .aro; - } - break :blk if (build_options.have_llvm) .clang else .aro; - }; - if (!build_options.have_llvm and c_frontend == .clang) { - return error.ZigCompilerNotBuiltWithLLVMExtensions; - } - - const is_safe_mode = switch (options.optimize_mode) { - .Debug, .ReleaseSafe => true, - .ReleaseFast, .ReleaseSmall => false, - }; - - const sanitize_c = options.want_sanitize_c orelse is_safe_mode; - - const stack_check: bool = options.want_stack_check orelse b: { - if (!target_util.supportsStackProbing(options.target)) break :b false; - break :b is_safe_mode; + while (true) switch (includes) { + .any, .msvc => break :b detectLibCIncludeDirs( + arena, + options.zig_lib_directory.path.?, + .{ + .cpu = target.cpu, + .os = target.os, + .abi = .msvc, + .ofmt = target.ofmt, + }, + options.root_mod.resolved_target.is_native_abi, + // The .rc preprocessor will need to know the libc include dirs even if we + // are not linking libc, so force 'link_libc' to true + true, + options.libc_installation, + ) catch |err| { + if (includes == .any) { + // fall back to mingw + includes = .gnu; + continue; + } + return err; + }, + .gnu => break :b try detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{ + .cpu = target.cpu, + .os = target.os, + .abi = .gnu, + .ofmt = target.ofmt, + }), + .none => break :b LibCDirs{ + .libc_include_dir_list = &[0][]u8{}, + .libc_installation = null, + .libc_framework_dir_list = &.{}, + .sysroot = null, + .darwin_sdk_layout = null, + }, + }; }; - if (stack_check and !target_util.supportsStackProbing(options.target)) - return error.StackCheckUnsupportedByTarget; - - const stack_protector: u32 = sp: { - const zig_backend = zigBackend(options.target, use_llvm); - if (!target_util.supportsStackProtector(options.target, zig_backend)) { - if (options.want_stack_protector) |x| { - if (x > 0) return error.StackProtectorUnsupportedByTarget; - } - break :sp 0; - } - - // This logic is checking for linking libc because otherwise our start code - // which is trying to set up TLS (i.e. the fs/gs registers) but the stack - // protection code depends on fs/gs registers being already set up. - // If we were able to annotate start code, or perhaps the entire std lib, - // as being exempt from stack protection checks, we could change this logic - // to supporting stack protection even when not linking libc. - // TODO file issue about this - if (!link_libc) { - if (options.want_stack_protector) |x| { - if (x > 0) return error.StackProtectorUnavailableWithoutLibC; - } - break :sp 0; - } - if (options.want_stack_protector) |x| break :sp x; - if (is_safe_mode) break :sp default_stack_protector_buffer_size; - break :sp 0; - }; + const sysroot = options.sysroot orelse libc_dirs.sysroot; const include_compiler_rt = options.want_compiler_rt orelse (!options.skip_linker_dependencies and is_exe_or_dyn_lib); - const single_threaded = st: { - if (target_util.isSingleThreaded(options.target)) { - if (options.single_threaded == false) - return error.TargetRequiresSingleThreaded; - break :st true; - } - if (options.main_mod != null) { - const zig_backend = zigBackend(options.target, use_llvm); - if (!target_util.supportsThreads(options.target, zig_backend)) { - if (options.single_threaded == false) - return error.BackendRequiresSingleThreaded; - break :st true; - } - } - break :st options.single_threaded orelse false; - }; - - const llvm_cpu_features: ?[*:0]const u8 = if (use_llvm) blk: { - var buf = std.ArrayList(u8).init(arena); - for (options.target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { - const index = @as(Target.Cpu.Feature.Set.Index, @intCast(index_usize)); - const is_enabled = options.target.cpu.features.isEnabled(index); - - if (feature.llvm_name) |llvm_name| { - const plus_or_minus = "-+"[@intFromBool(is_enabled)]; - try buf.ensureUnusedCapacity(2 + llvm_name.len); - buf.appendAssumeCapacity(plus_or_minus); - buf.appendSliceAssumeCapacity(llvm_name); - buf.appendSliceAssumeCapacity(","); - } - } - if (buf.items.len == 0) break :blk ""; - assert(mem.endsWith(u8, buf.items, ",")); - buf.items[buf.items.len - 1] = 0; - buf.shrinkAndFree(buf.items.len); - break :blk buf.items[0 .. buf.items.len - 1 :0].ptr; - } else null; + if (include_compiler_rt and output_mode == .Obj) { + // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");` + // injected into the object. + const compiler_rt_mod = try Package.Module.create(arena, .{ + .global_cache_directory = options.global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = options.zig_lib_directory, + }, + .root_src_path = "compiler_rt.zig", + }, + .fully_qualified_name = "compiler_rt", + .cc_argv = &.{}, + .inherited = .{}, + .global = options.config, + .parent = options.root_mod, + .builtin_mod = options.root_mod.getBuiltinDependency(), + }); + try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod); + } if (options.verbose_llvm_cpu_features) { - if (llvm_cpu_features) |cf| print: { + if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: { + const target = options.root_mod.resolved_target.result; std.debug.getStderrMutex().lock(); defer std.debug.getStderrMutex().unlock(); const stderr = std.io.getStdErr().writer(); - nosuspend stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print; - nosuspend stderr.print(" target: {s}\n", .{try options.target.zigTriple(arena)}) catch break :print; - nosuspend stderr.print(" cpu: {s}\n", .{options.target.cpu.model.name}) catch break :print; - nosuspend stderr.print(" features: {s}\n", .{cf}) catch {}; + nosuspend { + stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print; + stderr.print(" target: {s}\n", .{try target.zigTriple(arena)}) catch break :print; + stderr.print(" cpu: {s}\n", .{target.cpu.model.name}) catch break :print; + stderr.print(" features: {s}\n", .{cf}) catch {}; + } } } - const strip = options.strip orelse !target_util.hasDebugInfo(options.target); - const valgrind: bool = b: { - if (!target_util.hasValgrindSupport(options.target)) break :b false; - if (options.want_valgrind) |explicit| break :b explicit; - if (strip) break :b false; - break :b options.optimize_mode == .Debug; - }; - if (!valgrind and options.want_valgrind == true) - return error.ValgrindUnsupportedOnTarget; - - const red_zone = options.want_red_zone orelse target_util.hasRedZone(options.target); - const omit_frame_pointer = options.omit_frame_pointer orelse (options.optimize_mode != .Debug); - const linker_optimization: u8 = options.linker_optimization orelse switch (options.optimize_mode) { + const linker_optimization: u8 = options.linker_optimization orelse switch (options.root_mod.optimize_mode) { .Debug => @as(u8, 0), else => @as(u8, 3), }; - const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug); + // TODO: https://github.com/ziglang/zig/issues/17969 + const formatted_panics = options.formatted_panics orelse (options.root_mod.optimize_mode == .Debug); const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1); @@ -1453,43 +1287,25 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // This is shared hasher state common to zig source and all C source files. cache.hash.addBytes(build_options.version); cache.hash.add(builtin.zig_backend); - cache.hash.add(options.optimize_mode); - cache.hash.add(options.target.cpu.arch); - cache.hash.addBytes(options.target.cpu.model.name); - cache.hash.add(options.target.cpu.features.ints); - cache.hash.add(options.target.os.tag); - cache.hash.add(options.target.os.getVersionRange()); - cache.hash.add(options.is_native_os); - cache.hash.add(options.target.abi); - cache.hash.add(options.target.ofmt); - cache.hash.add(pic); - cache.hash.add(pie); - cache.hash.add(lto); - cache.hash.add(unwind_tables); - cache.hash.add(tsan); - cache.hash.add(stack_check); - cache.hash.add(stack_protector); - cache.hash.add(red_zone); - cache.hash.add(omit_frame_pointer); - cache.hash.add(link_mode); + cache.hash.add(options.config.pie); + cache.hash.add(options.config.lto); + cache.hash.add(options.config.link_mode); cache.hash.add(options.function_sections); cache.hash.add(options.data_sections); cache.hash.add(options.no_builtin); - cache.hash.add(strip); cache.hash.add(link_libc); - cache.hash.add(link_libcpp); - cache.hash.add(link_libunwind); - cache.hash.add(options.output_mode); - cache.hash.add(options.machine_code_model); + cache.hash.add(options.config.link_libcpp); + cache.hash.add(options.config.link_libunwind); + cache.hash.add(output_mode); cache.hash.addOptional(options.dwarf_format); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs); cache.hash.addBytes(options.root_name); - if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model); + cache.hash.add(options.config.wasi_exec_model); // TODO audit this and make sure everything is in it - const module: ?*Module = if (options.main_mod) |main_mod| blk: { + const zcu: ?*Module = if (have_zcu) blk: { // Options that are specific to zig source files, that cannot be // modified between incremental updates. var hash = cache.hash; @@ -1502,13 +1318,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // do want to namespace different source file names because they are // likely different compilations and therefore this would be likely to // cause cache hits. - hash.addBytes(main_mod.root_src_path); - hash.addOptionalBytes(main_mod.root.root_dir.path); - hash.addBytes(main_mod.root.sub_path); - { - var seen_table = std.AutoHashMap(*Package.Module, void).init(arena); - try addModuleTableToCacheHash(&hash, &arena_allocator, main_mod.deps, &seen_table, .path_bytes); - } + try addModuleTableToCacheHash(gpa, arena, &hash, options.root_mod, .path_bytes); }, .whole => { // In this case, we postpone adding the input source file until @@ -1518,13 +1328,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } // Synchronize with other matching comments: ZigOnlyHashStuff - hash.add(valgrind); - hash.add(single_threaded); hash.add(use_llvm); - hash.add(use_lib_llvm); + hash.add(options.config.use_lib_llvm); hash.add(dll_export_fns); - hash.add(options.is_test); - hash.add(options.test_evented_io); + hash.add(options.config.is_test); + hash.add(options.config.test_evented_io); hash.addOptionalBytes(options.test_filter); hash.addOptionalBytes(options.test_name_prefix); hash.add(options.skip_linker_dependencies); @@ -1565,85 +1373,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), }; - const builtin_mod = try Package.Module.create(arena, .{ - .root = .{ .root_dir = zig_cache_artifact_directory }, - .root_src_path = "builtin.zig", - .fully_qualified_name = "builtin", - }); - - // When you're testing std, the main module is std. In that case, - // we'll just set the std module to the main one, since avoiding - // the errors caused by duplicating it is more effort than it's - // worth. - const main_mod_is_std = m: { - const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ - options.zig_lib_directory.path orelse ".", - "std", - "std.zig", - }); - const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ - main_mod.root.root_dir.path orelse ".", - main_mod.root.sub_path, - main_mod.root_src_path, - }); - break :m mem.eql(u8, main_path, std_path); - }; - - const std_mod = if (main_mod_is_std) - main_mod - else - try Package.Module.create(arena, .{ - .root = .{ - .root_dir = options.zig_lib_directory, - .sub_path = "std", - }, - .root_src_path = "std.zig", - .fully_qualified_name = "std", - }); - - const root_mod = if (options.is_test) root_mod: { - const test_mod = if (options.test_runner_path) |test_runner| test_mod: { - const pkg = try Package.Module.create(arena, .{ - .root = .{ - .root_dir = Directory.cwd(), - .sub_path = std.fs.path.dirname(test_runner) orelse "", - }, - .root_src_path = std.fs.path.basename(test_runner), - .fully_qualified_name = "root", - }); - - pkg.deps = try main_mod.deps.clone(arena); - break :test_mod pkg; - } else try Package.Module.create(arena, .{ - .root = .{ - .root_dir = options.zig_lib_directory, - }, - .root_src_path = "test_runner.zig", - .fully_qualified_name = "root", - }); - - break :root_mod test_mod; - } else main_mod; - - const compiler_rt_mod = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_mod: { - break :compiler_rt_mod try Package.Module.create(arena, .{ - .root = .{ - .root_dir = options.zig_lib_directory, - }, - .root_src_path = "compiler_rt.zig", - .fully_qualified_name = "compiler_rt", - }); - } else null; - - { - try main_mod.deps.ensureUnusedCapacity(arena, 4); - main_mod.deps.putAssumeCapacity("builtin", builtin_mod); - main_mod.deps.putAssumeCapacity("root", root_mod); - main_mod.deps.putAssumeCapacity("std", std_mod); - if (compiler_rt_mod) |m| - main_mod.deps.putAssumeCapacity("compiler_rt", m); - } - // Pre-open the directory handles for cached ZIR code so that it does not need // to redundantly happen for each AstGen operation. const zir_sub_dir = "z"; @@ -1674,13 +1403,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // However we currently do not have serialization of such metadata, so for now // we set up an empty Module that does the entire compilation fresh. - const module = try arena.create(Module); - errdefer module.deinit(); - module.* = .{ + const zcu = try arena.create(Module); + errdefer zcu.deinit(); + zcu.* = .{ .gpa = gpa, .comp = comp, - .main_mod = main_mod, - .root_mod = root_mod, + .main_mod = options.main_mod orelse options.root_mod, + .root_mod = options.root_mod, + .std_mod = options.std_mod, .zig_cache_artifact_directory = zig_cache_artifact_directory, .global_zir_cache = global_zir_cache, .local_zir_cache = local_zir_cache, @@ -1688,31 +1418,24 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .tmp_hack_arena = std.heap.ArenaAllocator.init(gpa), .error_limit = error_limit, }; - try module.init(); + try zcu.init(); - break :blk module; + break :blk zcu; } 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) { - .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and - !options.target.cpu.arch.isBpf() and (options.error_tracing orelse true), - .ReleaseFast => options.error_tracing orelse false, - .ReleaseSmall => false, - }; + errdefer if (zcu) |u| u.deinit(); // For resource management purposes. var owned_link_dir: ?std.fs.Dir = null; errdefer if (owned_link_dir) |*dir| dir.close(); - const bin_file_emit: ?link.Emit = blk: { + const bin_file_emit: ?Emit = blk: { const emit_bin = options.emit_bin orelse break :blk null; if (emit_bin.directory) |directory| { - break :blk link.Emit{ + break :blk Emit{ .directory = directory, .sub_path = emit_bin.basename, }; @@ -1725,9 +1448,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .incremental => {}, } - if (module) |zm| { - break :blk link.Emit{ - .directory = zm.zig_cache_artifact_directory, + if (zcu) |u| { + break :blk Emit{ + .directory = u.zig_cache_artifact_directory, .sub_path = emit_bin.basename, }; } @@ -1752,17 +1475,17 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .handle = artifact_dir, .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), }; - break :blk link.Emit{ + break :blk Emit{ .directory = link_artifact_directory, .sub_path = emit_bin.basename, }; }; - const implib_emit: ?link.Emit = blk: { + const implib_emit: ?Emit = blk: { const emit_implib = options.emit_implib orelse break :blk null; if (emit_implib.directory) |directory| { - break :blk link.Emit{ + break :blk Emit{ .directory = directory, .sub_path = emit_implib.basename, }; @@ -1776,13 +1499,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // Use the same directory as the bin. The CLI already emits an // error if -fno-emit-bin is combined with -femit-implib. - break :blk link.Emit{ + break :blk Emit{ .directory = bin_file_emit.?.directory, .sub_path = emit_implib.basename, }; }; - const docs_emit: ?link.Emit = blk: { + const docs_emit: ?Emit = blk: { const emit_docs = options.emit_docs orelse break :blk null; if (emit_docs.directory) |directory| { @@ -1805,7 +1528,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; break :blk .{ - .directory = module.?.zig_cache_artifact_directory, + .directory = zcu.?.zig_cache_artifact_directory, .sub_path = emit_docs.basename, }; }; @@ -1828,131 +1551,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]); } - const bin_file = try link.File.openPath(gpa, .{ - .emit = bin_file_emit, - .implib_emit = implib_emit, - .docs_emit = docs_emit, - .root_name = root_name, - .module = module, - .target = options.target, - .sysroot = sysroot, - .output_mode = options.output_mode, - .link_mode = link_mode, - .optimize_mode = options.optimize_mode, - .use_lld = use_lld, - .use_llvm = use_llvm, - .use_lib_llvm = use_lib_llvm, - .link_libc = link_libc, - .link_libcpp = link_libcpp, - .link_libunwind = link_libunwind, - .darwin_sdk_layout = libc_dirs.darwin_sdk_layout, - .objects = options.link_objects, - .frameworks = options.frameworks, - .framework_dirs = options.framework_dirs, - .system_libs = system_libs, - .wasi_emulated_libs = options.wasi_emulated_libs, - .lib_dirs = options.lib_dirs, - .rpath_list = options.rpath_list, - .symbol_wrap_set = options.symbol_wrap_set, - .strip = strip, - .is_native_os = options.is_native_os, - .is_native_abi = options.is_native_abi, - .function_sections = options.function_sections, - .data_sections = options.data_sections, - .no_builtin = options.no_builtin, - .allow_shlib_undefined = options.linker_allow_shlib_undefined, - .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, - .compress_debug_sections = options.linker_compress_debug_sections orelse .none, - .module_definition_file = options.linker_module_definition_file, - .sort_section = options.linker_sort_section, - .import_memory = options.linker_import_memory orelse false, - .export_memory = options.linker_export_memory orelse !(options.linker_import_memory orelse false), - .import_symbols = options.linker_import_symbols, - .import_table = options.linker_import_table, - .export_table = options.linker_export_table, - .initial_memory = options.linker_initial_memory, - .max_memory = options.linker_max_memory, - .shared_memory = options.linker_shared_memory, - .global_base = options.linker_global_base, - .export_symbol_names = options.linker_export_symbol_names, - .print_gc_sections = options.linker_print_gc_sections, - .print_icf_sections = options.linker_print_icf_sections, - .print_map = options.linker_print_map, - .opt_bisect_limit = options.linker_opt_bisect_limit, - .z_nodelete = options.linker_z_nodelete, - .z_notext = options.linker_z_notext, - .z_defs = options.linker_z_defs, - .z_origin = options.linker_z_origin, - .z_nocopyreloc = options.linker_z_nocopyreloc, - .z_now = options.linker_z_now, - .z_relro = options.linker_z_relro, - .z_common_page_size = options.linker_z_common_page_size, - .z_max_page_size = options.linker_z_max_page_size, - .tsaware = options.linker_tsaware, - .nxcompat = options.linker_nxcompat, - .dynamicbase = options.linker_dynamicbase, - .linker_optimization = linker_optimization, - .major_subsystem_version = options.major_subsystem_version, - .minor_subsystem_version = options.minor_subsystem_version, - .entry = options.entry, - .stack_size_override = options.stack_size_override, - .image_base_override = options.image_base_override, - .include_compiler_rt = include_compiler_rt, - .linker_script = options.linker_script, - .version_script = options.version_script, - .gc_sections = options.linker_gc_sections, - .eh_frame_hdr = link_eh_frame_hdr, - .emit_relocs = options.link_emit_relocs, - .rdynamic = options.rdynamic, - .soname = options.soname, - .version = options.version, - .compatibility_version = options.compatibility_version, - .libc_installation = libc_dirs.libc_installation, - .pic = pic, - .pie = pie, - .lto = lto, - .valgrind = valgrind, - .tsan = tsan, - .stack_check = stack_check, - .stack_protector = stack_protector, - .red_zone = red_zone, - .omit_frame_pointer = omit_frame_pointer, - .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, - .skip_linker_dependencies = options.skip_linker_dependencies, - .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, - .build_id = build_id, - .cache_mode = cache_mode, - .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, - .subsystem = options.subsystem, - .is_test = options.is_test, - .dwarf_format = options.dwarf_format, - .wasi_exec_model = wasi_exec_model, - .hash_style = options.hash_style, - .enable_link_snapshots = options.enable_link_snapshots, - .install_name = options.install_name, - .entitlements = options.entitlements, - .pagezero_size = options.pagezero_size, - .headerpad_size = options.headerpad_size, - .headerpad_max_install_names = options.headerpad_max_install_names, - .dead_strip_dylibs = options.dead_strip_dylibs, - .force_undefined_symbols = options.force_undefined_symbols, - .pdb_source_path = options.pdb_source_path, - .pdb_out_path = options.pdb_out_path, - .want_structured_cfg = options.want_structured_cfg, - }); - errdefer bin_file.destroy(); + const each_lib_rpath = options.each_lib_rpath orelse + options.root_mod.resolved_target.is_native_os; + comp.* = .{ .gpa = gpa, .arena = arena_allocator, + .module = zcu, + .root_mod = options.root_mod, + .config = options.config, + .bin_file = null, + .cache_mode = cache_mode, .zig_lib_directory = options.zig_lib_directory, .local_cache_directory = options.local_cache_directory, .global_cache_directory = options.global_cache_directory, - .bin_file = bin_file, .whole_bin_sub_path = whole_bin_sub_path, .whole_implib_sub_path = whole_implib_sub_path, .whole_docs_sub_path = whole_docs_sub_path, @@ -1966,8 +1578,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), .keep_source_files_loaded = options.keep_source_files_loaded, - .c_frontend = c_frontend, - .clang_argv = options.clang_argv, .c_source_files = options.c_source_files, .rc_source_files = options.rc_source_files, .cache_parent = cache, @@ -1975,7 +1585,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .libc_include_dir_list = libc_dirs.libc_include_dir_list, .libc_framework_dir_list = libc_dirs.libc_framework_dir_list, .rc_include_dir_list = rc_dirs.libc_include_dir_list, - .sanitize_c = sanitize_c, .thread_pool = options.thread_pool, .clang_passthrough_mode = options.clang_passthrough_mode, .clang_preprocessor_mode = options.clang_preprocessor_mode, @@ -1994,22 +1603,110 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .formatted_panics = formatted_panics, .time_report = options.time_report, .stack_report = options.stack_report, - .unwind_tables = unwind_tables, .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, .debug_compile_errors = options.debug_compile_errors, .libcxx_abi_version = options.libcxx_abi_version, + .implib_emit = implib_emit, + .docs_emit = docs_emit, + .root_name = root_name, + .sysroot = sysroot, + .system_libs = system_libs, + .version = options.version, + .libc_installation = libc_dirs.libc_installation, + .include_compiler_rt = include_compiler_rt, + .objects = options.link_objects, }; + + if (bin_file_emit) |emit| { + comp.bin_file = try link.File.open(arena, .{ + .comp = comp, + .emit = emit, + .optimization = linker_optimization, + .linker_script = options.linker_script, + .z_nodelete = options.linker_z_nodelete, + .z_notext = options.linker_z_notext, + .z_defs = options.linker_z_defs, + .z_origin = options.linker_z_origin, + .z_nocopyreloc = options.linker_z_nocopyreloc, + .z_now = options.linker_z_now, + .z_relro = options.linker_z_relro, + .z_common_page_size = options.linker_z_common_page_size, + .z_max_page_size = options.linker_z_max_page_size, + .darwin_sdk_layout = libc_dirs.darwin_sdk_layout, + .frameworks = options.frameworks, + .framework_dirs = options.framework_dirs, + .wasi_emulated_libs = options.wasi_emulated_libs, + .lib_dirs = options.lib_dirs, + .rpath_list = options.rpath_list, + .symbol_wrap_set = options.symbol_wrap_set, + .function_sections = options.function_sections, + .data_sections = options.data_sections, + .no_builtin = options.no_builtin, + .allow_shlib_undefined = options.linker_allow_shlib_undefined, + .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, + .compress_debug_sections = options.linker_compress_debug_sections orelse .none, + .module_definition_file = options.linker_module_definition_file, + .sort_section = options.linker_sort_section, + .import_symbols = options.linker_import_symbols, + .import_table = options.linker_import_table, + .export_table = options.linker_export_table, + .initial_memory = options.linker_initial_memory, + .max_memory = options.linker_max_memory, + .global_base = options.linker_global_base, + .export_symbol_names = options.linker_export_symbol_names, + .print_gc_sections = options.linker_print_gc_sections, + .print_icf_sections = options.linker_print_icf_sections, + .print_map = options.linker_print_map, + .opt_bisect_limit = options.linker_opt_bisect_limit, + .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, + .version_script = options.version_script, + .gc_sections = options.linker_gc_sections, + .eh_frame_hdr = link_eh_frame_hdr, + .emit_relocs = options.link_emit_relocs, + .rdynamic = options.rdynamic, + .soname = options.soname, + .compatibility_version = options.compatibility_version, + .verbose_link = options.verbose_link, + .dll_export_fns = dll_export_fns, + .skip_linker_dependencies = options.skip_linker_dependencies, + .parent_compilation_link_libc = options.parent_compilation_link_libc, + .each_lib_rpath = each_lib_rpath, + .build_id = build_id, + .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, + .subsystem = options.subsystem, + .dwarf_format = options.dwarf_format, + .hash_style = options.hash_style, + .enable_link_snapshots = options.enable_link_snapshots, + .install_name = options.install_name, + .entitlements = options.entitlements, + .pagezero_size = options.pagezero_size, + .headerpad_size = options.headerpad_size, + .headerpad_max_install_names = options.headerpad_max_install_names, + .dead_strip_dylibs = options.dead_strip_dylibs, + .force_undefined_symbols = options.force_undefined_symbols, + .pdb_source_path = options.pdb_source_path, + .pdb_out_path = options.pdb_out_path, + .want_structured_cfg = options.want_structured_cfg, + .entry_addr = null, // CLI does not expose this option (yet?) + }); + } + break :comp comp; }; errdefer comp.destroy(); - const target = comp.getTarget(); + const target = options.root_mod.resolved_target.result; - const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.bin_file.options.use_llvm); - const capable_of_building_zig_libc = canBuildZigLibC(target, comp.bin_file.options.use_llvm); + const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, options.config.use_llvm); + const capable_of_building_zig_libc = canBuildZigLibC(target, options.config.use_llvm); // Add a `CObject` for each `c_source_files`. try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); @@ -2109,7 +1806,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }); } comp.work_queue.writeAssumeCapacity(&[_]Job{ - .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(wasi_exec_model) }, + .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) }, .{ .wasi_libc_crt_file = .libc_a }, }); } @@ -2171,7 +1868,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { 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 (options.output_mode != .Obj) { + } 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. @@ -2283,7 +1980,7 @@ pub fn clearMiscFailures(comp: *Compilation) void { } pub fn getTarget(self: Compilation) Target { - return self.bin_file.options.target; + return self.root_mod.resolved_target.result; } fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void { @@ -2436,7 +2133,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void // Make sure std.zig is inside the import_table. We unconditionally need // it for start.zig. - const std_mod = module.main_mod.deps.get("std").?; + const std_mod = module.std_mod; _ = try module.importPkg(std_mod); // Normally we rely on importing std to in turn import the root source file @@ -2449,7 +2146,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void _ = try module.importPkg(module.main_mod); } - if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| { + if (module.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { _ = try module.importPkg(compiler_rt_mod); } @@ -2474,7 +2171,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void try comp.work_queue.writeItem(.{ .analyze_mod = module.main_mod }); } - if (module.main_mod.deps.get("compiler_rt")) |compiler_rt_mod| { + if (module.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { try comp.work_queue.writeItem(.{ .analyze_mod = compiler_rt_mod }); } } @@ -2699,16 +2396,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_mod.root.joinString(arena, mod.main_mod.root_src_path); _ = try man.addFile(main_zig_file, null); - { - var seen_table = std.AutoHashMap(*Package.Module, void).init(arena); - - // Skip builtin.zig; it is useless as an input, and we don't want to have to - // write it before checking for a cache hit. - const builtin_mod = mod.main_mod.deps.get("builtin").?; - try seen_table.put(builtin_mod, {}); - - try addModuleTableToCacheHash(&man.hash, &arena_allocator, mod.main_mod.deps, &seen_table, .{ .files = man }); - } + try addModuleTableToCacheHash(gpa, arena, &man.hash, mod.main_mod, .{ .files = man }); // Synchronize with other matching comments: ZigOnlyHashStuff man.hash.add(comp.bin_file.options.valgrind); @@ -2762,8 +2450,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); - man.hash.addListOfBytes(comp.clang_argv); - man.hash.addOptional(comp.bin_file.options.stack_size_override); man.hash.addOptional(comp.bin_file.options.image_base_override); man.hash.addOptional(comp.bin_file.options.gc_sections); @@ -3341,7 +3027,7 @@ pub const ErrorNoteHashContext = struct { const eb = ctx.eb.tmpBundle(); const msg_a = eb.nullTerminatedString(a.msg); const msg_b = eb.nullTerminatedString(b.msg); - if (!std.mem.eql(u8, msg_a, msg_b)) return false; + if (!mem.eql(u8, msg_a, msg_b)) return false; if (a.src_loc == .none and b.src_loc == .none) return true; if (a.src_loc == .none or b.src_loc == .none) return false; @@ -3351,7 +3037,7 @@ pub const ErrorNoteHashContext = struct { const src_path_a = eb.nullTerminatedString(src_a.src_path); const src_path_b = eb.nullTerminatedString(src_b.src_path); - return std.mem.eql(u8, src_path_a, src_path_b) and + return mem.eql(u8, src_path_a, src_path_b) and src_a.line == src_b.line and src_a.column == src_b.column and src_a.span_main == src_b.span_main; @@ -4149,7 +3835,7 @@ pub const CImportResult = struct { cache_hit: bool, errors: std.zig.ErrorBundle, - pub fn deinit(result: *CImportResult, gpa: std.mem.Allocator) void { + pub fn deinit(result: *CImportResult, gpa: mem.Allocator) void { result.errors.deinit(gpa); } }; @@ -5321,9 +5007,9 @@ pub fn addCCArgs( argv.appendAssumeCapacity(arg); } } - const mcmodel = comp.bin_file.options.machine_code_model; - if (mcmodel != .default) { - try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)})); + const code_model = comp.bin_file.options.machine_code_model; + if (code_model != .default) { + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(code_model)})); } switch (target.os.tag) { @@ -5618,68 +5304,6 @@ fn failCObjWithOwnedDiagBundle( return error.AnalysisFail; } -/// The include directories used when preprocessing .rc files are separate from the -/// target. Which include directories are used is determined by `options.rc_includes`. -/// -/// Note: It should be okay that the include directories used when compiling .rc -/// files differ from the include directories used when compiling the main -/// binary, since the .res format is not dependent on anything ABI-related. The -/// only relevant differences would be things like `#define` constants being -/// different in the MinGW headers vs the MSVC headers, but any such -/// differences would likely be a MinGW bug. -fn detectWin32ResourceIncludeDirs(arena: Allocator, options: InitOptions) !LibCDirs { - // Set the includes to .none here when there are no rc files to compile - var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none; - if (builtin.target.os.tag != .windows) { - switch (includes) { - // MSVC can't be found when the host isn't Windows, so short-circuit. - .msvc => return error.WindowsSdkNotFound, - // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts. - .any => includes = .gnu, - .none, .gnu => {}, - } - } - while (true) { - switch (includes) { - .any, .msvc => return detectLibCIncludeDirs( - arena, - options.zig_lib_directory.path.?, - .{ - .cpu = options.target.cpu, - .os = options.target.os, - .abi = .msvc, - .ofmt = options.target.ofmt, - }, - options.is_native_abi, - // The .rc preprocessor will need to know the libc include dirs even if we - // are not linking libc, so force 'link_libc' to true - true, - options.libc_installation, - ) catch |err| { - if (includes == .any) { - // fall back to mingw - includes = .gnu; - continue; - } - return err; - }, - .gnu => return detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{ - .cpu = options.target.cpu, - .os = options.target.os, - .abi = .gnu, - .ofmt = options.target.ofmt, - }), - .none => return LibCDirs{ - .libc_include_dir_list = &[0][]u8{}, - .libc_installation = null, - .libc_framework_dir_list = &.{}, - .sysroot = null, - .darwin_sdk_layout = null, - }, - } - } -} - fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError { @setCold(true); var bundle: ErrorBundle.Wip = undefined; @@ -6061,7 +5685,7 @@ const LibCDirs = struct { libc_installation: ?*const LibCInstallation, libc_framework_dir_list: []const []const u8, sysroot: ?[]const u8, - darwin_sdk_layout: ?link.DarwinSdkLayout, + darwin_sdk_layout: ?link.File.MachO.SdkLayout, }; fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs { @@ -6374,7 +5998,7 @@ fn parseLldStderr(comp: *Compilation, comptime prefix: []const u8, stderr: []con err.context_lines = try context_lines.toOwnedSlice(); } - var split = std.mem.splitSequence(u8, line, "error: "); + var split = mem.splitSequence(u8, line, "error: "); _ = split.first(); const duped_msg = try std.fmt.allocPrint(comp.gpa, "{s}: {s}", .{ prefix, split.rest() }); @@ -6427,7 +6051,7 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { .spirv32, .spirv64 => return false, else => {}, } - return switch (zigBackend(target, use_llvm)) { + return switch (target_util.zigBackend(target, use_llvm)) { .stage2_llvm => true, .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, else => build_options.have_llvm, @@ -6445,7 +6069,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { .spirv32, .spirv64 => return false, else => {}, } - return switch (zigBackend(target, use_llvm)) { + return switch (target_util.zigBackend(target, use_llvm)) { .stage2_llvm => true, .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, else => build_options.have_llvm, @@ -6454,236 +6078,7 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { const target = comp.bin_file.options.target; - return zigBackend(target, comp.bin_file.options.use_llvm); -} - -fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend { - if (use_llvm) return .stage2_llvm; - if (target.ofmt == .c) return .stage2_c; - return switch (target.cpu.arch) { - .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, - .arm, .armeb, .thumb, .thumbeb => .stage2_arm, - .x86_64 => .stage2_x86_64, - .x86 => .stage2_x86, - .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, - .riscv64 => .stage2_riscv64, - .sparc64 => .stage2_sparc64, - .spirv64 => .stage2_spirv64, - else => .other, - }; -} - -pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 { - const tracy_trace = trace(@src()); - defer tracy_trace.end(); - - var buffer = std.ArrayList(u8).init(allocator); - defer buffer.deinit(); - - const target = comp.getTarget(); - const generic_arch_name = target.cpu.arch.genericName(); - const zig_backend = comp.getZigBackend(); - - @setEvalBranchQuota(4000); - try buffer.writer().print( - \\const std = @import("std"); - \\/// 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 = std.SemanticVersion.parse(zig_version_string) catch unreachable; - \\pub const zig_version_string = "{s}"; - \\pub const zig_backend = std.builtin.CompilerBackend.{}; - \\ - \\pub const output_mode = std.builtin.OutputMode.{}; - \\pub const link_mode = std.builtin.LinkMode.{}; - \\pub const is_test = {}; - \\pub const single_threaded = {}; - \\pub const abi = std.Target.Abi.{}; - \\pub const cpu: std.Target.Cpu = .{{ - \\ .arch = .{}, - \\ .model = &std.Target.{}.cpu.{}, - \\ .features = std.Target.{}.featureSet(&[_]std.Target.{}.Feature{{ - \\ - , .{ - build_options.version, - std.zig.fmtId(@tagName(zig_backend)), - 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, - 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(), 0..) |feature, index_usize| { - const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); - const is_enabled = target.cpu.features.isEnabled(index); - if (is_enabled) { - try buffer.writer().print(" .{},\n", .{std.zig.fmtId(feature.name)}); - } - } - try buffer.writer().print( - \\ }}), - \\}}; - \\pub const os = std.Target.Os{{ - \\ .tag = .{}, - \\ .version_range = .{{ - , - .{std.zig.fmtId(@tagName(target.os.tag))}, - ); - - switch (target.os.getVersionRange()) { - .none => try buffer.appendSlice(" .none = {} },\n"), - .semver => |semver| try buffer.writer().print( - \\ .semver = .{{ - \\ .min = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ .max = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}}}, - \\ - , .{ - semver.min.major, - semver.min.minor, - semver.min.patch, - - semver.max.major, - semver.max.minor, - semver.max.patch, - }), - .linux => |linux| try buffer.writer().print( - \\ .linux = .{{ - \\ .range = .{{ - \\ .min = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ .max = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}, - \\ .glibc = .{{ - \\ .major = {}, - \\ .minor = {}, - \\ .patch = {}, - \\ }}, - \\ }}}}, - \\ - , .{ - linux.range.min.major, - linux.range.min.minor, - linux.range.min.patch, - - linux.range.max.major, - linux.range.max.minor, - linux.range.max.patch, - - linux.glibc.major, - linux.glibc.minor, - linux.glibc.patch, - }), - .windows => |windows| try buffer.writer().print( - \\ .windows = .{{ - \\ .min = {s}, - \\ .max = {s}, - \\ }}}}, - \\ - , - .{ windows.min, windows.max }, - ), - } - try buffer.appendSlice( - \\}; - \\pub const target: std.Target = .{ - \\ .cpu = cpu, - \\ .os = os, - \\ .abi = abi, - \\ .ofmt = object_format, - \\ - ); - - if (target.dynamic_linker.get()) |dl| { - try buffer.writer().print( - \\ .dynamic_linker = std.Target.DynamicLinker.init("{s}"), - \\}}; - \\ - , .{dl}); - } else { - try buffer.appendSlice( - \\ .dynamic_linker = std.Target.DynamicLinker.none, - \\}; - \\ - ); - } - - try buffer.writer().print( - \\pub const object_format = std.Target.ObjectFormat.{}; - \\pub const mode = std.builtin.OptimizeMode.{}; - \\pub const link_libc = {}; - \\pub const link_libcpp = {}; - \\pub const have_error_return_tracing = {}; - \\pub const valgrind_support = {}; - \\pub const sanitize_thread = {}; - \\pub const position_independent_code = {}; - \\pub const position_independent_executable = {}; - \\pub const strip_debug_info = {}; - \\pub const code_model = std.builtin.CodeModel.{}; - \\pub const omit_frame_pointer = {}; - \\ - , .{ - std.zig.fmtId(@tagName(target.ofmt)), - std.zig.fmtId(@tagName(comp.bin_file.options.optimize_mode)), - comp.bin_file.options.link_libc, - comp.bin_file.options.link_libcpp, - comp.bin_file.options.error_return_tracing, - comp.bin_file.options.valgrind, - comp.bin_file.options.tsan, - comp.bin_file.options.pic, - comp.bin_file.options.pie, - comp.bin_file.options.strip, - std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)), - comp.bin_file.options.omit_frame_pointer, - }); - - if (target.os.tag == .wasi) { - const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model)); - try buffer.writer().print( - \\pub const wasi_exec_model = std.builtin.WasiExecModel.{}; - \\ - , .{wasi_exec_model_fmt}); - } - - if (comp.bin_file.options.is_test) { - try buffer.appendSlice( - \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later - \\ - ); - if (comp.test_evented_io) { - try buffer.appendSlice( - \\pub const test_io_mode = .evented; - \\ - ); - } else { - try buffer.appendSlice( - \\pub const test_io_mode = .blocking; - \\ - ); - } - } - - return buffer.toOwnedSliceSentinel(0); + return target_util.zigBackend(target, comp.bin_file.options.use_llvm); } pub fn updateSubCompilation( @@ -6730,21 +6125,46 @@ fn buildOutputFromZig( const tracy_trace = trace(@src()); defer tracy_trace.end(); + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + assert(output_mode != .Exe); - var main_mod: Package.Module = .{ - .root = .{ .root_dir = comp.zig_lib_directory }, - .root_src_path = src_basename, + const config = try Config.resolve(.{ + .output_mode = output_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = true, + .emit_bin = true, + .root_optimize_mode = comp.compilerRtOptMode(), + }); + + const root_mod = Package.Module.create(.{ + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = src_basename, + }, .fully_qualified_name = "root", - }; + .inherited = .{ + .strip = comp.compilerRtStrip(), + .stack_check = false, + .stack_protector = 0, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .unwind_tables = comp.bin_file.options.eh_frame_hdr, + .pic = comp.root_mod.pic, + }, + .global = config, + .cc_argv = &.{}, + }); const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; const target = comp.getTarget(); - const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ + const bin_basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, .output_mode = output_mode, }); - defer comp.gpa.free(bin_basename); const emit_bin = Compilation.EmitLoc{ .directory = null, // Put it in the cache directory. @@ -6754,33 +6174,18 @@ fn buildOutputFromZig( .global_cache_directory = comp.global_cache_directory, .local_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .resolved = config, .cache_mode = .whole, - .target = target, .root_name = root_name, - .main_mod = &main_mod, - .output_mode = output_mode, + .root_mod = root_mod, .thread_pool = comp.thread_pool, .libc_installation = comp.bin_file.options.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), .link_mode = .Static, .function_sections = true, .data_sections = true, .no_builtin = true, - .want_sanitize_c = false, - .want_stack_check = false, - .want_stack_protector = 0, - .want_red_zone = comp.bin_file.options.red_zone, - .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, - .want_valgrind = false, - .want_tsan = false, - .want_unwind_tables = comp.bin_file.options.eh_frame_hdr, - .want_pic = comp.bin_file.options.pic, - .want_pie = null, .emit_h = null, - .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, .verbose_cc = comp.verbose_cc, .verbose_link = comp.bin_file.options.verbose_link, @@ -6815,7 +6220,7 @@ pub fn build_crt_file( output_mode: std.builtin.OutputMode, misc_task_tag: MiscTask, prog_node: *std.Progress.Node, - c_source_files: []const Compilation.CSourceFile, + c_source_files: []const CSourceFile, ) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig new file mode 100644 index 0000000000..0b9c70fc42 --- /dev/null +++ b/src/Compilation/Config.zig @@ -0,0 +1,382 @@ +//! User-specified settings that have all the defaults resolved into concrete values. + +have_zcu: bool, +output_mode: std.builtin.OutputMode, +link_mode: std.builtin.LinkMode, +link_libc: bool, +link_libcpp: bool, +link_libunwind: bool, +any_unwind_tables: bool, +pie: bool, +/// If this is true then linker code is responsible for making an LLVM IR +/// Module, outputting it to an object file, and then linking that together +/// with link options and other objects. Otherwise (depending on `use_lld`) +/// linker code directly outputs and updates the final binary. +use_llvm: bool, +/// Whether or not the LLVM library API will be used by the LLVM backend. +use_lib_llvm: bool, +/// If this is true then linker code is responsible for outputting an object +/// file and then using LLD to link it together with the link options and other +/// objects. Otherwise (depending on `use_llvm`) linker code directly outputs +/// and updates the final binary. +use_lld: bool, +c_frontend: CFrontend, +lto: bool, +/// WASI-only. Type of WASI execution model ("command" or "reactor"). +/// Always set to `command` for non-WASI targets. +wasi_exec_model: std.builtin.WasiExecModel, +import_memory: bool, +export_memory: bool, +shared_memory: bool, +is_test: bool, +test_evented_io: bool, +entry: ?[]const u8, + +pub const CFrontend = enum { clang, aro }; + +pub const Options = struct { + output_mode: std.builtin.OutputMode, + resolved_target: Module.ResolvedTarget, + is_test: bool, + have_zcu: bool, + emit_bin: bool, + root_optimize_mode: ?std.builtin.OptimizeMode = null, + link_mode: ?std.builtin.LinkMode = null, + ensure_libc_on_non_freestanding: bool = false, + ensure_libcpp_on_non_freestanding: bool = false, + any_non_single_threaded: bool = false, + any_sanitize_thread: bool = false, + any_unwind_tables: bool = false, + any_dyn_libs: bool = false, + c_source_files_len: usize = 0, + emit_llvm_ir: bool = false, + emit_llvm_bc: bool = false, + link_libc: ?bool = null, + link_libcpp: ?bool = null, + link_libunwind: ?bool = null, + pie: ?bool = null, + use_llvm: ?bool = null, + use_lib_llvm: ?bool = null, + use_lld: ?bool = null, + use_clang: ?bool = null, + lto: ?bool = null, + entry: union(enum) { + default, + disabled, + enabled, + named: []const u8, + } = .default, + /// WASI-only. Type of WASI execution model ("command" or "reactor"). + wasi_exec_model: ?std.builtin.WasiExecModel = null, + import_memory: ?bool = null, + export_memory: ?bool = null, + shared_memory: ?bool = null, + test_evented_io: bool = false, +}; + +pub fn resolve(options: Options) !Config { + const target = options.resolved_target.result; + + // WASI-only. Resolve the optional exec-model option, defaults to command. + if (target.os.tag != .wasi and options.wasi_exec_model != null) + return error.WasiExecModelRequiresWasi; + const wasi_exec_model = options.wasi_exec_model orelse .command; + + const shared_memory = b: { + if (!target.cpu.arch.isWasm()) { + if (options.shared_memory == true) return error.SharedMemoryIsWasmOnly; + break :b false; + } + if (options.output_mode == .Obj) { + if (options.shared_memory == true) return error.ObjectFilesCannotShareMemory; + break :b false; + } + if (!std.Target.wasm.featureSetHasAll(target.cpu.features, .{ .atomics, .bulk_memory })) { + if (options.shared_memory == true) + return error.SharedMemoryRequiresAtomicsAndBulkMemory; + break :b false; + } + if (options.any_non_single_threaded) { + if (options.shared_memory == false) + return error.ThreadsRequireSharedMemory; + break :b true; + } + break :b options.shared_memory orelse false; + }; + + const entry: ?[]const u8 = switch (options.entry) { + .disabled => null, + .default => b: { + if (options.output_mode != .Exe) break :b null; + break :b target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse + return error.UnknownTargetEntryPoint; + }, + .enabled => target_util.defaultEntrySymbolName(target, wasi_exec_model) orelse + return error.UnknownTargetEntryPoint, + .named => |name| name, + }; + if (entry != null and options.output_mode != .Exe) + return error.NonExecutableEntryPoint; + + // *If* the LLVM backend were to be selected, should Zig use the LLVM + // library to build the LLVM module? + const use_lib_llvm = b: { + if (!build_options.have_llvm) { + if (options.use_lib_llvm == true) return error.LlvmLibraryUnavailable; + break :b false; + } + break :b options.use_lib_llvm orelse true; + }; + + const root_optimize_mode = options.root_optimize_mode orelse .Debug; + + // Make a decision on whether to use LLVM backend for machine code generation. + // Note that using the LLVM backend does not necessarily mean using LLVM libraries. + // For example, Zig can emit .bc and .ll files directly, and this is still considered + // using "the LLVM backend". + const use_llvm = b: { + // If emitting to LLVM bitcode object format, must use LLVM backend. + if (options.emit_llvm_ir or options.emit_llvm_bc) { + if (options.use_llvm == false) return error.EmittingLlvmModuleRequiresLlvmBackend; + break :b true; + } + + // If LLVM does not support the target, then we can't use it. + if (!target_util.hasLlvmSupport(target, target.ofmt)) { + if (options.use_llvm == true) return error.LlvmLacksTargetSupport; + break :b false; + } + + if (options.use_llvm) |x| break :b x; + + // If we have no zig code to compile, no need for LLVM. + if (!options.have_zcu) break :b false; + + // If we cannot use LLVM libraries, then our own backends will be a + // better default since the LLVM backend can only produce bitcode + // and not an object file or executable. + if (!use_lib_llvm) break :b false; + + // Prefer LLVM for release builds. + if (root_optimize_mode != .Debug) break :b true; + + // At this point we would prefer to use our own self-hosted backend, + // because the compilation speed is better than LLVM. But only do it if + // we are confident in the robustness of the backend. + break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target); + }; + + if (!use_lib_llvm and use_llvm and options.emit_bin) { + // Explicit request to use LLVM to produce an object file, but without + // using LLVM libraries. Impossible. + return error.EmittingBinaryRequiresLlvmLibrary; + } + + // Make a decision on whether to use LLD or our own linker. + const use_lld = b: { + if (target.isDarwin()) { + if (options.use_lld == true) return error.LldIncompatibleOs; + break :b false; + } + + if (!build_options.have_llvm) { + if (options.use_lld == true) return error.LldUnavailable; + break :b false; + } + + if (target.ofmt == .c) { + if (options.use_lld == true) return error.LldIncompatibleObjectFormat; + break :b false; + } + + if (options.lto == true) { + if (options.use_lld == false) return error.LtoRequiresLld; + break :b true; + } + + if (options.use_lld) |x| break :b x; + break :b true; + }; + + // Make a decision on whether to use Clang or Aro for translate-c and compiling C files. + const c_frontend: CFrontend = b: { + if (!build_options.have_llvm) { + if (options.use_clang == true) return error.ClangUnavailable; + break :b .aro; + } + if (options.use_clang) |clang| { + break :b if (clang) .clang else .aro; + } + break :b .clang; + }; + + const lto = b: { + if (!use_lld) { + // zig ld LTO support is tracked by + // https://github.com/ziglang/zig/issues/8680 + if (options.lto == true) return error.LtoRequiresLld; + break :b false; + } + + if (options.lto) |x| break :b x; + if (options.c_source_files_len == 0) break :b false; + + if (target.cpu.arch.isRISCV()) { + // Clang and LLVM currently don't support RISC-V target-abi for LTO. + // Compiling with LTO may fail or produce undesired results. + // See https://reviews.llvm.org/D71387 + // See https://reviews.llvm.org/D102582 + break :b false; + } + + break :b switch (options.output_mode) { + .Lib, .Obj => false, + .Exe => switch (root_optimize_mode) { + .Debug => false, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => true, + }, + }; + }; + + const link_libcpp = b: { + if (options.link_libcpp == true) break :b true; + if (options.any_sanitize_thread) { + // TSAN is (for now...) implemented in C++ so it requires linking libc++. + if (options.link_libcpp == false) return error.SanitizeThreadRequiresLibCpp; + break :b true; + } + if (options.ensure_libcpp_on_non_freestanding and target.os.tag != .freestanding) + break :b true; + + break :b false; + }; + + const link_libunwind = b: { + if (link_libcpp and target_util.libcNeedsLibUnwind(target)) { + if (options.link_libunwind == false) return error.LibCppRequiresLibUnwind; + break :b true; + } + break :b options.link_libunwind orelse false; + }; + + const link_libc = b: { + if (target_util.osRequiresLibC(target)) { + if (options.link_libc == false) return error.OsRequiresLibC; + break :b true; + } + if (link_libcpp) { + if (options.link_libc == false) return error.LibCppRequiresLibC; + break :b true; + } + if (link_libunwind) { + if (options.link_libc == false) return error.LibUnwindRequiresLibC; + break :b true; + } + if (options.link_libc) |x| break :b x; + if (options.ensure_libc_on_non_freestanding and target.os.tag != .freestanding) + break :b true; + + break :b false; + }; + + const any_unwind_tables = options.any_unwind_tables or + link_libunwind or target_util.needUnwindTables(target); + + const link_mode = b: { + const explicitly_exe_or_dyn_lib = switch (options.output_mode) { + .Obj => false, + .Lib => (options.link_mode orelse .Static) == .Dynamic, + .Exe => true, + }; + + if (target_util.cannotDynamicLink(target)) { + if (options.link_mode == .Dynamic) return error.TargetCannotDynamicLink; + break :b .Static; + } + if (explicitly_exe_or_dyn_lib and link_libc and + (target.isGnuLibC() or target_util.osRequiresLibC(target))) + { + if (options.link_mode == .Static) return error.LibCRequiresDynamicLinking; + break :b .Dynamic; + } + // When creating a executable that links to system libraries, we + // require dynamic linking, but we must not link static libraries + // or object files dynamically! + if (options.any_dyn_libs and options.output_mode == .Exe) { + if (options.link_mode == .Static) return error.SharedLibrariesRequireDynamicLinking; + break :b .Dynamic; + } + + if (options.link_mode) |link_mode| break :b link_mode; + + if (explicitly_exe_or_dyn_lib and link_libc and + options.resolved_target.is_native_abi and target.abi.isMusl()) + { + // If targeting the system's native ABI and the system's libc is + // musl, link dynamically by default. + break :b .Dynamic; + } + + // Static is generally a better default. Fight me. + break :b .Static; + }; + + const import_memory = options.import_memory orelse false; + const export_memory = b: { + if (link_mode == .Dynamic) { + if (options.export_memory == true) return error.ExportMemoryAndDynamicIncompatible; + break :b false; + } + if (options.export_memory) |x| break :b x; + break :b !import_memory; + }; + + const pie: bool = b: { + switch (options.output_mode) { + .Obj, .Exe => {}, + .Lib => if (link_mode == .Dynamic) { + if (options.pie == true) return error.DynamicLibraryPrecludesPie; + break :b false; + }, + } + if (target_util.requiresPIE(target)) { + if (options.pie == false) return error.TargetRequiresPie; + break :b true; + } + if (options.any_sanitize_thread) { + if (options.pie == false) return error.SanitizeThreadRequiresPie; + break :b true; + } + if (options.pie) |pie| break :b pie; + break :b false; + }; + + return .{ + .output_mode = options.output_mode, + .have_zcu = options.have_zcu, + .is_test = options.is_test, + .test_evented_io = options.test_evented_io, + .link_mode = link_mode, + .link_libc = link_libc, + .link_libcpp = link_libcpp, + .link_libunwind = link_libunwind, + .any_unwind_tables = any_unwind_tables, + .pie = pie, + .lto = lto, + .import_memory = import_memory, + .export_memory = export_memory, + .shared_memory = shared_memory, + .c_frontend = c_frontend, + .use_llvm = use_llvm, + .use_lib_llvm = use_lib_llvm, + .use_lld = use_lld, + .entry = entry, + .wasi_exec_model = wasi_exec_model, + }; +} + +const std = @import("std"); +const Module = @import("../Package.zig").Module; +const Config = @This(); +const target_util = @import("../target.zig"); +const build_options = @import("build_options"); diff --git a/src/Module.zig b/src/Module.zig index 6e5609f63c..5ec949293e 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -59,6 +59,7 @@ root_mod: *Package.Module, /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. main_mod: *Package.Module, +std_mod: *Package.Module, sema_prog_node: std.Progress.Node = undefined, /// Used by AstGen worker to load and store ZIR cache. @@ -3599,7 +3600,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { // TODO: figure out how this works under incremental changes to builtin.zig! const builtin_type_target_index: InternPool.Index = blk: { - const std_mod = mod.main_mod.deps.get("std").?; + const std_mod = mod.std_mod; if (decl.getFileScope(mod).mod != std_mod) break :blk .none; // We're in the std module. const std_file = (try mod.importPkg(std_mod)).file; @@ -3924,10 +3925,7 @@ pub fn importFile( import_string: []const u8, ) !ImportFileResult { if (std.mem.eql(u8, import_string, "std")) { - return mod.importPkg(mod.main_mod.deps.get("std").?); - } - if (std.mem.eql(u8, import_string, "builtin")) { - return mod.importPkg(mod.main_mod.deps.get("builtin").?); + return mod.importPkg(mod.std_mod); } if (std.mem.eql(u8, import_string, "root")) { return mod.importPkg(mod.root_mod); diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 0c8d40bffa..94431856ac 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -1,6 +1,6 @@ //! Corresponds to something that Zig source code can `@import`. -//! Not to be confused with src/Module.zig which should be renamed -//! to something else. https://github.com/ziglang/zig/issues/14307 +//! Not to be confused with src/Module.zig which will be renamed +//! to Zcu. https://github.com/ziglang/zig/issues/14307 /// Only files inside this directory can be imported. root: Package.Path, @@ -14,6 +14,26 @@ fully_qualified_name: []const u8, /// responsible for detecting these names and using the correct package. deps: Deps = .{}, +resolved_target: ResolvedTarget, +optimize_mode: std.builtin.OptimizeMode, +code_model: std.builtin.CodeModel, +single_threaded: bool, +error_tracing: bool, +valgrind: bool, +pic: bool, +strip: bool, +omit_frame_pointer: bool, +stack_check: bool, +stack_protector: u32, +red_zone: bool, +sanitize_c: bool, +sanitize_thread: bool, +unwind_tables: bool, +cc_argv: []const []const u8, + +/// The contents of `@import("builtin")` for this module. +generated_builtin_source: []const u8, + pub const Deps = std.StringArrayHashMapUnmanaged(*Module); pub const Tree = struct { @@ -21,10 +41,382 @@ pub const Tree = struct { build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module), }; -pub fn create(allocator: Allocator, m: Module) Allocator.Error!*Module { - const new = try allocator.create(Module); - new.* = m; - return new; +pub const CreateOptions = struct { + /// Where to store builtin.zig. The global cache directory is used because + /// it is a pure function based on CLI flags. + global_cache_directory: Cache.Directory, + paths: Paths, + fully_qualified_name: []const u8, + + cc_argv: []const []const u8, + inherited: Inherited, + global: Compilation.Config, + /// If this is null then `resolved_target` must be non-null. + parent: ?*Package.Module, + + builtin_mod: ?*Package.Module, + + pub const Paths = struct { + root: Package.Path, + /// Relative to `root`. May contain path separators. + root_src_path: []const u8, + }; + + pub const Inherited = struct { + /// If this is null then `parent` must be non-null. + resolved_target: ?ResolvedTarget = null, + optimize_mode: ?std.builtin.OptimizeMode = null, + code_model: ?std.builtin.CodeModel = null, + single_threaded: ?bool = null, + error_tracing: ?bool = null, + valgrind: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + omit_frame_pointer: ?bool = null, + stack_check: ?bool = null, + /// null means default. + /// 0 means no stack protector. + /// other number means stack protection with that buffer size. + stack_protector: ?u32 = null, + red_zone: ?bool = null, + unwind_tables: ?bool = null, + sanitize_c: ?bool = null, + sanitize_thread: ?bool = null, + }; +}; + +pub const ResolvedTarget = struct { + result: std.Target, + is_native_os: bool, + is_native_abi: bool, + llvm_cpu_features: ?[*:0]const u8 = null, +}; + +/// At least one of `parent` and `resolved_target` must be non-null. +pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { + const resolved_target = options.inherited.resolved_target orelse options.parent.?.resolved_target; + const target = resolved_target.result; + + const optimize_mode = options.inherited.optimize_mode orelse + if (options.parent) |p| p.optimize_mode else .Debug; + + const unwind_tables = options.inherited.unwind_tables orelse + if (options.parent) |p| p.unwind_tables else options.global.any_unwind_tables; + + const strip = b: { + if (options.inherited.strip) |x| break :b x; + if (options.parent) |p| break :b p.strip; + if (optimize_mode == .ReleaseSmall) break :b true; + if (!target_util.hasDebugInfo(target)) break :b true; + break :b false; + }; + + const valgrind = b: { + if (!target_util.hasValgrindSupport(target)) { + if (options.inherited.valgrind == true) + return error.ValgrindUnsupportedOnTarget; + break :b false; + } + if (options.inherited.valgrind) |x| break :b x; + if (options.parent) |p| break :b p.valgrind; + if (strip) break :b false; + break :b optimize_mode == .Debug; + }; + + const zig_backend = target_util.zigBackend(target, options.global.use_llvm); + + const single_threaded = b: { + if (target_util.alwaysSingleThreaded(target)) { + if (options.inherited.single_threaded == false) + return error.TargetRequiresSingleThreaded; + break :b true; + } + + if (options.global.have_zcu) { + if (!target_util.supportsThreads(target, zig_backend)) { + if (options.inherited.single_threaded == false) + return error.BackendRequiresSingleThreaded; + break :b true; + } + } + + if (options.inherited.single_threaded) |x| break :b x; + if (options.parent) |p| break :b p.single_threaded; + break :b target_util.defaultSingleThreaded(target); + }; + + const error_tracing = b: { + if (options.inherited.error_tracing) |x| break :b x; + if (options.parent) |p| break :b p.error_tracing; + if (strip) break :b false; + break :b switch (optimize_mode) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }; + }; + + const pic = b: { + if (target_util.requiresPIC(target, options.global.link_libc)) { + if (options.inherited.pic == false) + return error.TargetRequiresPic; + break :b true; + } + if (options.global.pie) { + if (options.inherited.pic == false) + return error.PieRequiresPic; + break :b true; + } + if (options.global.link_mode == .Dynamic) { + if (options.inherited.pic == false) + return error.DynamicLinkingRequiresPic; + break :b true; + } + if (options.inherited.pic) |x| break :b x; + if (options.parent) |p| break :b p.pic; + break :b false; + }; + + const red_zone = b: { + if (!target_util.hasRedZone(target)) { + if (options.inherited.red_zone == true) + return error.TargetHasNoRedZone; + break :b true; + } + if (options.inherited.red_zone) |x| break :b x; + if (options.parent) |p| break :b p.red_zone; + break :b true; + }; + + const omit_frame_pointer = b: { + if (options.inherited.omit_frame_pointer) |x| break :b x; + if (options.parent) |p| break :b p.omit_frame_pointer; + if (optimize_mode == .Debug) break :b false; + break :b true; + }; + + const sanitize_thread = b: { + if (options.inherited.sanitize_thread) |x| break :b x; + if (options.parent) |p| break :b p.sanitize_thread; + break :b false; + }; + + const code_model = b: { + if (options.inherited.code_model) |x| break :b x; + if (options.parent) |p| break :b p.code_model; + break :b .default; + }; + + const is_safe_mode = switch (optimize_mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; + + const sanitize_c = b: { + if (options.inherited.sanitize_c) |x| break :b x; + if (options.parent) |p| break :b p.sanitize_c; + break :b is_safe_mode; + }; + + const stack_check = b: { + if (!target_util.supportsStackProbing(target)) { + if (options.inherited.stack_check == true) + return error.StackCheckUnsupportedByTarget; + break :b false; + } + if (options.inherited.stack_check) |x| break :b x; + if (options.parent) |p| break :b p.stack_check; + break :b is_safe_mode; + }; + + const stack_protector: u32 = sp: { + if (!target_util.supportsStackProtector(target, zig_backend)) { + if (options.inherited.stack_protector) |x| { + if (x > 0) return error.StackProtectorUnsupportedByTarget; + } + break :sp 0; + } + + // This logic is checking for linking libc because otherwise our start code + // which is trying to set up TLS (i.e. the fs/gs registers) but the stack + // protection code depends on fs/gs registers being already set up. + // If we were able to annotate start code, or perhaps the entire std lib, + // as being exempt from stack protection checks, we could change this logic + // to supporting stack protection even when not linking libc. + // TODO file issue about this + if (!options.global.link_libc) { + if (options.inherited.stack_protector) |x| { + if (x > 0) return error.StackProtectorUnavailableWithoutLibC; + } + break :sp 0; + } + + if (options.inherited.stack_protector) |x| break :sp x; + if (options.parent) |p| break :sp p.stack_protector; + if (!is_safe_mode) break :sp 0; + + break :sp target_util.default_stack_protector_buffer_size; + }; + + const llvm_cpu_features: ?[*:0]const u8 = b: { + if (resolved_target.llvm_cpu_features) |x| break :b x; + if (!options.global.use_llvm) break :b null; + + var buf = std.ArrayList(u8).init(arena); + for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| { + const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); + const is_enabled = target.cpu.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + const plus_or_minus = "-+"[@intFromBool(is_enabled)]; + try buf.ensureUnusedCapacity(2 + llvm_name.len); + buf.appendAssumeCapacity(plus_or_minus); + buf.appendSliceAssumeCapacity(llvm_name); + buf.appendSliceAssumeCapacity(","); + } + } + if (buf.items.len == 0) break :b ""; + assert(std.mem.endsWith(u8, buf.items, ",")); + buf.items[buf.items.len - 1] = 0; + buf.shrinkAndFree(buf.items.len); + break :b buf.items[0 .. buf.items.len - 1 :0].ptr; + }; + + const builtin_mod = options.builtin_mod orelse b: { + const generated_builtin_source = try Builtin.generate(.{ + .target = target, + .zig_backend = zig_backend, + .output_mode = options.global.output_mode, + .link_mode = options.global.link_mode, + .is_test = options.global.is_test, + .test_evented_io = options.global.test_evented_io, + .single_threaded = single_threaded, + .link_libc = options.global.link_libc, + .link_libcpp = options.global.link_libcpp, + .optimize_mode = optimize_mode, + .error_tracing = error_tracing, + .valgrind = valgrind, + .sanitize_thread = sanitize_thread, + .pic = pic, + .pie = options.global.pie, + .strip = strip, + .code_model = code_model, + .omit_frame_pointer = omit_frame_pointer, + .wasi_exec_model = options.global.wasi_exec_model, + }, arena); + + const digest = Cache.HashHelper.oneShot(generated_builtin_source); + const builtin_sub_path = try arena.dupe(u8, "b" ++ std.fs.path.sep_str ++ digest); + const new = try arena.create(Module); + new.* = .{ + .root = .{ + .root_dir = options.global_cache_directory, + .sub_path = builtin_sub_path, + }, + .root_src_path = "builtin.zig", + .fully_qualified_name = if (options.parent == null) + "builtin" + else + try std.fmt.allocPrint(arena, "{s}.builtin", .{options.fully_qualified_name}), + .resolved_target = .{ + .result = target, + .is_native_os = resolved_target.is_native_os, + .is_native_abi = resolved_target.is_native_abi, + .llvm_cpu_features = llvm_cpu_features, + }, + .optimize_mode = optimize_mode, + .single_threaded = single_threaded, + .error_tracing = error_tracing, + .valgrind = valgrind, + .pic = pic, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, + .stack_check = stack_check, + .stack_protector = stack_protector, + .code_model = code_model, + .red_zone = red_zone, + .generated_builtin_source = generated_builtin_source, + .sanitize_c = sanitize_c, + .sanitize_thread = sanitize_thread, + .unwind_tables = unwind_tables, + .cc_argv = &.{}, + }; + break :b new; + }; + + const mod = try arena.create(Module); + mod.* = .{ + .root = options.paths.root, + .root_src_path = options.paths.root_src_path, + .fully_qualified_name = options.fully_qualified_name, + .resolved_target = .{ + .result = target, + .is_native_os = resolved_target.is_native_os, + .is_native_abi = resolved_target.is_native_abi, + .llvm_cpu_features = llvm_cpu_features, + }, + .optimize_mode = optimize_mode, + .single_threaded = single_threaded, + .error_tracing = error_tracing, + .valgrind = valgrind, + .pic = pic, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, + .stack_check = stack_check, + .stack_protector = stack_protector, + .code_model = code_model, + .red_zone = red_zone, + .generated_builtin_source = builtin_mod.generated_builtin_source, + .sanitize_c = sanitize_c, + .sanitize_thread = sanitize_thread, + .unwind_tables = unwind_tables, + .cc_argv = options.cc_argv, + }; + + try mod.deps.ensureUnusedCapacity(arena, 1); + mod.deps.putAssumeCapacityNoClobber("builtin", builtin_mod); + + return mod; +} + +/// All fields correspond to `CreateOptions`. +pub const LimitedOptions = struct { + root: Package.Path, + root_src_path: []const u8, + fully_qualified_name: []const u8, +}; + +/// This one can only be used if the Module will only be used for AstGen and earlier in +/// the pipeline. Illegal behavior occurs if a limited module touches Sema. +pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*Package.Module { + const mod = try gpa.create(Module); + mod.* = .{ + .root = options.root, + .root_src_path = options.root_src_path, + .fully_qualified_name = options.fully_qualified_name, + + .resolved_target = undefined, + .optimize_mode = undefined, + .code_model = undefined, + .single_threaded = undefined, + .error_tracing = undefined, + .valgrind = undefined, + .pic = undefined, + .strip = undefined, + .omit_frame_pointer = undefined, + .stack_check = undefined, + .stack_protector = undefined, + .red_zone = undefined, + .sanitize_c = undefined, + .sanitize_thread = undefined, + .unwind_tables = undefined, + .cc_argv = undefined, + .generated_builtin_source = undefined, + }; + return mod; +} + +pub fn getBuiltinDependency(m: *Module) *Module { + return m.deps.values()[0]; } const Module = @This(); @@ -32,3 +424,8 @@ const Package = @import("../Package.zig"); const std = @import("std"); const Allocator = std.mem.Allocator; const MultiHashHexDigest = Package.Manifest.MultiHashHexDigest; +const target_util = @import("../target.zig"); +const Cache = std.Build.Cache; +const Builtin = @import("../Builtin.zig"); +const assert = std.debug.assert; +const Compilation = @import("../Compilation.zig"); diff --git a/src/Sema.zig b/src/Sema.zig index 516fee4543..9d1f65584e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -36668,7 +36668,7 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Int const mod = sema.mod; const ip = &mod.intern_pool; - const std_mod = mod.main_mod.deps.get("std").?; + const std_mod = mod.std_mod; const std_file = (mod.importPkg(std_mod) catch unreachable).file; const opt_builtin_inst = (try sema.namespaceLookupRef( block, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 70eab9489c..cf6f38a53e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -853,16 +853,9 @@ pub const Object = struct { /// want to iterate over it while adding entries to it. pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr); - pub fn create(gpa: Allocator, options: link.Options) !*Object { - const obj = try gpa.create(Object); - errdefer gpa.destroy(obj); - obj.* = try Object.init(gpa, options); - return obj; - } - - pub fn init(gpa: Allocator, options: link.Options) !Object { - const llvm_target_triple = try targetTriple(gpa, options.target); - defer gpa.free(llvm_target_triple); + pub fn create(arena: Allocator, options: link.File.OpenOptions) !*Object { + const gpa = options.comp.gpa; + const llvm_target_triple = try targetTriple(arena, options.target); var builder = try Builder.init(.{ .allocator = gpa, @@ -899,19 +892,14 @@ pub const Object = struct { // TODO: the only concern I have with this is WASI as either host or target, should // we leave the paths as relative then? const compile_unit_dir_z = blk: { - var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; if (options.module) |mod| m: { - const d = try mod.root_mod.root.joinStringZ(builder.gpa, ""); + const d = try mod.root_mod.root.joinStringZ(arena, ""); if (d.len == 0) break :m; if (std.fs.path.isAbsolute(d)) break :blk d; - const abs = std.fs.realpath(d, &buf) catch break :blk d; - builder.gpa.free(d); - break :blk try builder.gpa.dupeZ(u8, abs); + break :blk std.fs.realpathAlloc(arena, d) catch d; } - const cwd = try std.process.getCwd(&buf); - break :blk try builder.gpa.dupeZ(u8, cwd); + break :blk try std.process.getCwdAlloc(arena); }; - defer builder.gpa.free(compile_unit_dir_z); builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit( DW.LANG.C99, @@ -989,7 +977,8 @@ pub const Object = struct { } } - return .{ + const obj = try arena.create(Object); + obj.* = .{ .gpa = gpa, .builder = builder, .module = options.module.?, @@ -1009,9 +998,11 @@ pub const Object = struct { .null_opt_usize = .no_init, .struct_field_map = .{}, }; + return obj; } - pub fn deinit(self: *Object, gpa: Allocator) void { + pub fn deinit(self: *Object) void { + const gpa = self.gpa; self.di_map.deinit(gpa); self.di_type_map.deinit(gpa); if (self.builder.useLibLlvm()) { @@ -1028,11 +1019,6 @@ pub const Object = struct { self.* = undefined; } - pub fn destroy(self: *Object, gpa: Allocator) void { - self.deinit(gpa); - gpa.destroy(self); - } - fn locPath( arena: Allocator, opt_loc: ?Compilation.EmitLoc, @@ -2899,7 +2885,7 @@ pub const Object = struct { fn getStackTraceType(o: *Object) Allocator.Error!Type { const mod = o.module; - const std_mod = mod.main_mod.deps.get("std").?; + const std_mod = mod.std_mod; const std_file = (mod.importPkg(std_mod) catch unreachable).file; const builtin_str = try mod.intern_pool.getOrPutString(mod.gpa, "builtin"); diff --git a/src/link.zig b/src/link.zig index 1ad66eaf7a..46dab1b6ed 100644 --- a/src/link.zig +++ b/src/link.zig @@ -66,237 +66,18 @@ pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version; -pub const Emit = struct { - /// Where the output will go. - directory: Compilation.Directory, - /// Path to the output file, relative to `directory`. - sub_path: []const u8, - - /// Returns the full path to `basename` if it were in the same directory as the - /// `Emit` sub_path. - pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 { - const full_path = if (emit.directory.path) |p| - try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) - else - emit.sub_path; - - if (fs.path.dirname(full_path)) |dirname| { - return try fs.path.joinZ(arena, &.{ dirname, basename }); - } else { - return basename; - } - } -}; - -pub const Options = struct { - /// This is `null` when `-fno-emit-bin` is used. - emit: ?Emit, - /// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used. - implib_emit: ?Emit, - /// This is non-null when `-femit-docs` is provided. - docs_emit: ?Emit, - target: std.Target, - output_mode: std.builtin.OutputMode, - link_mode: std.builtin.LinkMode, - optimize_mode: std.builtin.OptimizeMode, - machine_code_model: std.builtin.CodeModel, - root_name: [:0]const u8, - /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. - module: ?*Module, - /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) - sysroot: ?[]const u8, - /// Used for calculating how much space to reserve for symbols in case the binary file - /// does not already have a symbol table. - symbol_count_hint: u64 = 32, - /// Used for calculating how much space to reserve for executable program code in case - /// the binary file does not already have such a section. - program_code_size_hint: u64 = 256 * 1024, - entry_addr: ?u64 = null, - entry: ?[]const u8, - stack_size_override: ?u64, - image_base_override: ?u64, - /// 0 means no stack protector - /// other value means stack protector with that buffer size. - stack_protector: u32, - cache_mode: CacheMode, - include_compiler_rt: bool, - /// Set to `true` to omit debug info. - strip: bool, - /// If this is true then this link code is responsible for outputting an object - /// file and then using LLD to link it together with the link options and other objects. - /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. - use_lld: bool, - /// If this is true then this link code is responsible for making an LLVM IR Module, - /// outputting it to an object file, and then linking that together with link options and - /// other objects. - /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. - use_llvm: bool, - use_lib_llvm: bool, - link_libc: bool, - link_libcpp: bool, - link_libunwind: bool, - darwin_sdk_layout: ?DarwinSdkLayout, - function_sections: bool, - data_sections: bool, - no_builtin: bool, - eh_frame_hdr: bool, - emit_relocs: bool, - rdynamic: bool, - z_nodelete: bool, - z_notext: bool, - z_defs: bool, - z_origin: bool, - z_nocopyreloc: bool, - z_now: bool, - z_relro: bool, - z_common_page_size: ?u64, - z_max_page_size: ?u64, - tsaware: bool, - nxcompat: bool, - dynamicbase: bool, - linker_optimization: u8, - compress_debug_sections: CompressDebugSections, - bind_global_refs_locally: bool, - import_memory: bool, - export_memory: bool, - import_symbols: bool, - import_table: bool, - export_table: bool, - initial_memory: ?u64, - max_memory: ?u64, - shared_memory: bool, - export_symbol_names: []const []const u8, - global_base: ?u64, - is_native_os: bool, - is_native_abi: bool, - pic: bool, - pie: bool, - lto: bool, - valgrind: bool, - tsan: bool, - stack_check: bool, - red_zone: bool, - omit_frame_pointer: bool, - single_threaded: bool, - verbose_link: bool, - dll_export_fns: bool, - error_return_tracing: bool, - skip_linker_dependencies: bool, - each_lib_rpath: bool, - build_id: std.zig.BuildId, - disable_lld_caching: bool, - is_test: bool, - hash_style: HashStyle, - sort_section: ?SortSection, - major_subsystem_version: ?u32, - minor_subsystem_version: ?u32, - gc_sections: ?bool = null, - allow_shlib_undefined: ?bool, - subsystem: ?std.Target.SubSystem, - linker_script: ?[]const u8, - version_script: ?[]const u8, - soname: ?[]const u8, - llvm_cpu_features: ?[*:0]const u8, - print_gc_sections: bool, - print_icf_sections: bool, - print_map: bool, - opt_bisect_limit: i32, - - objects: []Compilation.LinkObject, - framework_dirs: []const []const u8, - frameworks: []const Framework, - /// These are *always* dynamically linked. Static libraries will be - /// provided as positional arguments. - system_libs: std.StringArrayHashMapUnmanaged(SystemLib), - wasi_emulated_libs: []const wasi_libc.CRTFile, - // TODO: remove this. libraries are resolved by the frontend. - lib_dirs: []const []const u8, - rpath_list: []const []const u8, - - /// List of symbols forced as undefined in the symbol table - /// thus forcing their resolution by the linker. - /// Corresponds to `-u ` for ELF/MachO and `/include:` for COFF/PE. - force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), - /// Use a wrapper function for symbol. Any undefined reference to symbol - /// will be resolved to __wrap_symbol. Any undefined reference to - /// __real_symbol will be resolved to symbol. This can be used to provide a - /// wrapper for a system function. The wrapper function should be called - /// __wrap_symbol. If it wishes to call the system function, it should call - /// __real_symbol. - symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), - - version: ?std.SemanticVersion, - compatibility_version: ?std.SemanticVersion, - libc_installation: ?*const LibCInstallation, - - dwarf_format: ?std.dwarf.Format, - - /// WASI-only. Type of WASI execution model ("command" or "reactor"). - wasi_exec_model: std.builtin.WasiExecModel = undefined, - - /// (Zig compiler development) Enable dumping of linker's state as JSON. - enable_link_snapshots: bool = false, - - /// (Darwin) Install name for the dylib - install_name: ?[]const u8 = null, - - /// (Darwin) Path to entitlements file - entitlements: ?[]const u8 = null, - - /// (Darwin) size of the __PAGEZERO segment - pagezero_size: ?u64 = null, - - /// (Darwin) set minimum space for future expansion of the load commands - headerpad_size: ?u32 = null, - - /// (Darwin) set enough space as if all paths were MATPATHLEN - headerpad_max_install_names: bool = false, - - /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols - dead_strip_dylibs: bool = false, - - /// (Windows) PDB source path prefix to instruct the linker how to resolve relative - /// paths when consolidating CodeView streams into a single PDB file. - pdb_source_path: ?[]const u8 = null, - - /// (Windows) PDB output path - pdb_out_path: ?[]const u8 = null, - - /// (Windows) .def file to specify when linking - module_definition_file: ?[]const u8 = null, - - /// (SPIR-V) whether to generate a structured control flow graph or not - want_structured_cfg: ?bool = null, - - pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { - return if (options.use_lld) .Obj else options.output_mode; - } - - pub fn move(self: *Options) Options { - const copied_state = self.*; - self.system_libs = .{}; - self.force_undefined_symbols = .{}; - return copied_state; - } -}; - pub const HashStyle = enum { sysv, gnu, both }; pub const CompressDebugSections = enum { none, zlib, zstd }; -/// The filesystem layout of darwin SDK elements. -pub const DarwinSdkLayout = enum { - /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }. - sdk, - /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, }. - vendored, -}; - pub const File = struct { tag: Tag, - options: Options, + + /// The owner of this output File. + comp: *Compilation, + emit: Compilation.Emit, + file: ?fs.File, - allocator: Allocator, /// When linking with LLD, this linker code will output an object file only at /// this location, and then this path can be placed on the LLD linker line. intermediary_basename: ?[]const u8 = null, @@ -307,103 +88,132 @@ pub const File = struct { child_pid: ?std.ChildProcess.Id = null, + pub const OpenOptions = struct { + comp: *Compilation, + emit: Compilation.Emit, + + symbol_count_hint: u64 = 32, + program_code_size_hint: u64 = 256 * 1024, + + /// Virtual address of the entry point procedure relative to image base. + entry_addr: ?u64, + stack_size_override: ?u64, + image_base_override: ?u64, + function_sections: bool, + data_sections: bool, + no_builtin: bool, + eh_frame_hdr: bool, + emit_relocs: bool, + rdynamic: bool, + optimization: u8, + linker_script: ?[]const u8, + z_nodelete: bool, + z_notext: bool, + z_defs: bool, + z_origin: bool, + z_nocopyreloc: bool, + z_now: bool, + z_relro: bool, + z_common_page_size: ?u64, + z_max_page_size: ?u64, + tsaware: bool, + nxcompat: bool, + dynamicbase: bool, + compress_debug_sections: CompressDebugSections, + bind_global_refs_locally: bool, + import_symbols: bool, + import_table: bool, + export_table: bool, + initial_memory: ?u64, + max_memory: ?u64, + export_symbol_names: []const []const u8, + global_base: ?u64, + verbose_link: bool, + dll_export_fns: bool, + skip_linker_dependencies: bool, + parent_compilation_link_libc: bool, + each_lib_rpath: bool, + build_id: std.zig.BuildId, + disable_lld_caching: bool, + hash_style: HashStyle, + sort_section: ?SortSection, + major_subsystem_version: ?u32, + minor_subsystem_version: ?u32, + gc_sections: ?bool = null, + allow_shlib_undefined: ?bool, + subsystem: ?std.Target.SubSystem, + version_script: ?[]const u8, + soname: ?[]const u8, + print_gc_sections: bool, + print_icf_sections: bool, + print_map: bool, + opt_bisect_limit: i32, + + /// List of symbols forced as undefined in the symbol table + /// thus forcing their resolution by the linker. + /// Corresponds to `-u ` for ELF/MachO and `/include:` for COFF/PE. + force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), + /// Use a wrapper function for symbol. Any undefined reference to symbol + /// will be resolved to __wrap_symbol. Any undefined reference to + /// __real_symbol will be resolved to symbol. This can be used to provide a + /// wrapper for a system function. The wrapper function should be called + /// __wrap_symbol. If it wishes to call the system function, it should call + /// __real_symbol. + symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), + + compatibility_version: ?std.SemanticVersion, + + dwarf_format: ?std.dwarf.Format, + + // TODO: remove this. libraries are resolved by the frontend. + lib_dirs: []const []const u8, + rpath_list: []const []const u8, + + /// (Zig compiler development) Enable dumping of linker's state as JSON. + enable_link_snapshots: bool, + + /// (Darwin) Install name for the dylib + install_name: ?[]const u8, + /// (Darwin) Path to entitlements file + entitlements: ?[]const u8, + /// (Darwin) size of the __PAGEZERO segment + pagezero_size: ?u64, + /// (Darwin) set minimum space for future expansion of the load commands + headerpad_size: ?u32, + /// (Darwin) set enough space as if all paths were MATPATHLEN + headerpad_max_install_names: bool, + /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols + dead_strip_dylibs: bool, + framework_dirs: []const []const u8, + frameworks: []const Framework, + darwin_sdk_layout: ?MachO.SdkLayout, + + /// (Windows) PDB source path prefix to instruct the linker how to resolve relative + /// paths when consolidating CodeView streams into a single PDB file. + pdb_source_path: ?[]const u8, + /// (Windows) PDB output path + pdb_out_path: ?[]const u8, + /// (Windows) .def file to specify when linking + module_definition_file: ?[]const u8, + + /// (SPIR-V) whether to generate a structured control flow graph or not + want_structured_cfg: ?bool, + + wasi_emulated_libs: []const wasi_libc.CRTFile, + }; + /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and /// rewriting it. A malicious file is detected as incremental link failure /// and does not cause Illegal Behavior. This operation is not atomic. - pub fn openPath(allocator: Allocator, options: Options) !*File { - const have_macho = !build_options.only_c; - if (have_macho and options.target.ofmt == .macho) { - return &(try MachO.openPath(allocator, options)).base; - } - - if (options.emit == null) { - return switch (options.target.ofmt) { - .coff => &(try Coff.createEmpty(allocator, options)).base, - .elf => &(try Elf.createEmpty(allocator, options)).base, - .macho => unreachable, - .wasm => &(try Wasm.createEmpty(allocator, options)).base, - .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, - .c => unreachable, // Reported error earlier. - .spirv => &(try SpirV.createEmpty(allocator, options)).base, - .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, - .hex => return error.HexObjectFormatUnimplemented, - .raw => return error.RawObjectFormatUnimplemented, - .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, - }; - } - const emit = options.emit.?; - const use_lld = build_options.have_llvm and options.use_lld; // comptime-known false when !have_llvm - const sub_path = if (use_lld) blk: { - if (options.module == null) { - // No point in opening a file, we would not write anything to it. - // Initialize with empty. - return switch (options.target.ofmt) { - .coff => &(try Coff.createEmpty(allocator, options)).base, - .elf => &(try Elf.createEmpty(allocator, options)).base, - .macho => unreachable, - .plan9 => &(try Plan9.createEmpty(allocator, options)).base, - .wasm => &(try Wasm.createEmpty(allocator, options)).base, - .c => unreachable, // Reported error earlier. - .spirv => &(try SpirV.createEmpty(allocator, options)).base, - .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, - .hex => return error.HexObjectFormatUnimplemented, - .raw => return error.RawObjectFormatUnimplemented, - .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, - }; - } - // Open a temporary object file, not the final output file because we - // want to link with LLD. - break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ - emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), - }); - } else emit.sub_path; - errdefer if (use_lld) allocator.free(sub_path); - - const file: *File = f: { - switch (options.target.ofmt) { - .coff => { - if (build_options.only_c) unreachable; - break :f &(try Coff.openPath(allocator, sub_path, options)).base; - }, - .elf => { - if (build_options.only_c) unreachable; - break :f &(try Elf.openPath(allocator, sub_path, options)).base; - }, - .macho => unreachable, - .plan9 => { - if (build_options.only_c) unreachable; - break :f &(try Plan9.openPath(allocator, sub_path, options)).base; - }, - .wasm => { - if (build_options.only_c) unreachable; - break :f &(try Wasm.openPath(allocator, sub_path, options)).base; - }, - .c => { - break :f &(try C.openPath(allocator, sub_path, options)).base; - }, - .spirv => { - if (build_options.only_c) unreachable; - break :f &(try SpirV.openPath(allocator, sub_path, options)).base; - }, - .nvptx => { - if (build_options.only_c) unreachable; - break :f &(try NvPtx.openPath(allocator, sub_path, options)).base; - }, - .hex => return error.HexObjectFormatUnimplemented, - .raw => return error.RawObjectFormatUnimplemented, - .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, - } - }; - - if (use_lld) { - // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, - // we also want to put the intermediary object file in the cache while the - // main emit directory is the cwd. - file.intermediary_basename = sub_path; + /// `arena` is used for allocations with the same lifetime as the created File. + pub fn open(arena: Allocator, options: OpenOptions) !*File { + switch (Tag.fromObjectFormat(options.comp.root_mod.resolved_target.result.ofmt)) { + inline else => |tag| { + const ptr = try tag.Type().open(arena, options); + return &ptr.base; + }, } - - return file; } pub fn cast(base: *File, comptime T: type) ?*T { @@ -664,56 +474,45 @@ pub const File = struct { pub fn destroy(base: *File) void { base.releaseLock(); if (base.file) |f| f.close(); - if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); - base.options.system_libs.deinit(base.allocator); - base.options.force_undefined_symbols.deinit(base.allocator); switch (base.tag) { .coff => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(Coff, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .elf => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(Elf, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .macho => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(MachO, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .c => { const parent = @fieldParentPtr(C, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .wasm => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(Wasm, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .spirv => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(SpirV, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .plan9 => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(Plan9, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, .nvptx => { if (build_options.only_c) unreachable; const parent = @fieldParentPtr(NvPtx, "base", base); parent.deinit(); - base.allocator.destroy(parent); }, } } @@ -1197,6 +996,35 @@ pub const File = struct { spirv, plan9, nvptx, + + pub fn Type(comptime tag: Tag) type { + return switch (tag) { + .coff => Coff, + .elf => Elf, + .macho => MachO, + .c => C, + .wasm => Wasm, + .spirv => SpirV, + .plan9 => Plan9, + .nvptx => NvPtx, + }; + } + + pub fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag { + return switch (ofmt) { + .coff => .coff, + .elf => .elf, + .macho => .macho, + .wasm => .wasm, + .plan9 => .plan9, + .c => .c, + .spirv => .spirv, + .nvptx => .nvptx, + .hex => @panic("TODO implement hex object format"), + .raw => @panic("TODO implement raw object format"), + .dxcontainer => @panic("TODO implement dxcontainer object format"), + }; + } }; pub const ErrorFlags = struct { @@ -1235,6 +1063,33 @@ pub const File = struct { } }; + pub fn effectiveOutputMode( + use_lld: bool, + output_mode: std.builtin.OutputMode, + ) std.builtin.OutputMode { + return if (use_lld) .Obj else output_mode; + } + + pub fn determineMode( + use_lld: bool, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + ) fs.File.Mode { + // On common systems with a 0o022 umask, 0o777 will still result in a file created + // with 0o755 permissions, but it works appropriately if the system is configured + // more leniently. As another data point, C's fopen seems to open files with the + // 666 mode. + const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777; + switch (effectiveOutputMode(use_lld, output_mode)) { + .Lib => return switch (link_mode) { + .Dynamic => executable_mode, + .Static => fs.File.default_mode, + }, + .Exe => return executable_mode, + .Obj => return fs.File.default_mode, + } + } + pub const C = @import("link/C.zig"); pub const Coff = @import("link/Coff.zig"); pub const Plan9 = @import("link/Plan9.zig"); @@ -1245,19 +1100,3 @@ pub const File = struct { pub const NvPtx = @import("link/NvPtx.zig"); pub const Dwarf = @import("link/Dwarf.zig"); }; - -pub fn determineMode(options: Options) fs.File.Mode { - // On common systems with a 0o022 umask, 0o777 will still result in a file created - // with 0o755 permissions, but it works appropriately if the system is configured - // more leniently. As another data point, C's fopen seems to open files with the - // 666 mode. - const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777; - switch (options.effectiveOutputMode()) { - .Lib => return switch (options.link_mode) { - .Dynamic => executable_mode, - .Static => fs.File.default_mode, - }, - .Exe => return executable_mode, - .Obj => return fs.File.default_mode, - } -} diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 5fbf02871a..e477aeffcf 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -48,9 +48,6 @@ got_table_count_dirty: bool = true, got_table_contents_dirty: bool = true, imports_count_dirty: bool = true, -/// Virtual address of the entry point procedure relative to image base. -entry_addr: ?u32 = null, - /// Table of tracked LazySymbols. lazy_syms: LazySymbolTable = .{}, @@ -226,44 +223,150 @@ const ideal_factor = 3; const minimum_text_block_size = 64; pub const min_text_capacity = padToIdeal(minimum_text_block_size); -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff { - assert(options.target.ofmt == .coff); +pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff { + if (build_options.only_c) unreachable; + const target = options.comp.root_mod.resolved_target.result; + assert(target.ofmt == .coff); + + const self = try createEmpty(arena, options); + errdefer self.base.destroy(); + + const use_lld = build_options.have_llvm and options.comp.config.use_lld; + const use_llvm = build_options.have_llvm and options.comp.config.use_llvm; - if (options.use_llvm) { - return createEmpty(allocator, options); + if (use_lld and use_llvm) { + // LLVM emits the object file; LLD links it into the final product. + return self; } - const self = try createEmpty(allocator, options); - errdefer self.base.destroy(); + const sub_path = if (!use_lld) options.emit.sub_path else p: { + // Open a temporary object file, not the final output file because we + // want to link with LLD. + const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ + options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch), + }); + self.base.intermediary_basename = o_file_path; + break :p o_file_path; + }; - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + self.base.file = try options.emit.directory.handle.createFile(sub_path, .{ .truncate = false, .read = true, - .mode = link.determineMode(options), + .mode = link.File.determineMode( + use_lld, + options.comp.config.output_mode, + options.comp.config.link_mode, + ), }); - self.base.file = file; - try self.populateMissingMetadata(); + assert(self.llvm_object == null); + const gpa = self.base.comp.gpa; + + try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); + self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); + + try self.temp_strtab.buffer.append(gpa, 0); + + // Index 0 is always a null symbol. + try self.locals.append(gpa, .{ + .name = [_]u8{0} ** 8, + .value = 0, + .section_number = .UNDEFINED, + .type = .{ .base_type = .NULL, .complex_type = .NULL }, + .storage_class = .NULL, + .number_of_aux_symbols = 0, + }); + + if (self.text_section_index == null) { + const file_size: u32 = @intCast(options.program_code_size_hint); + self.text_section_index = try self.allocateSection(".text", file_size, .{ + .CNT_CODE = 1, + .MEM_EXECUTE = 1, + .MEM_READ = 1, + }); + } + + if (self.got_section_index == null) { + const file_size = @as(u32, @intCast(options.symbol_count_hint)) * self.ptr_width.size(); + self.got_section_index = try self.allocateSection(".got", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_READ = 1, + }); + } + + if (self.rdata_section_index == null) { + const file_size: u32 = self.page_size; + self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_READ = 1, + }); + } + + if (self.data_section_index == null) { + const file_size: u32 = self.page_size; + self.data_section_index = try self.allocateSection(".data", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_READ = 1, + .MEM_WRITE = 1, + }); + } + + if (self.idata_section_index == null) { + const file_size = @as(u32, @intCast(options.symbol_count_hint)) * self.ptr_width.size(); + self.idata_section_index = try self.allocateSection(".idata", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_READ = 1, + }); + } + + if (self.reloc_section_index == null) { + const file_size = @as(u32, @intCast(options.symbol_count_hint)) * @sizeOf(coff.BaseRelocation); + self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ + .CNT_INITIALIZED_DATA = 1, + .MEM_DISCARDABLE = 1, + .MEM_READ = 1, + }); + } + + if (self.strtab_offset == null) { + const file_size = @as(u32, @intCast(self.strtab.buffer.items.len)); + self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here + log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size }); + } + + { + // We need to find out what the max file offset is according to section headers. + // Otherwise, we may end up with an COFF binary with file size not matching the final section's + // offset + it's filesize. + // TODO I don't like this here one bit + var max_file_offset: u64 = 0; + for (self.sections.items(.header)) |header| { + if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) { + max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data; + } + } + try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); + } return self; } -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { - const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) { +pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff { + const target = options.comp.root_mod.resolved_target.result; + const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 0...32 => .p32, 33...64 => .p64, else => return error.UnsupportedCOFFArchitecture, }; - const page_size: u32 = switch (options.target.cpu.arch) { + const page_size: u32 = switch (target.cpu.arch) { else => 0x1000, }; - const self = try gpa.create(Coff); - errdefer gpa.destroy(self); + const self = try arena.create(Coff); self.* = .{ .base = .{ .tag = .coff, - .options = options, - .allocator = gpa, + .comp = options.comp, + .emit = options.emit, .file = null, }, .ptr_width = ptr_width, @@ -271,16 +374,17 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { .data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory), }; - if (options.use_llvm) { - self.llvm_object = try LlvmObject.create(gpa, options); + const use_llvm = build_options.have_llvm and options.comp.config.use_llvm; + if (use_llvm and options.comp.config.have_zcu) { + self.llvm_object = try LlvmObject.create(arena, options); } return self; } pub fn deinit(self: *Coff) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; - if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + if (self.llvm_object) |llvm_object| llvm_object.deinit(); for (self.objects.items) |*object| { object.deinit(gpa); @@ -349,97 +453,6 @@ pub fn deinit(self: *Coff) void { self.base_relocs.deinit(gpa); } -fn populateMissingMetadata(self: *Coff) !void { - assert(self.llvm_object == null); - const gpa = self.base.allocator; - - try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); - self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); - - try self.temp_strtab.buffer.append(gpa, 0); - - // Index 0 is always a null symbol. - try self.locals.append(gpa, .{ - .name = [_]u8{0} ** 8, - .value = 0, - .section_number = .UNDEFINED, - .type = .{ .base_type = .NULL, .complex_type = .NULL }, - .storage_class = .NULL, - .number_of_aux_symbols = 0, - }); - - if (self.text_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.program_code_size_hint)); - self.text_section_index = try self.allocateSection(".text", file_size, .{ - .CNT_CODE = 1, - .MEM_EXECUTE = 1, - .MEM_READ = 1, - }); - } - - if (self.got_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size(); - self.got_section_index = try self.allocateSection(".got", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_READ = 1, - }); - } - - if (self.rdata_section_index == null) { - const file_size: u32 = self.page_size; - self.rdata_section_index = try self.allocateSection(".rdata", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_READ = 1, - }); - } - - if (self.data_section_index == null) { - const file_size: u32 = self.page_size; - self.data_section_index = try self.allocateSection(".data", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_READ = 1, - .MEM_WRITE = 1, - }); - } - - if (self.idata_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size(); - self.idata_section_index = try self.allocateSection(".idata", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_READ = 1, - }); - } - - if (self.reloc_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * @sizeOf(coff.BaseRelocation); - self.reloc_section_index = try self.allocateSection(".reloc", file_size, .{ - .CNT_INITIALIZED_DATA = 1, - .MEM_DISCARDABLE = 1, - .MEM_READ = 1, - }); - } - - if (self.strtab_offset == null) { - const file_size = @as(u32, @intCast(self.strtab.buffer.items.len)); - self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here - log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size }); - } - - { - // We need to find out what the max file offset is according to section headers. - // Otherwise, we may end up with an COFF binary with file size not matching the final section's - // offset + it's filesize. - // TODO I don't like this here one bit - var max_file_offset: u64 = 0; - for (self.sections.items(.header)) |header| { - if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) { - max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data; - } - } - try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); - } -} - fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 { const index = @as(u16, @intCast(self.sections.slice().len)); const off = self.findFreeSpace(size, default_file_alignment); @@ -471,8 +484,9 @@ fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.Section .number_of_linenumbers = 0, .flags = flags, }; + const gpa = self.base.comp.gpa; try self.setSectionName(&header, name); - try self.sections.append(self.base.allocator, .{ .header = header }); + try self.sections.append(gpa, .{ .header = header }); return index; } @@ -654,7 +668,7 @@ fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignme } pub fn allocateSymbol(self: *Coff) !u32 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.locals.ensureUnusedCapacity(gpa, 1); const index = blk: { @@ -682,7 +696,7 @@ pub fn allocateSymbol(self: *Coff) !u32 { } fn allocateGlobal(self: *Coff) !u32 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.globals.ensureUnusedCapacity(gpa, 1); const index = blk: { @@ -706,15 +720,16 @@ fn allocateGlobal(self: *Coff) !u32 { } fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void { + const gpa = self.base.comp.gpa; if (self.got_table.lookup.contains(target)) return; - const got_index = try self.got_table.allocateEntry(self.base.allocator, target); + const got_index = try self.got_table.allocateEntry(gpa, target); try self.writeOffsetTableEntry(got_index); self.got_table_count_dirty = true; self.markRelocsDirtyByTarget(target); } pub fn createAtom(self: *Coff) !Atom.Index { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len)); const atom = try self.atoms.addOne(gpa); const sym_index = try self.allocateSymbol(); @@ -759,7 +774,7 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { file_offset + code.len, }); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; // Gather relocs which can be resolved. // We need to do this as we will be applying different slide values depending @@ -870,7 +885,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { if (is_hot_update_compatible) { if (self.base.child_pid) |handle| { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const slide = @intFromPtr(self.hot_state.loaded_base_address.?); const actual_vmaddr = vmaddr + slide; const pvaddr = @as(*anyopaque, @ptrFromInt(actual_vmaddr)); @@ -974,7 +989,7 @@ pub fn ptraceDetach(self: *Coff, handle: std.ChildProcess.Id) void { fn freeAtom(self: *Coff, atom_index: Atom.Index) void { log.debug("freeAtom {d}", .{atom_index}); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; // Remove any relocs and base relocs associated with this Atom Atom.freeRelocations(self, atom_index); @@ -1061,7 +1076,8 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air: self.freeUnnamedConsts(decl_index); Atom.freeRelocations(self, atom_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); const res = try codegen.generateFunction( @@ -1090,7 +1106,7 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air: } pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); @@ -1121,7 +1137,7 @@ const LowerConstResult = union(enum) { }; fn lowerConst(self: *Coff, name: []const u8, tv: TypedValue, required_alignment: InternPool.Alignment, sect_id: u16, src_loc: Module.SrcLoc) !LowerConstResult { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1174,13 +1190,14 @@ pub fn updateDecl( return; } + const gpa = self.base.comp.gpa; if (decl.isExtern(mod)) { // TODO make this part of getGlobalSymbol const variable = decl.getOwnedVariable(mod).?; const name = mod.intern_pool.stringToSlice(decl.name); const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); const global_index = try self.getGlobalSymbol(name, lib_name); - try self.need_got_table.put(self.base.allocator, global_index, {}); + try self.need_got_table.put(gpa, global_index, {}); return; } @@ -1188,7 +1205,7 @@ pub fn updateDecl( Atom.freeRelocations(self, atom_index); const atom = self.getAtom(atom_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); const decl_val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; @@ -1220,7 +1237,7 @@ fn updateLazySymbolAtom( atom_index: Atom.Index, section_index: u16, ) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; var required_alignment: InternPool.Alignment = .none; @@ -1281,8 +1298,9 @@ fn updateLazySymbolAtom( } pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Atom.Index { + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; - const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod)); + const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { @@ -1305,7 +1323,8 @@ pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Ato } pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: InternPool.DeclIndex) !Atom.Index { - const gop = try self.decls.getOrPut(self.base.allocator, decl_index); + const gpa = self.base.comp.gpa; + const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{ .atom = try self.createAtom(), @@ -1401,7 +1420,7 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com } fn freeUnnamedConsts(self: *Coff, decl_index: InternPool.DeclIndex) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom_index| { self.freeAtom(atom_index); @@ -1412,6 +1431,7 @@ fn freeUnnamedConsts(self: *Coff, decl_index: InternPool.DeclIndex) void { pub fn freeDecl(self: *Coff, decl_index: InternPool.DeclIndex) void { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -1421,7 +1441,7 @@ pub fn freeDecl(self: *Coff, decl_index: InternPool.DeclIndex) void { var kv = const_kv; self.freeAtom(kv.value.atom); self.freeUnnamedConsts(decl_index); - kv.value.exports.deinit(self.base.allocator); + kv.value.exports.deinit(gpa); } } @@ -1476,7 +1496,7 @@ pub fn updateExports( if (self.base.options.emit == null) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const metadata = switch (exported) { .decl_index => |decl_index| blk: { @@ -1574,7 +1594,7 @@ pub fn deleteDeclExport( const name = mod.intern_pool.stringToSlice(name_ip); const sym_index = metadata.getExportPtr(self, name) orelse return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null }; const sym = self.getSymbolPtr(sym_loc); log.debug("deleting export '{s}'", .{name}); @@ -1602,7 +1622,7 @@ pub fn deleteDeclExport( } fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym = self.getSymbol(current); const sym_name = self.getSymbolName(current); @@ -1653,7 +1673,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod sub_prog_node.activate(); defer sub_prog_node.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; @@ -1794,7 +1814,7 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const decl_alignment = switch (explicit_alignment) { @@ -1868,7 +1888,7 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8, lib_name_name: ?[]const u8 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; gop.value_ptr.* = sym_loc; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym = self.getSymbolPtr(sym_loc); try self.setSymbolName(sym, name); sym.storage_class = .EXTERNAL; @@ -1895,7 +1915,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: InternPool /// TODO: note that .ABSOLUTE is used as padding within each block; we could use this fact to do /// incremental updates and writes into the table instead of doing it all at once fn writeBaseRelocations(self: *Coff) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa); defer { @@ -2006,7 +2026,7 @@ fn writeImportTables(self: *Coff) !void { if (self.idata_section_index == null) return; if (!self.imports_count_dirty) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const ext = ".dll"; const header = &self.sections.items(.header)[self.idata_section_index.?]; @@ -2154,7 +2174,8 @@ fn writeStrtab(self: *Coff) !void { log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size }); - var buffer = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); try buffer.ensureTotalCapacityPrecise(needed_size); buffer.appendSliceAssumeCapacity(self.strtab.buffer.items); @@ -2176,7 +2197,7 @@ fn writeDataDirectoriesHeaders(self: *Coff) !void { } fn writeHeader(self: *Coff) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); const writer = buffer.writer(); @@ -2499,7 +2520,7 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult if (self.getGlobalPtr(name)) |ptr| { return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; } - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const global_index = try self.allocateGlobal(); const global_name = try gpa.dupe(u8, name); _ = try self.resolver.put(gpa, global_name, global_index); @@ -2530,7 +2551,8 @@ fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !v @memset(header.name[name.len..], 0); return; } - const offset = try self.strtab.insert(self.base.allocator, name); + const gpa = self.base.comp.gpa; + const offset = try self.strtab.insert(gpa, name); const name_offset = fmt.bufPrint(&header.name, "/{d}", .{offset}) catch unreachable; @memset(header.name[name_offset.len..], 0); } @@ -2549,7 +2571,8 @@ fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { @memset(symbol.name[name.len..], 0); return; } - const offset = try self.strtab.insert(self.base.allocator, name); + const gpa = self.base.comp.gpa; + const offset = try self.strtab.insert(gpa, name); @memset(symbol.name[0..4], 0); mem.writeInt(u32, symbol.name[4..8], offset, .little); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 6f3c59c4e7..ef25c63e32 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -200,26 +200,34 @@ pub const min_text_capacity = padToIdeal(minimum_atom_size); pub const PtrWidth = enum { p32, p64 }; -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { - assert(options.target.ofmt == .elf); +pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf { + if (build_options.only_c) unreachable; + const target = options.comp.root_mod.resolved_target.result; + assert(target.ofmt == .elf); - const self = try createEmpty(allocator, options); + const use_lld = build_options.have_llvm and options.comp.config.use_lld; + const use_llvm = build_options.have_llvm and options.comp.config.use_llvm; + + const self = try createEmpty(arena, options); errdefer self.base.destroy(); + if (use_lld and use_llvm) { + // LLVM emits the object file; LLD links it into the final product. + return self; + } + const is_obj = options.output_mode == .Obj; const is_obj_or_ar = is_obj or (options.output_mode == .Lib and options.link_mode == .Static); - if (options.use_llvm) { - const use_lld = build_options.have_llvm and self.base.options.use_lld; - if (use_lld) return self; - - if (options.module != null) { - self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ - sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), - }); - } - } - errdefer if (self.base.intermediary_basename) |path| allocator.free(path); + const sub_path = if (!use_lld) options.emit.sub_path else p: { + // Open a temporary object file, not the final output file because we + // want to link with LLD. + const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ + options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch), + }); + self.base.intermediary_basename = o_file_path; + break :p o_file_path; + }; self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = false, @@ -227,24 +235,26 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option .mode = link.determineMode(options), }); + const gpa = options.comp.gpa; + // Index 0 is always a null symbol. - try self.symbols.append(allocator, .{}); + try self.symbols.append(gpa, .{}); // Index 0 is always a null symbol. - try self.symbols_extra.append(allocator, 0); + try self.symbols_extra.append(gpa, 0); // Allocate atom index 0 to null atom - try self.atoms.append(allocator, .{}); + try self.atoms.append(gpa, .{}); // Append null file at index 0 - try self.files.append(allocator, .null); + try self.files.append(gpa, .null); // Append null byte to string tables - try self.shstrtab.append(allocator, 0); - try self.strtab.append(allocator, 0); + try self.shstrtab.append(gpa, 0); + try self.strtab.append(gpa, 0); // There must always be a null shdr in index 0 _ = try self.addSection(.{ .name = "" }); // Append null symbol in output symtab - try self.symtab.append(allocator, null_sym); + try self.symtab.append(gpa, null_sym); if (!is_obj_or_ar) { - try self.dynstrtab.append(allocator, 0); + try self.dynstrtab.append(gpa, 0); // Initialize PT_PHDR program header const p_align: u16 = switch (self.ptr_width) { @@ -283,10 +293,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option } if (options.module != null and !options.use_llvm) { - const index = @as(File.Index, @intCast(try self.files.addOne(allocator))); + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = try std.fmt.allocPrint(self.base.allocator, "{s}.o", .{std.fs.path.stem( + .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem( options.module.?.main_mod.root_src_path, )}), } }); @@ -298,16 +308,16 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option return self; } -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { - const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) { +pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { + const target = options.comp.root_mod.resolved_target.result; + const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 0...32 => .p32, 33...64 => .p64, else => return error.UnsupportedELFArchitecture, }; - const self = try gpa.create(Elf); - errdefer gpa.destroy(self); + const self = try arena.create(Elf); - const page_size: u32 = switch (options.target.cpu.arch) { + const page_size: u32 = switch (target.cpu.arch) { .powerpc64le => 0x10000, .sparc64 => 0x2000, else => 0x1000, @@ -321,25 +331,25 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { self.* = .{ .base = .{ .tag = .elf, - .options = options, - .allocator = gpa, + .comp = options.comp, + .emit = options.emit, .file = null, }, .ptr_width = ptr_width, .page_size = page_size, .default_sym_version = default_sym_version, }; - if (options.use_llvm and options.module != null) { - self.llvm_object = try LlvmObject.create(gpa, options); + if (options.use_llvm and options.comp.config.have_zcu) { + self.llvm_object = try LlvmObject.create(arena, options); } return self; } pub fn deinit(self: *Elf) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; - if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + if (self.llvm_object) |llvm_object| llvm_object.deinit(); for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { .null => {}, @@ -496,10 +506,11 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { /// TODO move to ZigObject pub fn initMetadata(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const ptr_size = self.ptrWidthBytes(); - const ptr_bit_width = self.base.options.target.ptrBitWidth(); - const is_linux = self.base.options.target.os.tag == .linux; + const target = self.base.comp.root_mod.resolved_target.result; + const ptr_bit_width = target.ptrBitWidth(); + const is_linux = target.os.tag == .linux; const zig_object = self.zigObjectPtr().?; const fillSection = struct { @@ -943,7 +954,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (use_lld) return; } - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var sub_prog_node = prog_node.start("ELF Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); @@ -952,7 +963,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const target = self.base.options.target; + const target = self.base.comp.root_mod.resolved_target.result; const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: { @@ -1303,7 +1314,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); @@ -1447,7 +1458,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const } pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); @@ -1524,7 +1535,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const target = self.base.options.target; + const target = self.base.comp.root_mod.resolved_target.result; const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.intermediary_basename) |path| blk: { @@ -1574,7 +1585,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } } else { if (!self.isStatic()) { - if (self.base.options.target.dynamic_linker.get()) |path| { + if (target.dynamic_linker.get()) |path| { try argv.append("-dynamic-linker"); try argv.append(path); } @@ -1842,7 +1853,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const in_file = try std.fs.cwd().openFile(path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); @@ -1862,7 +1873,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const in_file = try std.fs.cwd().openFile(path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); @@ -1888,7 +1899,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const in_file = try std.fs.cwd().openFile(lib.path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); @@ -1910,7 +1921,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const in_file = try std.fs.cwd().openFile(lib.path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); @@ -1996,7 +2007,7 @@ fn accessLibPath( link_mode: ?std.builtin.LinkMode, ) !bool { const sep = fs.path.sep_str; - const target = self.base.options.target; + const target = self.base.comp.root_mod.resolved_target.result; test_path.clearRetainingCapacity(); const prefix = if (link_mode != null) "lib" else ""; const suffix = if (link_mode) |mode| switch (mode) { @@ -2190,7 +2201,7 @@ fn claimUnresolvedObject(self: *Elf) void { /// This is also the point where we will report undefined symbols for any /// alloc sections. fn scanRelocs(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); defer { @@ -2293,7 +2304,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; const have_dynamic_linker = self.base.options.link_libc and self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; - const target = self.base.options.target; + const target = self.base.comp.root_mod.resolved_target.result; const gc_sections = self.base.options.gc_sections orelse !is_obj; const stack_size = self.base.options.stack_size_override orelse 16777216; const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; @@ -2374,7 +2385,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.addBytes(libc_installation.crt_dir.?); } if (have_dynamic_linker) { - man.hash.addOptionalBytes(self.base.options.target.dynamic_linker.get()); + man.hash.addOptionalBytes(target.dynamic_linker.get()); } } man.hash.addOptionalBytes(self.base.options.soname); @@ -2687,7 +2698,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } if (have_dynamic_linker) { - if (self.base.options.target.dynamic_linker.get()) |dynamic_linker| { + if (target.dynamic_linker.get()) |dynamic_linker| { try argv.append("-dynamic-linker"); try argv.append(dynamic_linker); } @@ -2937,7 +2948,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { - const target_endian = self.base.options.target.cpu.arch.endian(); + const target = self.base.comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); switch (self.ptr_width) { .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @as(u32, @intCast(addr)), target_endian), .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), @@ -2945,8 +2957,9 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) } fn writeShdrTable(self: *Elf) !void { - const gpa = self.base.allocator; - const target_endian = self.base.options.target.cpu.arch.endian(); + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); const shsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Shdr), @@ -3001,8 +3014,9 @@ fn writeShdrTable(self: *Elf) !void { } fn writePhdrTable(self: *Elf) !void { - const gpa = self.base.allocator; - const target_endian = self.base.options.target.cpu.arch.endian(); + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; @@ -3054,7 +3068,8 @@ fn writeElfHeader(self: *Elf) !void { }; index += 1; - const endian = self.base.options.target.cpu.arch.endian(); + const target = self.base.comp.root_mod.resolved_target.result; + const endian = target.cpu.arch.endian(); hdr_buf[index] = switch (endian) { .little => elf.ELFDATA2LSB, .big => elf.ELFDATA2MSB, @@ -3083,7 +3098,7 @@ fn writeElfHeader(self: *Elf) !void { mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian); index += 2; - const machine = self.base.options.target.cpu.arch.toElfMachine(); + const machine = target.cpu.arch.toElfMachine(); mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(machine), endian); index += 2; @@ -3248,7 +3263,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { for (self.shdrs.items) |shdr| { if (self.getStartStopBasename(shdr)) |name| { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); @@ -3394,6 +3409,7 @@ fn initOutputSections(self: *Elf) !void { } fn initSyntheticSections(self: *Elf) !void { + const target = self.base.comp.root_mod.resolved_target.result; const ptr_size = self.ptrWidthBytes(); const needs_eh_frame = for (self.objects.items) |index| { @@ -3503,7 +3519,7 @@ fn initSyntheticSections(self: *Elf) !void { // a segfault in the dynamic linker trying to load a binary that is static // and doesn't contain .dynamic section. if (self.isStatic() and !self.base.options.pie) break :blk false; - break :blk self.base.options.target.dynamic_linker.get() != null; + break :blk target.dynamic_linker.get() != null; }; if (needs_interp) { self.interp_section_index = try self.addSection(.{ @@ -3613,7 +3629,7 @@ fn initSectionsObject(self: *Elf) !void { } fn initComdatGroups(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (self.objects.items) |index| { const object = self.file(index).?.object; @@ -3732,7 +3748,7 @@ fn initSpecialPhdrs(self: *Elf) !void { /// Ties are broken by the file prority which corresponds to the inclusion of input sections in this output section /// we are about to sort. fn sortInitFini(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const Entry = struct { priority: i32, @@ -3872,7 +3888,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void { } }; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var entries = try std.ArrayList(Entry).initCapacity(gpa, self.phdrs.items.len); defer entries.deinit(); for (0..self.phdrs.items.len) |phndx| { @@ -3977,7 +3993,7 @@ fn sortShdrs(self: *Elf) !void { } }; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var entries = try std.ArrayList(Entry).initCapacity(gpa, self.shdrs.items.len); defer entries.deinit(); for (0..self.shdrs.items.len) |shndx| { @@ -4004,7 +4020,7 @@ fn sortShdrs(self: *Elf) !void { } fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (&[_]*?u16{ &self.eh_frame_section_index, @@ -4187,6 +4203,7 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void { } fn updateSectionSizes(self: *Elf) !void { + const target = self.base.comp.root_mod.resolved_target.result; for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| { const shdr = &self.shdrs.items[shndx]; for (atom_list.items) |atom_index| { @@ -4244,7 +4261,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.interp_section_index) |index| { - self.shdrs.items[index].sh_size = self.base.options.target.dynamic_linker.get().?.len + 1; + self.shdrs.items[index].sh_size = target.dynamic_linker.get().?.len + 1; } if (self.hash_section_index) |index| { @@ -4453,7 +4470,7 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { // as we are more interested in quick turnaround and compatibility // with `findFreeSpace` mechanics than anything else. const Cover = std.ArrayList(u16); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var covers: [max_number_of_object_segments]Cover = undefined; for (&covers) |*cover| { cover.* = Cover.init(gpa); @@ -4691,7 +4708,7 @@ fn allocateAtoms(self: *Elf) void { } fn writeAtoms(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); defer { @@ -4779,7 +4796,7 @@ fn writeAtoms(self: *Elf) !void { } fn writeAtomsObject(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; // TODO iterate over `output_sections` directly for (self.shdrs.items, 0..) |shdr, shndx| { @@ -4852,7 +4869,7 @@ fn updateSymtabSize(self: *Elf) !void { var nglobals: u32 = 0; var strsize: u32 = 0; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 2); @@ -4935,11 +4952,12 @@ fn updateSymtabSize(self: *Elf) !void { } fn writeSyntheticSections(self: *Elf) !void { - const gpa = self.base.allocator; + const target = self.base.comp.root_mod.resolved_target.result; + const gpa = self.base.comp.gpa; if (self.interp_section_index) |shndx| { var buffer: [256]u8 = undefined; - const interp = self.base.options.target.dynamic_linker.get().?; + const interp = target.dynamic_linker.get().?; @memcpy(buffer[0..interp.len], interp); buffer[interp.len] = 0; const contents = buffer[0 .. interp.len + 1]; @@ -5065,7 +5083,7 @@ fn writeSyntheticSections(self: *Elf) !void { } fn writeSyntheticSectionsObject(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (self.output_rela_sections.values()) |sec| { if (sec.atom_list.items.len == 0) continue; @@ -5135,7 +5153,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { } fn writeComdatGroups(self: *Elf) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (self.comdat_group_sections.items) |cgs| { const shdr = self.shdrs.items[cgs.shndx]; const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; @@ -5160,7 +5178,8 @@ fn writeShStrtab(self: *Elf) !void { } fn writeSymtab(self: *Elf) !void { - const gpa = self.base.allocator; + const target = self.base.comp.root_mod.resolved_target.result; + const gpa = self.base.comp.gpa; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; const strtab_shdr = self.shdrs.items[self.strtab_section_index.?]; const sym_size: u64 = switch (self.ptr_width) { @@ -5220,7 +5239,7 @@ fn writeSymtab(self: *Elf) !void { self.plt_got.writeSymtab(self); } - const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); + const foreign_endian = target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { .p32 => { const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len); @@ -5299,7 +5318,8 @@ fn ptrWidthBytes(self: Elf) u8 { /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes /// in a 32-bit ELF file. pub fn archPtrWidthBytes(self: Elf) u8 { - return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8))); + const target = self.base.comp.root_mod.resolved_target.result; + return @intCast(@divExact(target.ptrBitWidth(), 8)); } fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { @@ -5694,7 +5714,7 @@ pub const AddSectionOpts = struct { }; pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const index = @as(u16, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ @@ -5887,7 +5907,7 @@ const GetOrPutGlobalResult = struct { }; pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const name_off = try self.strings.insert(gpa, name); const gop = try self.resolver.getOrPut(gpa, name_off); if (!gop.found_existing) { @@ -5923,7 +5943,7 @@ const GetOrCreateComdatGroupOwnerResult = struct { }; pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const off = try self.strings.insert(gpa, name); const gop = try self.comdat_groups_table.getOrPut(gpa, off); if (!gop.found_existing) { @@ -6039,7 +6059,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 { } fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const max_notes = 4; try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count()); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 61446b3300..f61e78c233 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -143,14 +143,23 @@ tlv_table: TlvSymbolTable = .{}, /// Hot-code swapping state. hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, -pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { - assert(options.target.ofmt == .macho); +darwin_sdk_layout: ?SdkLayout, + +/// The filesystem layout of darwin SDK elements. +pub const SdkLayout = enum { + /// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }. + sdk, + /// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, }. + vendored, +}; - if (options.emit == null) { - return createEmpty(allocator, options); - } +pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { + if (build_options.only_c) unreachable; + const target = options.comp.root_mod.resolved_target.result; + assert(target.ofmt == .macho); - const emit = options.emit.?; + const gpa = options.comp.gpa; + const emit = options.emit; const mode: Mode = mode: { if (options.use_llvm or options.module == null or options.cache_mode == .whole) break :mode .zld; @@ -160,17 +169,16 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { if (options.module == null) { // No point in opening a file, we would not write anything to it. // Initialize with empty. - return createEmpty(allocator, options); + return createEmpty(arena, options); } // Open a temporary object file, not the final output file because we // want to link with LLD. - break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ - emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), + break :blk try std.fmt.allocPrint(arena, "{s}{s}", .{ + emit.sub_path, target.ofmt.fileExt(target.cpu.arch), }); } else emit.sub_path; - errdefer if (mode == .zld) allocator.free(sub_path); - const self = try createEmpty(allocator, options); + const self = try createEmpty(arena, options); errdefer self.base.destroy(); if (mode == .zld) { @@ -186,7 +194,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { .read = true, .mode = link.determineMode(options), }); - errdefer file.close(); self.base.file = file; if (!options.strip and options.module != null) { @@ -194,11 +201,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { log.debug("creating {s}.dSYM bundle", .{sub_path}); const d_sym_path = try std.fmt.allocPrint( - allocator, + arena, "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", .{sub_path}, ); - defer allocator.free(d_sym_path); var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); @@ -209,21 +215,21 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { }); self.d_sym = .{ - .allocator = allocator, - .dwarf = link.File.Dwarf.init(allocator, &self.base, .dwarf32), + .allocator = gpa, + .dwarf = link.File.Dwarf.init(gpa, &self.base, .dwarf32), .file = d_sym_file, }; } // Index 0 is always a null symbol. - try self.locals.append(allocator, .{ + try self.locals.append(gpa, .{ .n_strx = 0, .n_type = 0, .n_sect = 0, .n_desc = 0, .n_value = 0, }); - try self.strtab.buffer.append(allocator, 0); + try self.strtab.buffer.append(gpa, 0); try self.populateMissingMetadata(); @@ -234,15 +240,14 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { return self; } -pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { - const self = try gpa.create(MachO); - errdefer gpa.destroy(self); +pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO { + const self = try arena.create(MachO); self.* = .{ .base = .{ .tag = .macho, - .options = options, - .allocator = gpa, + .comp = options.comp, + .emit = options.emit, .file = null, }, .mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole) @@ -252,7 +257,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { }; if (options.use_llvm and options.module != null) { - self.llvm_object = try LlvmObject.create(gpa, options); + self.llvm_object = try LlvmObject.create(arena, options); } log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); @@ -261,20 +266,15 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { } pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - if (self.base.options.emit == null) { - if (self.llvm_object) |llvm_object| { - try llvm_object.flushModule(comp, prog_node); - } - return; - } + const gpa = self.base.comp.gpa; if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) { if (build_options.have_llvm) { return self.base.linkAsArchive(comp, prog_node); } else { - try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1); + try self.misc_errors.ensureUnusedCapacity(gpa, 1); self.misc_errors.appendAssumeCapacity(.{ - .msg = try self.base.allocator.dupe(u8, "TODO: non-LLVM archiver for MachO object files"), + .msg = try gpa.dupe(u8, "TODO: non-LLVM archiver for MachO object files"), }); return error.FlushFailure; } @@ -294,7 +294,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No return try llvm_object.flushModule(comp, prog_node); } - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + const gpa = self.base.comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -391,7 +392,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (cache_miss) { for (self.dylibs.items) |*dylib| { - dylib.deinit(self.base.allocator); + dylib.deinit(gpa); } self.dylibs.clearRetainingCapacity(); self.dylibs_map.clearRetainingCapacity(); @@ -403,7 +404,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const in_file = try std.fs.cwd().openFile(path, .{}); defer in_file.close(); - var parse_ctx = ParseErrorCtx.init(self.base.allocator); + var parse_ctx = ParseErrorCtx.init(gpa); defer parse_ctx.deinit(); self.parseLibrary( @@ -470,7 +471,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const section = self.sections.get(sym.n_sect - 1).header; const file_offset = section.offset + sym.n_value - section.addr; - var code = std.ArrayList(u8).init(self.base.allocator); + var code = std.ArrayList(u8).init(gpa); defer code.deinit(); try code.resize(math.cast(usize, atom.size) orelse return error.Overflow); @@ -518,12 +519,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No var codesig = CodeSignature.init(getPageSize(self.base.options.target.cpu.arch)); codesig.code_directory.ident = self.base.options.emit.?.sub_path; if (self.base.options.entitlements) |path| { - try codesig.addEntitlements(self.base.allocator, path); + try codesig.addEntitlements(gpa, path); } try self.writeCodeSignaturePadding(&codesig); break :blk codesig; } else null; - defer if (codesig) |*csig| csig.deinit(self.base.allocator); + defer if (codesig) |*csig| csig.deinit(gpa); // Write load commands var lc_buffer = std.ArrayList(u8).init(arena); @@ -555,12 +556,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No }); }, .Lib => if (self.base.options.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer); + try load_commands.writeDylibIdLC(gpa, &self.base.options, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer); + try load_commands.writeRpathLCs(gpa, &self.base.options, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); @@ -644,7 +645,8 @@ pub fn resolveLibSystem( search_dirs: []const []const u8, out_libs: anytype, ) !void { - var tmp_arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + const gpa = self.base.comp.gpa; + var tmp_arena_allocator = std.heap.ArenaAllocator.init(gpa); defer tmp_arena_allocator.deinit(); const tmp_arena = tmp_arena_allocator.allocator(); @@ -775,7 +777,7 @@ fn parseObject( const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mtime: u64 = mtime: { const stat = file.stat() catch break :mtime 0; break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); @@ -868,7 +870,7 @@ pub fn parseFatLibrary( cpu_arch: std.Target.Cpu.Arch, ctx: *ParseErrorCtx, ) ParseError!u64 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const fat_archs = try fat.parseArchs(gpa, file); defer gpa.free(fat_archs); @@ -892,7 +894,7 @@ fn parseArchive( must_link: bool, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; // We take ownership of the file so that we can store it for the duration of symbol resolution. // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF? @@ -973,7 +975,7 @@ fn parseDylib( dylib_options: DylibOpts, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const file_stat = try file.stat(); const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow; @@ -1019,7 +1021,7 @@ fn parseLibStub( dylib_options: DylibOpts, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var lib_stub = try LibStub.loadFromFile(gpa, file); defer lib_stub.deinit(); @@ -1072,7 +1074,7 @@ fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErr } } - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name); if (gop.found_existing) return error.DylibAlreadyExists; @@ -1098,7 +1100,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { // 2) afterwards, we parse dependents of the included dylibs // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; while (dependent_libs.readItem()) |dep_id| { defer dep_id.id.deinit(gpa); @@ -1162,7 +1164,8 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); // Gather relocs which can be resolved. - var relocs = std.ArrayList(*Relocation).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var relocs = std.ArrayList(*Relocation).init(gpa); defer relocs.deinit(); if (self.relocs.getPtr(atom_index)) |rels| { @@ -1237,7 +1240,7 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { fn writeStubHelperPreamble(self: *MachO) !void { if (self.stub_helper_preamble_allocated) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const cpu_arch = self.base.options.target.cpu.arch; const size = stubs.stubHelperPreambleSize(cpu_arch); @@ -1290,7 +1293,7 @@ fn writeStubTableEntry(self: *MachO, index: usize) !void { self.stub_table_count_dirty = false; } - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const stubs_header = self.sections.items(.header)[stubs_sect_id]; const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id]; @@ -1469,7 +1472,7 @@ const CreateAtomOpts = struct { }; pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Index { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const index = @as(Atom.Index, @intCast(self.atoms.items.len)); const atom = try self.atoms.addOne(gpa); atom.* = .{}; @@ -1481,7 +1484,7 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde } pub fn createTentativeDefAtoms(self: *MachO) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (self.globals.items) |global| { const sym = self.getSymbolPtr(global); @@ -1536,7 +1539,8 @@ pub fn createDyldPrivateAtom(self: *MachO) !void { .size = @sizeOf(u64), .alignment = .@"8", }); - try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index); + const gpa = self.base.comp.gpa; + try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); if (self.data_section_index == null) { self.data_section_index = try self.initSection("__DATA", "__data", .{}); @@ -1560,7 +1564,7 @@ pub fn createDyldPrivateAtom(self: *MachO) !void { } fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const size = 3 * @sizeOf(u64); const required_alignment: Alignment = .@"1"; const sym_index = try self.allocateSymbol(); @@ -1595,7 +1599,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { if (self.base.options.output_mode != .Exe) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym_index = try self.allocateSymbol(); const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; const sym = self.getSymbolPtr(sym_loc); @@ -1622,7 +1626,7 @@ pub fn createDsoHandleSymbol(self: *MachO) !void { const global = self.getGlobalPtr("___dso_handle") orelse return; if (!self.getSymbol(global.*).undf()) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym_index = try self.allocateSymbol(); const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; const sym = self.getSymbolPtr(sym_loc); @@ -1686,7 +1690,7 @@ pub fn resolveSymbols(self: *MachO) !void { } fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym = self.getSymbol(current); const sym_name = self.getSymbolName(current); @@ -1800,7 +1804,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void { fn resolveSymbolsInArchives(self: *MachO) !void { if (self.archives.items.len == 0) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var next_sym: usize = 0; loop: while (next_sym < self.unresolved.count()) { const global = self.globals.items[self.unresolved.keys()[next_sym]]; @@ -1829,7 +1833,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void { fn resolveSymbolsInDylibs(self: *MachO) !void { if (self.dylibs.items.len == 0) return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var next_sym: usize = 0; loop: while (next_sym < self.unresolved.count()) { const global_index = self.unresolved.keys()[next_sym]; @@ -1899,6 +1903,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void { } fn resolveBoundarySymbols(self: *MachO) !void { + const gpa = self.base.comp.gpa; var next_sym: usize = 0; while (next_sym < self.unresolved.count()) { const global_index = self.unresolved.keys()[next_sym]; @@ -1909,7 +1914,7 @@ fn resolveBoundarySymbols(self: *MachO) !void { const sym_loc = SymbolWithLoc{ .sym_index = sym_index }; const sym = self.getSymbolPtr(sym_loc); sym.* = .{ - .n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(global.*)), + .n_strx = try self.strtab.insert(gpa, self.getSymbolName(global.*)), .n_type = macho.N_SECT | macho.N_EXT, .n_sect = 0, .n_desc = N_BOUNDARY, @@ -1929,9 +1934,9 @@ fn resolveBoundarySymbols(self: *MachO) !void { } pub fn deinit(self: *MachO) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; - if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + if (self.llvm_object) |llvm_object| llvm_object.deinit(); if (self.d_sym) |*d_sym| { d_sym.deinit(); @@ -2032,7 +2037,7 @@ pub fn deinit(self: *MachO) void { } fn freeAtom(self: *MachO, atom_index: Atom.Index) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; log.debug("freeAtom {d}", .{atom_index}); // Remove any relocs and base relocs associated with this Atom @@ -2124,7 +2129,8 @@ fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: } pub fn allocateSymbol(self: *MachO) !u32 { - try self.locals.ensureUnusedCapacity(self.base.allocator, 1); + const gpa = self.base.comp.gpa; + try self.locals.ensureUnusedCapacity(gpa, 1); const index = blk: { if (self.locals_free_list.popOrNull()) |index| { @@ -2150,7 +2156,8 @@ pub fn allocateSymbol(self: *MachO) !u32 { } fn allocateGlobal(self: *MachO) !u32 { - try self.globals.ensureUnusedCapacity(self.base.allocator, 1); + const gpa = self.base.comp.gpa; + try self.globals.ensureUnusedCapacity(gpa, 1); const index = blk: { if (self.globals_free_list.popOrNull()) |index| { @@ -2171,7 +2178,8 @@ fn allocateGlobal(self: *MachO) !u32 { pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.got_table.lookup.contains(target)) return; - const got_index = try self.got_table.allocateEntry(self.base.allocator, target); + const gpa = self.base.comp.gpa; + const got_index = try self.got_table.allocateEntry(gpa, target); if (self.got_section_index == null) { self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{ .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, @@ -2186,7 +2194,8 @@ pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.stub_table.lookup.contains(target)) return; - const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); + const gpa = self.base.comp.gpa; + const stub_index = try self.stub_table.allocateEntry(gpa, target); if (self.stubs_section_index == null) { self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{ .flags = macho.S_SYMBOL_STUBS | @@ -2212,7 +2221,8 @@ pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.tlv_ptr_table.lookup.contains(target)) return; - _ = try self.tlv_ptr_table.allocateEntry(self.base.allocator, target); + const gpa = self.base.comp.gpa; + _ = try self.tlv_ptr_table.allocateEntry(gpa, target); if (self.tlv_ptr_section_index == null) { self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, @@ -2236,7 +2246,8 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: self.freeUnnamedConsts(decl_index); Atom.freeRelocations(self, atom_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); var decl_state = if (self.d_sym) |*d_sym| @@ -2279,7 +2290,7 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: } pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: InternPool.DeclIndex) !u32 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); if (!gop.found_existing) { @@ -2318,7 +2329,7 @@ fn lowerConst( sect_id: u8, src_loc: Module.SrcLoc, ) !LowerConstResult { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -2366,6 +2377,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.comp.gpa; const decl = mod.declPtr(decl_index); if (decl.val.getExternFunc(mod)) |_| { @@ -2375,8 +2387,8 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) if (decl.isExtern(mod)) { // TODO make this part of getGlobalSymbol const name = mod.intern_pool.stringToSlice(decl.name); - const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); - defer self.base.allocator.free(sym_name); + const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); + defer gpa.free(sym_name); _ = try self.addUndefined(sym_name, .{ .add_got = true }); return; } @@ -2391,7 +2403,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) const sym_index = self.getAtom(atom_index).getSymbolIndex().?; Atom.freeRelocations(self, atom_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| @@ -2449,7 +2461,7 @@ fn updateLazySymbolAtom( atom_index: Atom.Index, section_index: u8, ) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; var required_alignment: Alignment = .none; @@ -2515,7 +2527,8 @@ fn updateLazySymbolAtom( pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index { const mod = self.base.options.module.?; - const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod)); + const gpa = self.base.comp.gpa; + const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { @@ -2529,7 +2542,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In .unused => { const sym_index = try self.allocateSymbol(); metadata.atom.* = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, metadata.atom.*); + try self.atom_by_index_table.putNoClobber(gpa, sym_index, metadata.atom.*); }, .pending_flush => return metadata.atom.*, .flushed => {}, @@ -2556,7 +2569,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPo const init_sym_index = init_atom.getSymbolIndex().?; Atom.freeRelocations(self, init_atom_index); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -2640,11 +2653,12 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPo } pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !Atom.Index { - const gop = try self.decls.getOrPut(self.base.allocator, decl_index); + const gpa = self.base.comp.gpa; + const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const sym_index = try self.allocateSymbol(); const atom_index = try self.createAtom(sym_index, .{}); - try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index); + try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); gop.value_ptr.* = .{ .atom = atom_index, .section = self.getDeclOutputSection(decl_index), @@ -2694,7 +2708,7 @@ fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 { } fn updateDeclCode(self: *MachO, decl_index: InternPool.DeclIndex, code: []u8) !u64 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -2787,7 +2801,7 @@ pub fn updateExports( const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const metadata = switch (exported) { .decl_index => |decl_index| blk: { @@ -2912,7 +2926,7 @@ pub fn deleteDeclExport( if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{mod.intern_pool.stringToSlice(name)}); defer gpa.free(exp_name); @@ -2941,7 +2955,7 @@ pub fn deleteDeclExport( } fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom| { self.freeAtom(atom); @@ -2951,6 +2965,7 @@ fn freeUnnamedConsts(self: *MachO, decl_index: InternPool.DeclIndex) void { pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -2960,7 +2975,7 @@ pub fn freeDecl(self: *MachO, decl_index: InternPool.DeclIndex) void { var kv = const_kv; self.freeAtom(kv.value.atom); self.freeUnnamedConsts(decl_index); - kv.value.exports.deinit(self.base.allocator); + kv.value.exports.deinit(gpa); } if (self.d_sym) |*d_sym| { @@ -2993,7 +3008,7 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const mod = self.base.options.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const decl_alignment = switch (explicit_alignment) { @@ -3060,7 +3075,7 @@ pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: li fn populateMissingMetadata(self: *MachO) !void { assert(self.mode == .incremental); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const cpu_arch = self.base.options.target.cpu.arch; const pagezero_vmsize = self.calcPagezeroSize(); @@ -3228,7 +3243,8 @@ const InitSectionOpts = struct { pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 { log.debug("creating section '{s},{s}'", .{ segname, sectname }); const index = @as(u8, @intCast(self.sections.slice().len)); - try self.sections.append(self.base.allocator, .{ + const gpa = self.base.comp.gpa; + try self.sections.append(gpa, .{ .segment_index = undefined, // Segments will be created automatically later down the pipeline .header = .{ .sectname = makeStaticString(sectname), @@ -3248,7 +3264,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts flags: u32 = macho.S_REGULAR, reserved2: u32 = 0, }) !u8 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const page_size = getPageSize(self.base.options.target.cpu.arch); // In incremental context, we create one section per segment pairing. This way, // we can move the segment in raw file as we please. @@ -3521,7 +3537,7 @@ fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignm pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); defer gpa.free(sym_name); return self.addUndefined(sym_name, .{ .add_stub = true }); @@ -3582,7 +3598,7 @@ pub fn writeLinkeditSegmentData(self: *MachO) !void { } fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const header = self.sections.items(.header)[sect_id]; const segment_index = self.sections.items(.segment_index)[sect_id]; const segment = self.segments.items[segment_index]; @@ -3605,7 +3621,7 @@ fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, } fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const slice = self.sections.slice(); for (self.rebases.keys(), 0..) |atom_index, i| { @@ -3715,7 +3731,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { } fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const header = self.sections.items(.header)[sect_id]; const segment_index = self.sections.items(.segment_index)[sect_id]; const segment = self.segments.items[segment_index]; @@ -3746,7 +3762,7 @@ fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, tab } fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const slice = self.sections.slice(); for (raw_bindings.keys(), 0..) |atom_index, i| { @@ -3885,12 +3901,13 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { fn collectLazyBindData(self: *MachO, bind: anytype) !void { const sect_id = self.la_symbol_ptr_section_index orelse return; + const gpa = self.base.comp.gpa; try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table); - try bind.finalize(self.base.allocator, self); + try bind.finalize(gpa, self); } fn collectExportData(self: *MachO, trie: *Trie) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. log.debug("generating export trie", .{}); @@ -3922,7 +3939,7 @@ fn writeDyldInfoData(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var rebase = Rebase{}; defer rebase.deinit(gpa); @@ -4046,7 +4063,7 @@ fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *s } fn writeFunctionStarts(self: *MachO) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const seg = self.segments.items[self.header_segment_cmd_index.?]; // We need to sort by address first @@ -4133,7 +4150,7 @@ fn filterDataInCode( } pub fn writeDataInCode(self: *MachO) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa); defer out_dice.deinit(); @@ -4211,13 +4228,14 @@ fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip if (sym.ext()) return; // an export lands in its own symtab section, skip if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip + const gpa = self.base.comp.gpa; var out_sym = sym; - out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc)); + out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc)); try locals.append(out_sym); } fn writeSymtab(self: *MachO) !SymtabCtx { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var locals = std.ArrayList(macho.nlist_64).init(gpa); defer locals.deinit(); @@ -4322,7 +4340,7 @@ fn generateSymbolStabs( ) !void { log.debug("generating stabs for '{s}'", .{object.name}); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; var debug_info = object.parseDwarfInfo(); var lookup = DwarfInfo.AbbrevLookupTable.init(gpa); @@ -4450,7 +4468,7 @@ fn generateSymbolStabsForSymbol( lookup: ?DwarfInfo.SubprogramLookupByName, buf: *[4]macho.nlist_64, ) ![]const macho.nlist_64 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const object = self.objects.items[sym_loc.getFile().?]; const sym = self.getSymbol(sym_loc); const sym_name = self.getSymbolName(sym_loc); @@ -4536,7 +4554,7 @@ fn generateSymbolStabsForSymbol( } pub fn writeStrtab(self: *MachO) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const seg = self.getLinkeditSegmentPtr(); const offset = seg.fileoff + seg.filesize; assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64))); @@ -4565,7 +4583,7 @@ const SymtabCtx = struct { }; pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const nstubs = @as(u32, @intCast(self.stub_table.lookup.count())); const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count())); const nindirectsyms = nstubs * 2 + ngot_entries; @@ -4671,7 +4689,8 @@ pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *Cod const seg = self.segments.items[seg_id]; const offset = self.codesig_cmd.dataoff; - var buffer = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); try buffer.ensureTotalCapacityPrecise(code_sig.size()); try code_sig.writeAdhocSignature(comp, .{ @@ -4817,7 +4836,7 @@ pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { } pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const gop = try self.getOrPutGlobalPtr(name); const global_index = self.getGlobalIndex(name).?; @@ -4842,7 +4861,8 @@ pub fn addUndefined(self: *MachO, name: []const u8, flags: RelocFlags) !u32 { } fn updateRelocActions(self: *MachO, global_index: u32, flags: RelocFlags) !void { - const act_gop = try self.actions.getOrPut(self.base.allocator, global_index); + const gpa = self.base.comp.gpa; + const act_gop = try self.actions.getOrPut(gpa, global_index); if (!act_gop.found_existing) { act_gop.value_ptr.* = .{}; } @@ -5022,7 +5042,7 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul if (self.getGlobalPtr(name)) |ptr| { return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; } - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const global_index = try self.allocateGlobal(); const global_name = try gpa.dupe(u8, name); _ = try self.resolver.put(gpa, global_name, global_index); @@ -5171,6 +5191,7 @@ pub fn handleAndReportParseError( err: ParseError, ctx: *const ParseErrorCtx, ) error{OutOfMemory}!void { + const gpa = self.base.comp.gpa; const cpu_arch = self.base.options.target.cpu.arch; switch (err) { error.DylibAlreadyExists => {}, @@ -5188,7 +5209,7 @@ pub fn handleAndReportParseError( }, error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}), error.InvalidTarget, error.InvalidTargetFatLibrary => { - var targets_string = std.ArrayList(u8).init(self.base.allocator); + var targets_string = std.ArrayList(u8).init(gpa); defer targets_string.deinit(); if (ctx.detected_targets.items.len > 1) { @@ -5226,7 +5247,7 @@ fn reportMissingLibraryError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); const notes = try gpa.alloc(File.ErrorMsg, checked_paths.len); errdefer gpa.free(notes); @@ -5246,7 +5267,7 @@ fn reportDependencyError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); defer notes.deinit(); @@ -5266,7 +5287,7 @@ pub fn reportParseError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); var notes = try gpa.alloc(File.ErrorMsg, 1); errdefer gpa.free(notes); @@ -5283,7 +5304,7 @@ pub fn reportUnresolvedBoundarySymbol( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); var notes = try gpa.alloc(File.ErrorMsg, 1); errdefer gpa.free(notes); @@ -5295,7 +5316,7 @@ pub fn reportUnresolvedBoundarySymbol( } pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const count = self.unresolved.count(); try self.misc_errors.ensureUnusedCapacity(gpa, count); @@ -5327,7 +5348,7 @@ fn reportSymbolCollision( first: SymbolWithLoc, other: SymbolWithLoc, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); @@ -5355,7 +5376,7 @@ fn reportSymbolCollision( } fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.misc_errors.ensureUnusedCapacity(gpa, 1); const notes = try gpa.alloc(File.ErrorMsg, 1); diff --git a/src/main.zig b/src/main.zig index 681833857b..5685ddad27 100644 --- a/src/main.zig +++ b/src/main.zig @@ -269,8 +269,6 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } } - defer log_scopes.deinit(gpa); - const cmd = args[1]; const cmd_args = args[2..]; if (mem.eql(u8, cmd, "build-exe")) { @@ -321,7 +319,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } else if (mem.eql(u8, cmd, "init")) { return cmdInit(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { - const host = try std.zig.system.resolveTargetQuery(.{}); + const host = resolveTargetQueryOrFatal(.{}); const stdout = io.getStdOut().writer(); return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host); } else if (mem.eql(u8, cmd, "version")) { @@ -404,37 +402,69 @@ const usage_build_generic = \\ --global-cache-dir [path] Override the global cache directory \\ --zig-lib-dir [path] Override path to Zig installation lib directory \\ - \\Compile Options: + \\Global Compile Options: + \\ --name [name] Compilation unit name (not a file path) + \\ --libc [file] Provide a file which specifies libc paths + \\ -x language Treat subsequent input files as having type + \\ --dep [[import=]name] Add an entry to the next module's import table + \\ --mod [name] [src] Create a module based on the current per-module settings. + \\ The first module is the main module. + \\ "std" can be configured by leaving src blank. + \\ After a --mod argument, per-module settings are reset. + \\ --error-limit [num] Set the maximum amount of distinct error values + \\ -fllvm Force using LLVM as the codegen backend + \\ -fno-llvm Prevent using LLVM as the codegen backend + \\ -flibllvm Force using the LLVM API in the codegen backend + \\ -fno-libllvm Prevent using the LLVM API in the codegen backend + \\ -fclang Force using Clang as the C/C++ compilation backend + \\ -fno-clang Prevent using Clang as the C/C++ compilation backend + \\ -fPIE Force-enable Position Independent Executable + \\ -fno-PIE Force-disable Position Independent Executable + \\ -flto Force-enable Link Time Optimization (requires LLVM extensions) + \\ -fno-lto Force-disable Link Time Optimization + \\ -fdll-export-fns Mark exported functions as DLL exports (Windows) + \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports + \\ -freference-trace[=num] Show num lines of reference trace per compile error + \\ -fno-reference-trace Disable reference trace + \\ -fbuiltin Enable implicit builtin knowledge of functions + \\ -fno-builtin Disable implicit builtin knowledge of functions + \\ -ffunction-sections Places each function in a separate section + \\ -fno-function-sections All functions go into same section + \\ -fdata-sections Places each data in a separate section + \\ -fno-data-sections All data go into same section + \\ -fformatted-panics Enable formatted safety panics + \\ -fno-formatted-panics Disable formatted safety panics + \\ -fstructured-cfg (SPIR-V) force SPIR-V kernels to use structured control flow + \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow + \\ -mexec-model=[value] (WASI) Execution model + \\ + \\Per-Module Compile Options: \\ -target [name] -- see the targets command + \\ -O [mode] Choose what to optimize for + \\ Debug (default) Optimizations off, safety on + \\ ReleaseFast Optimize for performance, safety off + \\ ReleaseSafe Optimize for performance, safety on + \\ ReleaseSmall Optimize for small binary, safety off + \\ -ofmt=[fmt] Override target object format + \\ elf Executable and Linking Format + \\ c C source code + \\ wasm WebAssembly + \\ coff Common Object File Format (Windows) + \\ macho macOS relocatables + \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) + \\ plan9 Plan 9 from Bell Labs object format + \\ hex (planned feature) Intel IHEX + \\ raw (planned feature) Dump machine code directly \\ -mcpu [cpu] Specify target CPU and feature set \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses \\ small|kernel| \\ medium|large] - \\ -x language Treat subsequent input files as having type \\ -mred-zone Force-enable the "red-zone" \\ -mno-red-zone Force-disable the "red-zone" \\ -fomit-frame-pointer Omit the stack frame pointer \\ -fno-omit-frame-pointer Store the stack frame pointer - \\ -mexec-model=[value] (WASI) Execution model - \\ --name [name] Override root name (not a file path) - \\ -O [mode] Choose what to optimize for - \\ Debug (default) Optimizations off, safety on - \\ ReleaseFast Optimize for performance, safety off - \\ ReleaseSafe Optimize for performance, safety on - \\ ReleaseSmall Optimize for small binary, safety off - \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name - \\ deps: [dep],[dep],... - \\ dep: [[import=]name] - \\ --deps [dep],[dep],... Set dependency names for the root package - \\ dep: [[import=]name] - \\ --main-mod-path Set the directory of the root module - \\ --error-limit [num] Set the maximum amount of distinct error values \\ -fPIC Force-enable Position Independent Code \\ -fno-PIC Force-disable Position Independent Code - \\ -fPIE Force-enable Position Independent Executable - \\ -fno-PIE Force-disable Position Independent Executable - \\ -flto Force-enable Link Time Optimization (requires LLVM extensions) - \\ -fno-lto Force-disable Link Time Optimization \\ -fstack-check Enable stack probing in unsafe builds \\ -fno-stack-check Disable stack probing in safe builds \\ -fstack-protector Enable stack protection in unsafe builds @@ -445,47 +475,18 @@ const usage_build_generic = \\ -fno-valgrind Omit valgrind client requests in debug builds \\ -fsanitize-thread Enable Thread Sanitizer \\ -fno-sanitize-thread Disable Thread Sanitizer - \\ -fdll-export-fns Mark exported functions as DLL exports (Windows) - \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports \\ -funwind-tables Always produce unwind table entries for all functions \\ -fno-unwind-tables Never produce unwind table entries - \\ -fllvm Force using LLVM as the codegen backend - \\ -fno-llvm Prevent using LLVM as the codegen backend - \\ -flibllvm Force using the LLVM API in the codegen backend - \\ -fno-libllvm Prevent using the LLVM API in the codegen backend - \\ -fclang Force using Clang as the C/C++ compilation backend - \\ -fno-clang Prevent using Clang as the C/C++ compilation backend - \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error - \\ -fno-reference-trace Disable reference trace \\ -ferror-tracing Enable error tracing in ReleaseFast mode \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode \\ -fsingle-threaded Code assumes there is only one thread \\ -fno-single-threaded Code may not assume there is only one thread - \\ -fbuiltin Enable implicit builtin knowledge of functions - \\ -fno-builtin Disable implicit builtin knowledge of functions - \\ -ffunction-sections Places each function in a separate section - \\ -fno-function-sections All functions go into same section - \\ -fdata-sections Places each data in a separate section - \\ -fno-data-sections All data go into same section \\ -fstrip Omit debug symbols \\ -fno-strip Keep debug symbols - \\ -fformatted-panics Enable formatted safety panics - \\ -fno-formatted-panics Disable formatted safety panics - \\ -ofmt=[mode] Override target object format - \\ elf Executable and Linking Format - \\ c C source code - \\ wasm WebAssembly - \\ coff Common Object File Format (Windows) - \\ macho macOS relocatables - \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) - \\ plan9 Plan 9 from Bell Labs object format - \\ hex (planned feature) Intel IHEX - \\ raw (planned feature) Dump machine code directly \\ -idirafter [dir] Add directory to AFTER include search path \\ -isystem [dir] Add directory to SYSTEM include search path \\ -I[dir] Add directory to include search path \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted) - \\ --libc [file] Provide a file which specifies libc paths \\ -cflags [flags] -- Set extra flags for the next positional C source files \\ -rcflags [flags] -- Set extra flags for the next positional .rc source files \\ -rcincludes=[type] Set the type of includes to use when compiling .rc source files @@ -493,26 +494,8 @@ const usage_build_generic = \\ msvc Use msvc include paths (must be present on the system) \\ gnu Use mingw include paths (distributed with Zig) \\ none Do not use any autodetected include paths - \\ -fstructured-cfg (SPIR-V) force SPIR-V kernels to use structured control flow - \\ -fno-structured-cfg (SPIR-V) force SPIR-V kernels to not use structured control flow \\ - \\Link Options: - \\ -l[lib], --library [lib] Link against system library (only if actually used) - \\ -needed-l[lib], Link against system library (even if unused) - \\ --needed-library [lib] - \\ -weak-l[lib] link against system library marking it and all - \\ -weak_library [lib] referenced symbols as weak - \\ -L[d], --library-directory [d] Add a directory to the library search path - \\ -search_paths_first For each library search path, check for dynamic - \\ lib then static lib before proceeding to next path. - \\ -search_paths_first_static For each library search path, check for static - \\ lib then dynamic lib before proceeding to next path. - \\ -search_dylibs_first Search for dynamic libs in all library search - \\ paths, then static libs. - \\ -search_static_first Search for static libs in all library search - \\ paths, then dynamic libs. - \\ -search_dylibs_only Only search for dynamic libs. - \\ -search_static_only Only search for static libs. + \\Global Link Options: \\ -T[script], --script [script] Use a custom linker script \\ --version-script [path] Provide a version .map file \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) @@ -529,7 +512,6 @@ const usage_build_generic = \\ -fcompiler-rt Always include compiler-rt symbols in output \\ -fno-compiler-rt Prevent including compiler-rt symbols in output \\ -rdynamic Add all symbols to the dynamic symbol table - \\ -rpath [path] Add directory to the runtime library search path \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries @@ -566,11 +548,6 @@ const usage_build_generic = \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image - \\ -framework [name] (Darwin) link against framework - \\ -needed_framework [name] (Darwin) link against framework (even if unused) - \\ -needed_library [lib] link against system library (even if unused) - \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak - \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation @@ -587,6 +564,30 @@ const usage_build_generic = \\ --max-memory=[bytes] (WebAssembly) maximum size of the linear memory \\ --shared-memory (WebAssembly) use shared linear memory \\ --global-base=[addr] (WebAssembly) where to start to place global data + \\ + \\Per-Module Link Options: + \\ -l[lib], --library [lib] Link against system library (only if actually used) + \\ -needed-l[lib], Link against system library (even if unused) + \\ --needed-library [lib] + \\ -weak-l[lib] link against system library marking it and all + \\ -weak_library [lib] referenced symbols as weak + \\ -L[d], --library-directory [d] Add a directory to the library search path + \\ -search_paths_first For each library search path, check for dynamic + \\ lib then static lib before proceeding to next path. + \\ -search_paths_first_static For each library search path, check for static + \\ lib then dynamic lib before proceeding to next path. + \\ -search_dylibs_first Search for dynamic libs in all library search + \\ paths, then static libs. + \\ -search_static_first Search for static libs in all library search + \\ paths, then dynamic libs. + \\ -search_dylibs_only Only search for dynamic libs. + \\ -search_static_only Only search for static libs. + \\ -rpath [path] Add directory to the runtime library search path + \\ -framework [name] (Darwin) link against framework + \\ -needed_framework [name] (Darwin) link against framework (even if unused) + \\ -needed_library [lib] link against system library (even if unused) + \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak + \\ -F[dir] (Darwin) add search path for frameworks \\ --export=[value] (WebAssembly) Force a symbol to be exported \\ \\Test Options: @@ -758,9 +759,24 @@ const Framework = struct { }; const CliModule = struct { - mod: *Package.Module, - /// still in CLI arg format - deps_str: []const u8, + paths: Package.Module.CreateOptions.Paths, + cc_argv: []const []const u8, + inherited: Package.Module.CreateOptions.Inherited, + target_arch_os_abi: ?[]const u8, + target_mcpu: ?[]const u8, + + deps: []const Dep, + resolved: ?*Package.Module, + + c_source_files_start: usize, + c_source_files_end: usize, + rc_source_files_start: usize, + rc_source_files_end: usize, + + pub const Dep = struct { + key: []const u8, + value: []const u8, + }; }; fn buildOutputType( @@ -769,17 +785,12 @@ fn buildOutputType( all_args: []const []const u8, arg_mode: ArgMode, ) !void { - var color: Color = .auto; - var optimize_mode: std.builtin.OptimizeMode = .Debug; var provided_name: ?[]const u8 = null; - var link_mode: ?std.builtin.LinkMode = null; var dll_export_fns: ?bool = null; - var single_threaded: ?bool = null; var root_src_file: ?[]const u8 = null; var version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }; var have_version = false; var compatibility_version: ?std.SemanticVersion = null; - var strip: ?bool = null; var formatted_panics: ?bool = null; var function_sections = false; var data_sections = false; @@ -807,30 +818,11 @@ fn buildOutputType( var emit_docs: Emit = .no; var emit_implib: Emit = .yes_default_path; var emit_implib_arg_provided = false; - var target_arch_os_abi: []const u8 = "native"; + var target_arch_os_abi: ?[]const u8 = null; var target_mcpu: ?[]const u8 = null; - var target_dynamic_linker: ?[]const u8 = null; - var target_ofmt: ?[]const u8 = null; - var output_mode: std.builtin.OutputMode = undefined; var emit_h: Emit = .no; var soname: SOName = undefined; - var ensure_libc_on_non_freestanding = false; - var ensure_libcpp_on_non_freestanding = false; - var link_libc = false; - var link_libcpp = false; - var link_libunwind = false; var want_native_include_dirs = false; - var want_pic: ?bool = null; - var want_pie: ?bool = null; - var want_lto: ?bool = null; - var want_unwind_tables: ?bool = null; - var want_sanitize_c: ?bool = null; - var want_stack_check: ?bool = null; - var want_stack_protector: ?u32 = null; - var want_red_zone: ?bool = null; - var omit_frame_pointer: ?bool = null; - var want_valgrind: ?bool = null; - var want_tsan: ?bool = null; var want_compiler_rt: ?bool = null; var rdynamic: bool = false; var linker_script: ?[]const u8 = null; @@ -841,15 +833,11 @@ fn buildOutputType( var linker_compress_debug_sections: ?link.CompressDebugSections = null; var linker_allow_shlib_undefined: ?bool = null; var linker_bind_global_refs_locally: ?bool = null; - var linker_import_memory: ?bool = null; - var linker_export_memory: ?bool = null; var linker_import_symbols: bool = false; var linker_import_table: bool = false; var linker_export_table: bool = false; - var linker_force_entry: ?bool = null; var linker_initial_memory: ?u64 = null; var linker_max_memory: ?u64 = null; - var linker_shared_memory: bool = false; var linker_global_base: ?u64 = null; var linker_print_gc_sections: bool = false; var linker_print_icf_sections: bool = false; @@ -869,23 +857,16 @@ fn buildOutputType( var linker_dynamicbase = true; var linker_optimization: ?u8 = null; var linker_module_definition_file: ?[]const u8 = null; - var test_evented_io = false; var test_no_exec = false; - var entry: ?[]const u8 = null; var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{}; var stack_size_override: ?u64 = null; var image_base_override: ?u64 = null; - var use_llvm: ?bool = null; - var use_lib_llvm: ?bool = null; - var use_lld: ?bool = null; - var use_clang: ?bool = null; var link_eh_frame_hdr = false; var link_emit_relocs = false; var each_lib_rpath: ?bool = null; var build_id: ?std.zig.BuildId = null; var sysroot: ?[]const u8 = null; var libc_paths_file: ?[]const u8 = try EnvVar.ZIG_LIBC.get(arena); - var machine_code_model: std.builtin.CodeModel = .default; var runtime_args_start: ?usize = null; var test_filter: ?[]const u8 = null; var test_name_prefix: ?[]const u8 = null; @@ -893,12 +874,10 @@ fn buildOutputType( var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena); var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); - var main_mod_path: ?[]const u8 = null; var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var subsystem: ?std.Target.SubSystem = null; var major_subsystem_version: ?u32 = null; var minor_subsystem_version: ?u32 = null; - var wasi_exec_model: ?std.builtin.WasiExecModel = null; var enable_link_snapshots: bool = false; var debug_incremental: bool = false; var install_name: ?[]const u8 = null; @@ -910,63 +889,100 @@ fn buildOutputType( var headerpad_size: ?u32 = null; var headerpad_max_install_names: bool = false; var dead_strip_dylibs: bool = false; + var contains_res_file: bool = false; var reference_trace: ?u32 = null; - var error_tracing: ?bool = null; var pdb_out_path: ?[]const u8 = null; var dwarf_format: ?std.dwarf.Format = null; var error_limit: ?Module.ErrorInt = null; var want_structured_cfg: ?bool = null; - // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. - // This array is populated by zig cc frontend and then has to be converted to zig-style - // CPU features. - var llvm_m_args = std.ArrayList([]const u8).init(arena); - var system_libs = std.StringArrayHashMap(SystemLib).init(arena); - var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(arena); - var clang_argv = std.ArrayList([]const u8).init(arena); - var extra_cflags = std.ArrayList([]const u8).init(arena); - var extra_rcflags = std.ArrayList([]const u8).init(arena); // These are before resolving sysroot. - var lib_dir_args = std.ArrayList([]const u8).init(arena); - var rpath_list = std.ArrayList([]const u8).init(arena); + var lib_dir_args: std.ArrayListUnmanaged([]const u8) = .{}; + var extra_cflags: std.ArrayListUnmanaged([]const u8) = .{}; + var extra_rcflags: std.ArrayListUnmanaged([]const u8) = .{}; var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}; - var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); - var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena); + var rpath_list: std.ArrayListUnmanaged([]const u8) = .{}; var rc_includes: Compilation.RcIncludes = .any; - var res_files = std.ArrayList(Compilation.LinkObject).init(arena); var manifest_file: ?[]const u8 = null; - var link_objects = std.ArrayList(Compilation.LinkObject).init(arena); - var framework_dirs = std.ArrayList([]const u8).init(arena); + var link_objects: std.ArrayListUnmanaged(Compilation.LinkObject) = .{}; + var framework_dirs: std.ArrayListUnmanaged([]const u8) = .{}; var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{}; + var linker_export_symbol_names: std.ArrayListUnmanaged([]const u8) = .{}; + + // Tracks the position in c_source_files which have already their owner populated. + var c_source_files_owner_index: usize = 0; + // Tracks the position in rc_source_files which have already their owner populated. + var rc_source_files_owner_index: usize = 0; + // null means replace with the test executable binary var test_exec_args = std.ArrayList(?[]const u8).init(arena); - var linker_export_symbol_names = std.ArrayList([]const u8).init(arena); + + // These get set by CLI flags and then snapshotted when a `--mod` flag is + // encountered. + var mod_opts: Package.Module.CreateOptions.Inherited = .{}; + + // These get appended to by CLI flags and then slurped when a `--mod` flag + // is encountered. + var cssan: ClangSearchSanitizer = .{}; + var clang_argv: std.ArrayListUnmanaged([]const u8) = .{}; + var deps: std.ArrayListUnmanaged(CliModule.Dep) = .{}; + // Contains every module specified via --mod. The dependencies are added // after argument parsing is completed. We use a StringArrayHashMap to make - // error output consistent. - var modules = std.StringArrayHashMap(CliModule).init(arena); + // error output consistent. "root" is special. + var create_module: CreateModule = .{ + // Populated just before the call to `createModule`. + .global_cache_directory = undefined, + .object_format = null, + .dynamic_linker = null, + .modules = .{}, + .opts = .{ + .is_test = arg_mode == .zig_test, + // Populated while parsing CLI args. + .output_mode = undefined, + // Populated in the call to `createModule` for the root module. + .resolved_target = undefined, + .have_zcu = false, + // Populated just before the call to `createModule`. + .emit_llvm_ir = undefined, + // Populated just before the call to `createModule`. + .emit_llvm_bc = undefined, + // Populated just before the call to `createModule`. + .emit_bin = undefined, + // Populated just before the call to `createModule`. + .c_source_files_len = undefined, + }, + // Populated in the call to `createModule` for the root module. + .resolved_options = undefined, - // The dependency string for the root package - var root_deps_str: ?[]const u8 = null; + .system_libs = .{}, + .external_system_libs = .{}, + .resolved_system_libs = .{}, + .wasi_emulated_libs = .{}, + + .c_source_files = .{}, + .rc_source_files = .{}, + + .llvm_m_args = .{}, + }; // before arg parsing, check for the NO_COLOR environment variable // if it exists, default the color setting to .off // explicit --color arguments will still override this setting. // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 - color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto; + var color: Color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto; switch (arg_mode) { .build, .translate_c, .zig_test, .run => { - var optimize_mode_string: ?[]const u8 = null; switch (arg_mode) { .build => |m| { - output_mode = m; + create_module.opts.output_mode = m; }, .translate_c => { emit_bin = .no; - output_mode = .Obj; + create_module.opts.output_mode = .Obj; }, .zig_test, .run => { - output_mode = .Exe; + create_module.opts.output_mode = .Exe; }, else => unreachable, } @@ -977,9 +993,6 @@ fn buildOutputType( .args = all_args[2..], }; - var cssan = ClangSearchSanitizer.init(gpa, &clang_argv); - defer cssan.map.deinit(); - var file_ext: ?Compilation.FileExt = null; args_loop: while (args_iter.next()) |arg| { if (mem.startsWith(u8, arg, "@")) { @@ -1002,49 +1015,73 @@ fn buildOutputType( } else { fatal("unexpected end-of-parameter mark: --", .{}); } - } else if (mem.eql(u8, arg, "--mod")) { - const info = args_iter.nextOrFatal(); - var info_it = mem.splitScalar(u8, info, ':'); - const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg}); - const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg}); - const root_src_orig = info_it.rest(); - if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg}); - if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig}); - - const root_src = try introspect.resolvePath(arena, root_src_orig); - - for ([_][]const u8{ "std", "root", "builtin" }) |name| { - if (mem.eql(u8, mod_name, name)) { - fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ - mod_name, root_src, + } else if (mem.eql(u8, arg, "--dep")) { + var it = mem.splitScalar(u8, args_iter.nextOrFatal(), '='); + const key = it.next().?; + const value = it.next() orelse key; + if (mem.eql(u8, key, "std") and !mem.eql(u8, value, "std")) { + fatal("unable to import as '{s}': conflicts with builtin module", .{ + key, + }); + } + for ([_][]const u8{ "root", "builtin" }) |name| { + if (mem.eql(u8, key, name)) { + fatal("unable to import as '{s}': conflicts with builtin module", .{ + key, }); } } + try deps.append(arena, .{ + .key = key, + .value = value, + }); + } else if (mem.eql(u8, arg, "--mod")) { + const mod_name = args_iter.nextOrFatal(); + const root_src_orig = args_iter.nextOrFatal(); - if (modules.get(mod_name)) |value| { - fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{ - mod_name, root_src, value.mod.root_src_path, + const gop = try create_module.modules.getOrPut(arena, mod_name); + + if (gop.found_existing) { + fatal("unable to add module '{s}': already exists as '{s}'", .{ + mod_name, gop.value_ptr.paths.root_src_path, }); } - try modules.put(mod_name, .{ - .mod = try Package.Module.create(arena, .{ + // See duplicate logic: ModCreationGlobalFlags + create_module.opts.have_zcu = true; + if (mod_opts.single_threaded == false) + create_module.opts.any_non_single_threaded = true; + if (mod_opts.sanitize_thread == true) + create_module.opts.any_sanitize_thread = true; + if (mod_opts.unwind_tables == true) + create_module.opts.any_unwind_tables = true; + + const root_src = try introspect.resolvePath(arena, root_src_orig); + try create_module.modules.put(arena, mod_name, .{ + .paths = .{ .root = .{ .root_dir = Cache.Directory.cwd(), .sub_path = fs.path.dirname(root_src) orelse "", }, .root_src_path = fs.path.basename(root_src), - .fully_qualified_name = mod_name, - }), - .deps_str = deps_str, + }, + .cc_argv = try clang_argv.toOwnedSlice(arena), + .inherited = mod_opts, + .target_arch_os_abi = target_arch_os_abi, + .target_mcpu = target_mcpu, + .deps = try deps.toOwnedSlice(arena), + .resolved = null, + .c_source_files_start = c_source_files_owner_index, + .c_source_files_end = create_module.c_source_files.items.len, + .rc_source_files_start = rc_source_files_owner_index, + .rc_source_files_end = create_module.rc_source_files.items.len, }); - } else if (mem.eql(u8, arg, "--deps")) { - if (root_deps_str != null) { - fatal("only one --deps argument is allowed", .{}); - } - root_deps_str = args_iter.nextOrFatal(); - } else if (mem.eql(u8, arg, "--main-mod-path")) { - main_mod_path = args_iter.nextOrFatal(); + cssan.reset(); + mod_opts = .{}; + target_arch_os_abi = null; + target_mcpu = null; + c_source_files_owner_index = create_module.c_source_files.items.len; + rc_source_files_owner_index = create_module.rc_source_files.items.len; } else if (mem.eql(u8, arg, "--error-limit")) { const next_arg = args_iter.nextOrFatal(); error_limit = std.fmt.parseUnsigned(Module.ErrorInt, next_arg, 0) catch |err| { @@ -1057,7 +1094,7 @@ fn buildOutputType( fatal("expected -- after -cflags", .{}); }; if (mem.eql(u8, next_arg, "--")) break; - try extra_cflags.append(next_arg); + try extra_cflags.append(arena, next_arg); } } else if (mem.eql(u8, arg, "-rcincludes")) { rc_includes = parseRcIncludes(args_iter.nextOrFatal()); @@ -1070,7 +1107,7 @@ fn buildOutputType( fatal("expected -- after -rcflags", .{}); }; if (mem.eql(u8, next_arg, "--")) break; - try extra_rcflags.append(next_arg); + try extra_rcflags.append(arena, next_arg); } } else if (mem.startsWith(u8, arg, "-fstructured-cfg")) { want_structured_cfg = true; @@ -1086,11 +1123,11 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--subsystem")) { subsystem = try parseSubSystem(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-O")) { - optimize_mode_string = args_iter.nextOrFatal(); + mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal()); } else if (mem.startsWith(u8, arg, "-fentry=")) { - entry = arg["-fentry=".len..]; + create_module.opts.entry = .{ .named = arg["-fentry=".len..] }; } else if (mem.eql(u8, arg, "--force_undefined")) { - try force_undefined_symbols.put(gpa, args_iter.nextOrFatal(), {}); + try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {}); } else if (mem.eql(u8, arg, "--stack")) { const next_arg = args_iter.nextOrFatal(); stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { @@ -1106,17 +1143,17 @@ fn buildOutputType( if (!mem.eql(u8, provided_name.?, fs.path.basename(provided_name.?))) fatal("invalid package name '{s}': cannot contain folder separators", .{provided_name.?}); } else if (mem.eql(u8, arg, "-rpath")) { - try rpath_list.append(args_iter.nextOrFatal()); + try rpath_list.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { - try lib_dir_args.append(args_iter.nextOrFatal()); + try lib_dir_args.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-F")) { - try framework_dirs.append(args_iter.nextOrFatal()); + try framework_dirs.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(gpa, args_iter.nextOrFatal(), .{}); + try frameworks.put(arena, args_iter.nextOrFatal(), .{}); } else if (mem.eql(u8, arg, "-weak_framework")) { - try frameworks.put(gpa, args_iter.nextOrFatal(), .{ .weak = true }); + try frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { - try frameworks.put(gpa, args_iter.nextOrFatal(), .{ .needed = true }); + try frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true }); } else if (mem.eql(u8, arg, "-install_name")) { install_name = args_iter.nextOrFatal(); } else if (mem.startsWith(u8, arg, "--compress-debug-sections=")) { @@ -1168,7 +1205,7 @@ fn buildOutputType( // We don't know whether this library is part of libc // or libc++ until we resolve the target, so we append // to the list for now. - try system_libs.put(args_iter.nextOrFatal(), .{ + try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{ .needed = false, .weak = false, .preferred_mode = lib_preferred_mode, @@ -1179,38 +1216,37 @@ fn buildOutputType( mem.eql(u8, arg, "-needed_library")) { const next_arg = args_iter.nextOrFatal(); - try system_libs.put(next_arg, .{ + try create_module.system_libs.put(arena, next_arg, .{ .needed = true, .weak = false, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) { - try system_libs.put(args_iter.nextOrFatal(), .{ + try create_module.system_libs.put(arena, args_iter.nextOrFatal(), .{ .needed = false, .weak = true, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.eql(u8, arg, "-D")) { - try clang_argv.append(arg); - try clang_argv.append(args_iter.nextOrFatal()); + try clang_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() }); } else if (mem.eql(u8, arg, "-I")) { - try cssan.addIncludePath(.I, arg, args_iter.nextOrFatal(), false); + try cssan.addIncludePath(arena, &clang_argv, .I, arg, args_iter.nextOrFatal(), false); } else if (mem.eql(u8, arg, "-isystem")) { - try cssan.addIncludePath(.isystem, arg, args_iter.nextOrFatal(), false); + try cssan.addIncludePath(arena, &clang_argv, .isystem, arg, args_iter.nextOrFatal(), false); } else if (mem.eql(u8, arg, "-iwithsysroot")) { - try cssan.addIncludePath(.iwithsysroot, arg, args_iter.nextOrFatal(), false); + try cssan.addIncludePath(arena, &clang_argv, .iwithsysroot, arg, args_iter.nextOrFatal(), false); } else if (mem.eql(u8, arg, "-idirafter")) { - try cssan.addIncludePath(.idirafter, arg, args_iter.nextOrFatal(), false); + try cssan.addIncludePath(arena, &clang_argv, .idirafter, arg, args_iter.nextOrFatal(), false); } else if (mem.eql(u8, arg, "-iframework")) { const path = args_iter.nextOrFatal(); - try cssan.addIncludePath(.iframework, arg, path, false); - try framework_dirs.append(path); // Forward to the backend as -F + try cssan.addIncludePath(arena, &clang_argv, .iframework, arg, path, false); + try framework_dirs.append(arena, path); // Forward to the backend as -F } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) { const path = args_iter.nextOrFatal(); - try cssan.addIncludePath(.iframeworkwithsysroot, arg, path, false); - try framework_dirs.append(path); // Forward to the backend as -F + try cssan.addIncludePath(arena, &clang_argv, .iframeworkwithsysroot, arg, path, false); + try framework_dirs.append(arena, path); // Forward to the backend as -F } else if (mem.eql(u8, arg, "--version")) { const next_arg = args_iter.nextOrFatal(); version = std.SemanticVersion.parse(next_arg) catch |err| { @@ -1222,21 +1258,21 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-mcpu")) { target_mcpu = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "-mcmodel")) { - machine_code_model = parseCodeModel(args_iter.nextOrFatal()); + mod_opts.code_model = parseCodeModel(args_iter.nextOrFatal()); + } else if (mem.startsWith(u8, arg, "-mcmodel=")) { + mod_opts.code_model = parseCodeModel(arg["-mcmodel=".len..]); } else if (mem.startsWith(u8, arg, "-ofmt=")) { - target_ofmt = arg["-ofmt=".len..]; + create_module.object_format = arg["-ofmt=".len..]; } else if (mem.startsWith(u8, arg, "-mcpu=")) { target_mcpu = arg["-mcpu=".len..]; - } else if (mem.startsWith(u8, arg, "-mcmodel=")) { - machine_code_model = parseCodeModel(arg["-mcmodel=".len..]); } else if (mem.startsWith(u8, arg, "-O")) { - optimize_mode_string = arg["-O".len..]; + mod_opts.optimize_mode = parseOptimizeMode(arg["-O".len..]); } else if (mem.eql(u8, arg, "--dynamic-linker")) { - target_dynamic_linker = args_iter.nextOrFatal(); + create_module.dynamic_linker = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--sysroot")) { - sysroot = args_iter.nextOrFatal(); - try clang_argv.append("-isysroot"); - try clang_argv.append(sysroot.?); + const next_arg = args_iter.nextOrFatal(); + sysroot = next_arg; + try clang_argv.appendSlice(arena, &.{ "-isysroot", next_arg }); } else if (mem.eql(u8, arg, "--libc")) { libc_paths_file = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--test-filter")) { @@ -1258,7 +1294,7 @@ fn buildOutputType( warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}); _ = args_iter.nextOrFatal(); } else { - try log_scopes.append(gpa, args_iter.nextOrFatal()); + try log_scopes.append(arena, args_iter.nextOrFatal()); } } else if (mem.eql(u8, arg, "--listen")) { const next_arg = args_iter.nextOrFatal(); @@ -1298,7 +1334,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--test-cmd-bin")) { try test_exec_args.append(null); } else if (mem.eql(u8, arg, "--test-evented-io")) { - test_evented_io = true; + create_module.opts.test_evented_io = true; } else if (mem.eql(u8, arg, "--test-no-exec")) { test_no_exec = true; } else if (mem.eql(u8, arg, "-ftime-report")) { @@ -1306,65 +1342,65 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fstack-report")) { stack_report = true; } else if (mem.eql(u8, arg, "-fPIC")) { - want_pic = true; + mod_opts.pic = true; } else if (mem.eql(u8, arg, "-fno-PIC")) { - want_pic = false; + mod_opts.pic = false; } else if (mem.eql(u8, arg, "-fPIE")) { - want_pie = true; + create_module.opts.pie = true; } else if (mem.eql(u8, arg, "-fno-PIE")) { - want_pie = false; + create_module.opts.pie = false; } else if (mem.eql(u8, arg, "-flto")) { - want_lto = true; + create_module.opts.lto = true; } else if (mem.eql(u8, arg, "-fno-lto")) { - want_lto = false; + create_module.opts.lto = false; } else if (mem.eql(u8, arg, "-funwind-tables")) { - want_unwind_tables = true; + mod_opts.unwind_tables = true; } else if (mem.eql(u8, arg, "-fno-unwind-tables")) { - want_unwind_tables = false; + mod_opts.unwind_tables = false; } else if (mem.eql(u8, arg, "-fstack-check")) { - want_stack_check = true; + mod_opts.stack_check = true; } else if (mem.eql(u8, arg, "-fno-stack-check")) { - want_stack_check = false; + mod_opts.stack_check = false; } else if (mem.eql(u8, arg, "-fstack-protector")) { - want_stack_protector = Compilation.default_stack_protector_buffer_size; + mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size; } else if (mem.eql(u8, arg, "-fno-stack-protector")) { - want_stack_protector = 0; + mod_opts.stack_protector = 0; } else if (mem.eql(u8, arg, "-mred-zone")) { - want_red_zone = true; + mod_opts.red_zone = true; } else if (mem.eql(u8, arg, "-mno-red-zone")) { - want_red_zone = false; + mod_opts.red_zone = false; } else if (mem.eql(u8, arg, "-fomit-frame-pointer")) { - omit_frame_pointer = true; + mod_opts.omit_frame_pointer = true; } else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) { - omit_frame_pointer = false; + mod_opts.omit_frame_pointer = false; } else if (mem.eql(u8, arg, "-fsanitize-c")) { - want_sanitize_c = true; + mod_opts.sanitize_c = true; } else if (mem.eql(u8, arg, "-fno-sanitize-c")) { - want_sanitize_c = false; + mod_opts.sanitize_c = false; } else if (mem.eql(u8, arg, "-fvalgrind")) { - want_valgrind = true; + mod_opts.valgrind = true; } else if (mem.eql(u8, arg, "-fno-valgrind")) { - want_valgrind = false; + mod_opts.valgrind = false; } else if (mem.eql(u8, arg, "-fsanitize-thread")) { - want_tsan = true; + mod_opts.sanitize_thread = true; } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) { - want_tsan = false; + mod_opts.sanitize_thread = false; } else if (mem.eql(u8, arg, "-fllvm")) { - use_llvm = true; + create_module.opts.use_llvm = true; } else if (mem.eql(u8, arg, "-fno-llvm")) { - use_llvm = false; + create_module.opts.use_llvm = false; } else if (mem.eql(u8, arg, "-flibllvm")) { - use_lib_llvm = true; + create_module.opts.use_lib_llvm = true; } else if (mem.eql(u8, arg, "-fno-libllvm")) { - use_lib_llvm = false; + create_module.opts.use_lib_llvm = false; } else if (mem.eql(u8, arg, "-flld")) { - use_lld = true; + create_module.opts.use_lld = true; } else if (mem.eql(u8, arg, "-fno-lld")) { - use_lld = false; + create_module.opts.use_lld = false; } else if (mem.eql(u8, arg, "-fclang")) { - use_clang = true; + create_module.opts.use_clang = true; } else if (mem.eql(u8, arg, "-fno-clang")) { - use_clang = false; + create_module.opts.use_clang = false; } else if (mem.eql(u8, arg, "-freference-trace")) { reference_trace = 256; } else if (mem.startsWith(u8, arg, "-freference-trace=")) { @@ -1375,9 +1411,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fno-reference-trace")) { reference_trace = null; } else if (mem.eql(u8, arg, "-ferror-tracing")) { - error_tracing = true; + mod_opts.error_tracing = true; } else if (mem.eql(u8, arg, "-fno-error-tracing")) { - error_tracing = false; + mod_opts.error_tracing = false; } else if (mem.eql(u8, arg, "-rdynamic")) { rdynamic = true; } else if (mem.eql(u8, arg, "-fsoname")) { @@ -1432,11 +1468,11 @@ fn buildOutputType( emit_implib = .no; emit_implib_arg_provided = true; } else if (mem.eql(u8, arg, "-dynamic")) { - link_mode = .Dynamic; + create_module.opts.link_mode = .Dynamic; lib_preferred_mode = .Dynamic; lib_search_strategy = .mode_first; } else if (mem.eql(u8, arg, "-static")) { - link_mode = .Static; + create_module.opts.link_mode = .Static; lib_preferred_mode = .Static; lib_search_strategy = .no_fallback; } else if (mem.eql(u8, arg, "-fdll-export-fns")) { @@ -1447,9 +1483,9 @@ fn buildOutputType( show_builtin = true; emit_bin = .no; } else if (mem.eql(u8, arg, "-fstrip")) { - strip = true; + mod_opts.strip = true; } else if (mem.eql(u8, arg, "-fno-strip")) { - strip = false; + mod_opts.strip = false; } else if (mem.eql(u8, arg, "-gdwarf32")) { dwarf_format = .@"32"; } else if (mem.eql(u8, arg, "-gdwarf64")) { @@ -1459,9 +1495,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fno-formatted-panics")) { formatted_panics = false; } else if (mem.eql(u8, arg, "-fsingle-threaded")) { - single_threaded = true; + mod_opts.single_threaded = true; } else if (mem.eql(u8, arg, "-fno-single-threaded")) { - single_threaded = false; + mod_opts.single_threaded = false; } else if (mem.eql(u8, arg, "-ffunction-sections")) { function_sections = true; } else if (mem.eql(u8, arg, "-fno-function-sections")) { @@ -1518,13 +1554,16 @@ fn buildOutputType( fatal("unsupported linker extension flag: -z {s}", .{z_arg}); } } else if (mem.eql(u8, arg, "--import-memory")) { - linker_import_memory = true; + create_module.opts.import_memory = true; } else if (mem.eql(u8, arg, "-fentry")) { - linker_force_entry = true; + switch (create_module.opts.entry) { + .default, .disabled => create_module.opts.entry = .enabled, + .enabled, .named => {}, + } } else if (mem.eql(u8, arg, "-fno-entry")) { - linker_force_entry = false; + create_module.opts.entry = .disabled; } else if (mem.eql(u8, arg, "--export-memory")) { - linker_export_memory = true; + create_module.opts.export_memory = true; } else if (mem.eql(u8, arg, "--import-symbols")) { linker_import_symbols = true; } else if (mem.eql(u8, arg, "--import-table")) { @@ -1536,11 +1575,11 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "--max-memory=")) { linker_max_memory = parseIntSuffix(arg, "--max-memory=".len); } else if (mem.eql(u8, arg, "--shared-memory")) { - linker_shared_memory = true; + create_module.opts.shared_memory = true; } else if (mem.startsWith(u8, arg, "--global-base=")) { linker_global_base = parseIntSuffix(arg, "--global-base=".len); } else if (mem.startsWith(u8, arg, "--export=")) { - try linker_export_symbol_names.append(arg["--export=".len..]); + try linker_export_symbol_names.append(arena, arg["--export=".len..]); } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--gc-sections")) { @@ -1585,37 +1624,37 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "-T")) { linker_script = arg[2..]; } else if (mem.startsWith(u8, arg, "-L")) { - try lib_dir_args.append(arg[2..]); + try lib_dir_args.append(arena, arg[2..]); } else if (mem.startsWith(u8, arg, "-F")) { - try framework_dirs.append(arg[2..]); + try framework_dirs.append(arena, arg[2..]); } else if (mem.startsWith(u8, arg, "-l")) { // We don't know whether this library is part of libc // or libc++ until we resolve the target, so we append // to the list for now. - try system_libs.put(arg["-l".len..], .{ + try create_module.system_libs.put(arena, arg["-l".len..], .{ .needed = false, .weak = false, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.startsWith(u8, arg, "-needed-l")) { - try system_libs.put(arg["-needed-l".len..], .{ + try create_module.system_libs.put(arena, arg["-needed-l".len..], .{ .needed = true, .weak = false, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.startsWith(u8, arg, "-weak-l")) { - try system_libs.put(arg["-weak-l".len..], .{ + try create_module.system_libs.put(arena, arg["-weak-l".len..], .{ .needed = false, .weak = true, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.startsWith(u8, arg, "-D")) { - try clang_argv.append(arg); + try clang_argv.append(arena, arg); } else if (mem.startsWith(u8, arg, "-I")) { - try cssan.addIncludePath(.I, arg, arg[2..], true); + try cssan.addIncludePath(arena, &clang_argv, .I, arg, arg[2..], true); } else if (mem.eql(u8, arg, "-x")) { const lang = args_iter.nextOrFatal(); if (mem.eql(u8, lang, "none")) { @@ -1626,23 +1665,27 @@ fn buildOutputType( fatal("language not recognized: '{s}'", .{lang}); } } else if (mem.startsWith(u8, arg, "-mexec-model=")) { - wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse { - fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]}); - }; + create_module.opts.wasi_exec_model = parseWasiExecModel(arg["-mexec-model=".len..]); } else { fatal("unrecognized parameter: '{s}'", .{arg}); } - } else switch (file_ext orelse - Compilation.classifyFileExt(arg)) { - .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }), - .res => try res_files.append(.{ .path = arg }), + } else switch (file_ext orelse Compilation.classifyFileExt(arg)) { + .object, .static_library, .shared_library => { + try link_objects.append(arena, .{ .path = arg }); + }, + .res => { + try link_objects.append(arena, .{ .path = arg }); + contains_res_file = true; + }, .manifest => { if (manifest_file) |other| { fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other }); } else manifest_file = arg; }, .assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => { - try c_source_files.append(.{ + try create_module.c_source_files.append(arena, .{ + // Populated after module creation. + .owner = undefined, .src_path = arg, .extra_flags = try arena.dupe([]const u8, extra_cflags.items), // duped when parsing the args. @@ -1650,7 +1693,9 @@ fn buildOutputType( }); }, .rc => { - try rc_source_files.append(.{ + try create_module.rc_source_files.append(arena, .{ + // Populated after module creation. + .owner = undefined, .src_path = arg, .extra_flags = try arena.dupe([]const u8, extra_rcflags.items), }); @@ -1668,18 +1713,14 @@ fn buildOutputType( }, } } - if (optimize_mode_string) |s| { - optimize_mode = std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse - fatal("unrecognized optimization mode: '{s}'", .{s}); - } }, .cc, .cpp => { if (build_options.only_c) unreachable; emit_h = .no; soname = .no; - ensure_libc_on_non_freestanding = true; - ensure_libcpp_on_non_freestanding = arg_mode == .cpp; + create_module.opts.ensure_libc_on_non_freestanding = true; + create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp; want_native_include_dirs = true; // Clang's driver enables this switch unconditionally. // Disabling the emission of .eh_frame_hdr can unexpectedly break @@ -1733,24 +1774,30 @@ fn buildOutputType( } }, .other => { - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); }, - .positional => switch (file_ext orelse - Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) { + .positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) { .assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .m, .mm, .cu => { - try c_source_files.append(.{ + try create_module.c_source_files.append(arena, .{ + // Populated after module creation. + .owner = undefined, .src_path = it.only_arg, .ext = file_ext, // duped while parsing the args. }); }, - .unknown, .shared_library, .object, .static_library => try link_objects.append(.{ - .path = it.only_arg, - .must_link = must_link, - }), - .res => try res_files.append(.{ - .path = it.only_arg, - .must_link = must_link, - }), + .unknown, .shared_library, .object, .static_library => { + try link_objects.append(arena, .{ + .path = it.only_arg, + .must_link = must_link, + }); + }, + .res => { + try link_objects.append(arena, .{ + .path = it.only_arg, + .must_link = must_link, + }); + contains_res_file = true; + }, .manifest => { if (manifest_file) |other| { fatal("only one manifest file can be specified, found '{s}' after previously specified manifest '{s}'", .{ it.only_arg, other }); @@ -1760,7 +1807,11 @@ fn buildOutputType( linker_module_definition_file = it.only_arg; }, .rc => { - try rc_source_files.append(.{ .src_path = it.only_arg }); + try create_module.rc_source_files.append(arena, .{ + // Populated after module creation. + .owner = undefined, + .src_path = it.only_arg, + }); }, .zig => { if (root_src_file) |other| { @@ -1777,13 +1828,13 @@ fn buildOutputType( // more control over what's in the resulting // binary: no extra rpaths and DSO filename exactly // as provided. Hello, Go. - try link_objects.append(.{ + try link_objects.append(arena, .{ .path = it.only_arg, .must_link = must_link, .loption = true, }); } else { - try system_libs.put(it.only_arg, .{ + try create_module.system_libs.put(arena, it.only_arg, .{ .needed = needed, .weak = false, .preferred_mode = lib_preferred_mode, @@ -1796,16 +1847,16 @@ fn buildOutputType( // Never mind what we're doing, just pass the args directly. For example --help. return process.exit(try clangMain(arena, all_args)); }, - .pic => want_pic = true, - .no_pic => want_pic = false, - .pie => want_pie = true, - .no_pie => want_pie = false, - .lto => want_lto = true, - .no_lto => want_lto = false, - .red_zone => want_red_zone = true, - .no_red_zone => want_red_zone = false, - .omit_frame_pointer => omit_frame_pointer = true, - .no_omit_frame_pointer => omit_frame_pointer = false, + .pic => mod_opts.pic = true, + .no_pic => mod_opts.pic = false, + .pie => create_module.opts.pie = true, + .no_pie => create_module.opts.pie = false, + .lto => create_module.opts.lto = true, + .no_lto => create_module.opts.lto = false, + .red_zone => mod_opts.red_zone = true, + .no_red_zone => mod_opts.red_zone = false, + .omit_frame_pointer => mod_opts.omit_frame_pointer = true, + .no_omit_frame_pointer => mod_opts.omit_frame_pointer = false, .function_sections => function_sections = true, .no_function_sections => function_sections = false, .data_sections => data_sections = true, @@ -1814,23 +1865,23 @@ fn buildOutputType( .no_builtin => no_builtin = true, .color_diagnostics => color = .on, .no_color_diagnostics => color = .off, - .stack_check => want_stack_check = true, - .no_stack_check => want_stack_check = false, + .stack_check => mod_opts.stack_check = true, + .no_stack_check => mod_opts.stack_check = false, .stack_protector => { - if (want_stack_protector == null) { - want_stack_protector = Compilation.default_stack_protector_buffer_size; + if (mod_opts.stack_protector == null) { + mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size; } }, - .no_stack_protector => want_stack_protector = 0, - .unwind_tables => want_unwind_tables = true, - .no_unwind_tables => want_unwind_tables = false, + .no_stack_protector => mod_opts.stack_protector = 0, + .unwind_tables => mod_opts.unwind_tables = true, + .no_unwind_tables => mod_opts.unwind_tables = false, .nostdlib => { - ensure_libc_on_non_freestanding = false; - ensure_libcpp_on_non_freestanding = false; + create_module.opts.ensure_libc_on_non_freestanding = false; + create_module.opts.ensure_libcpp_on_non_freestanding = false; }, - .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, + .nostdlib_cpp => create_module.opts.ensure_libcpp_on_non_freestanding = false, .shared => { - link_mode = .Dynamic; + create_module.opts.link_mode = .Dynamic; is_shared_lib = true; }, .rdynamic => rdynamic = true, @@ -1870,7 +1921,7 @@ fn buildOutputType( } else if (mem.eql(u8, linker_arg, "--no-as-needed")) { needed = true; } else if (mem.eql(u8, linker_arg, "-no-pie")) { - want_pie = false; + create_module.opts.pie = false; } else if (mem.eql(u8, linker_arg, "--sort-common")) { // from ld.lld(1): --sort-common is ignored for GNU compatibility, // this ignores plain --sort-common @@ -1912,50 +1963,50 @@ fn buildOutputType( if (mem.eql(u8, level, "s") or mem.eql(u8, level, "z")) { - optimize_mode = .ReleaseSmall; + mod_opts.optimize_mode = .ReleaseSmall; } else if (mem.eql(u8, level, "1") or mem.eql(u8, level, "2") or mem.eql(u8, level, "3") or mem.eql(u8, level, "4") or mem.eql(u8, level, "fast")) { - optimize_mode = .ReleaseFast; + mod_opts.optimize_mode = .ReleaseFast; } else if (mem.eql(u8, level, "g") or mem.eql(u8, level, "0")) { - optimize_mode = .Debug; + mod_opts.optimize_mode = .Debug; } else { - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); } }, .debug => { - strip = false; + mod_opts.strip = false; if (mem.eql(u8, it.only_arg, "g")) { // We handled with strip = false above. } else if (mem.eql(u8, it.only_arg, "g1") or mem.eql(u8, it.only_arg, "gline-tables-only")) { // We handled with strip = false above. but we also want reduced debug info. - try clang_argv.append("-gline-tables-only"); + try clang_argv.append(arena, "-gline-tables-only"); } else { - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); } }, .gdwarf32 => { - strip = false; + mod_opts.strip = false; dwarf_format = .@"32"; }, .gdwarf64 => { - strip = false; + mod_opts.strip = false; dwarf_format = .@"64"; }, .sanitize => { if (mem.eql(u8, it.only_arg, "undefined")) { - want_sanitize_c = true; + mod_opts.sanitize_c = true; } else if (mem.eql(u8, it.only_arg, "thread")) { - want_tsan = true; + mod_opts.sanitize_thread = true; } else { - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); } }, .linker_script => linker_script = it.only_arg, @@ -1964,59 +2015,57 @@ fn buildOutputType( // Have Clang print more infos, some tools such as CMake // parse this to discover any implicit include and // library dir to look-up into. - try clang_argv.append("-v"); + try clang_argv.append(arena, "-v"); }, .dry_run => { // This flag means "dry run". Clang will not actually output anything // to the file system. verbose_link = true; disable_c_depfile = true; - try clang_argv.append("-###"); + try clang_argv.append(arena, "-###"); }, .for_linker => try linker_args.append(it.only_arg), .linker_input_z => { try linker_args.append("-z"); try linker_args.append(it.only_arg); }, - .lib_dir => try lib_dir_args.append(it.only_arg), + .lib_dir => try lib_dir_args.append(arena, it.only_arg), .mcpu => target_mcpu = it.only_arg, - .m => try llvm_m_args.append(it.only_arg), + .m => try create_module.llvm_m_args.append(arena, it.only_arg), .dep_file => { disable_c_depfile = true; - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); }, .dep_file_to_stdout => { // -M, -MM // "Like -MD, but also implies -E and writes to stdout by default" // "Like -MMD, but also implies -E and writes to stdout by default" c_out_mode = .preprocessor; disable_c_depfile = true; - try clang_argv.appendSlice(it.other_args); + try clang_argv.appendSlice(arena, it.other_args); }, - .framework_dir => try framework_dirs.append(it.only_arg), - .framework => try frameworks.put(gpa, it.only_arg, .{}), + .framework_dir => try framework_dirs.append(arena, it.only_arg), + .framework => try frameworks.put(arena, it.only_arg, .{}), .nostdlibinc => want_native_include_dirs = false, - .strip => strip = true, + .strip => mod_opts.strip = true, .exec_model => { - wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse { - fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg}); - }; + create_module.opts.wasi_exec_model = parseWasiExecModel(it.only_arg); }, .sysroot => { sysroot = it.only_arg; }, .entry => { - entry = it.only_arg; + create_module.opts.entry = .{ .named = it.only_arg }; }, .force_undefined_symbol => { - try force_undefined_symbols.put(gpa, it.only_arg, {}); + try force_undefined_symbols.put(arena, it.only_arg, {}); }, - .weak_library => try system_libs.put(it.only_arg, .{ + .weak_library => try create_module.system_libs.put(arena, it.only_arg, .{ .needed = false, .weak = true, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }), - .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }), + .weak_framework => try frameworks.put(arena, it.only_arg, .{ .weak = true }), .headerpad_max_install_names => headerpad_max_install_names = true, .compress_debug_sections => { if (it.only_arg.len == 0) { @@ -2077,14 +2126,14 @@ fn buildOutputType( } provided_name = name[prefix..end]; } else if (mem.eql(u8, arg, "-rpath")) { - try rpath_list.append(linker_args_it.nextOrFatal()); + try rpath_list.append(arena, linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--subsystem")) { subsystem = try parseSubSystem(linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "-I") or mem.eql(u8, arg, "--dynamic-linker") or mem.eql(u8, arg, "-dynamic-linker")) { - target_dynamic_linker = linker_args_it.nextOrFatal(); + create_module.dynamic_linker = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-E") or mem.eql(u8, arg, "--export-dynamic") or mem.eql(u8, arg, "-export-dynamic")) @@ -2145,9 +2194,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--import-memory")) { - linker_import_memory = true; + create_module.opts.import_memory = true; } else if (mem.eql(u8, arg, "--export-memory")) { - linker_export_memory = true; + create_module.opts.export_memory = true; } else if (mem.eql(u8, arg, "--import-symbols")) { linker_import_symbols = true; } else if (mem.eql(u8, arg, "--import-table")) { @@ -2155,7 +2204,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--export-table")) { linker_export_table = true; } else if (mem.eql(u8, arg, "--no-entry")) { - linker_force_entry = false; + create_module.opts.entry = .disabled; } else if (mem.eql(u8, arg, "--initial-memory")) { const next_arg = linker_args_it.nextOrFatal(); linker_initial_memory = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { @@ -2167,14 +2216,14 @@ fn buildOutputType( fatal("unable to parse max memory size '{s}': {s}", .{ next_arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--shared-memory")) { - linker_shared_memory = true; + create_module.opts.shared_memory = true; } else if (mem.eql(u8, arg, "--global-base")) { const next_arg = linker_args_it.nextOrFatal(); linker_global_base = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse global base '{s}': {s}", .{ next_arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--export")) { - try linker_export_symbol_names.append(linker_args_it.nextOrFatal()); + try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--compress-debug-sections")) { const arg1 = linker_args_it.nextOrFatal(); linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse { @@ -2232,9 +2281,9 @@ fn buildOutputType( }; have_version = true; } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) { - entry = linker_args_it.nextOrFatal(); + create_module.opts.entry = .{ .named = linker_args_it.nextOrFatal() }; } else if (mem.eql(u8, arg, "-u")) { - try force_undefined_symbols.put(gpa, linker_args_it.nextOrFatal(), {}); + try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {}); } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) { const stack_size = linker_args_it.nextOrFatal(); stack_size_override = std.fmt.parseUnsigned(u64, stack_size, 0) catch |err| { @@ -2276,7 +2325,7 @@ fn buildOutputType( { // -s, --strip-all Strip all symbols // -S, --strip-debug Strip debugging symbols - strip = true; + mod_opts.strip = true; } else if (mem.eql(u8, arg, "--start-group") or mem.eql(u8, arg, "--end-group")) { @@ -2307,27 +2356,27 @@ fn buildOutputType( fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) }); }; } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{}); + try frameworks.put(arena, linker_args_it.nextOrFatal(), .{}); } else if (mem.eql(u8, arg, "-weak_framework")) { - try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .weak = true }); + try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { - try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{ .needed = true }); + try frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true }); } else if (mem.eql(u8, arg, "-needed_library")) { - try system_libs.put(linker_args_it.nextOrFatal(), .{ + try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{ .weak = false, .needed = true, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.startsWith(u8, arg, "-weak-l")) { - try system_libs.put(arg["-weak-l".len..], .{ + try create_module.system_libs.put(arena, arg["-weak-l".len..], .{ .weak = true, .needed = false, .preferred_mode = lib_preferred_mode, .search_strategy = lib_search_strategy, }); } else if (mem.eql(u8, arg, "-weak_library")) { - try system_libs.put(linker_args_it.nextOrFatal(), .{ + try create_module.system_libs.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true, .needed = false, .preferred_mode = lib_preferred_mode, @@ -2361,7 +2410,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-install_name")) { install_name = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-force_load")) { - try link_objects.append(.{ + try link_objects.append(arena, .{ .path = linker_args_it.nextOrFatal(), .must_link = true, }); @@ -2402,22 +2451,22 @@ fn buildOutputType( } } - if (want_sanitize_c) |wsc| { - if (wsc and optimize_mode == .ReleaseFast) { - optimize_mode = .ReleaseSafe; + if (mod_opts.sanitize_c) |wsc| { + if (wsc and mod_opts.optimize_mode == .ReleaseFast) { + mod_opts.optimize_mode = .ReleaseSafe; } } switch (c_out_mode) { .link => { - output_mode = if (is_shared_lib) .Lib else .Exe; + create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe; emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out; if (emit_llvm) { fatal("-emit-llvm cannot be used when linking", .{}); } }, .object => { - output_mode = .Obj; + create_module.opts.output_mode = .Obj; if (emit_llvm) { emit_bin = .no; if (out_path) |p| { @@ -2434,7 +2483,7 @@ fn buildOutputType( } }, .assembly => { - output_mode = .Obj; + create_module.opts.output_mode = .Obj; emit_bin = .no; if (emit_llvm) { if (out_path) |p| { @@ -2451,9 +2500,9 @@ fn buildOutputType( } }, .preprocessor => { - output_mode = .Obj; + create_module.opts.output_mode = .Obj; // An error message is generated when there is more than 1 C source file. - if (c_source_files.items.len != 1) { + if (create_module.c_source_files.items.len != 1) { // For example `zig cc` and no args should print the "no input files" message. return process.exit(try clangMain(arena, all_args)); } @@ -2465,7 +2514,7 @@ fn buildOutputType( } }, } - if (c_source_files.items.len == 0 and + if (create_module.c_source_files.items.len == 0 and link_objects.items.len == 0 and root_src_file == null) { @@ -2476,258 +2525,72 @@ fn buildOutputType( }, } - { - // Resolve module dependencies - var it = modules.iterator(); - while (it.next()) |kv| { - const deps_str = kv.value_ptr.deps_str; - var deps_it = ModuleDepIterator.init(deps_str); - while (deps_it.next()) |dep| { - if (dep.expose.len == 0) { - fatal("module '{s}' depends on '{s}' with a blank name", .{ - kv.key_ptr.*, dep.name, - }); - } - - for ([_][]const u8{ "std", "root", "builtin" }) |name| { - if (mem.eql(u8, dep.expose, name)) { - fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ - dep.name, dep.expose, - }); - } - } - - const dep_mod = modules.get(dep.name) orelse { - fatal("module '{s}' depends on module '{s}' which does not exist", .{ - kv.key_ptr.*, dep.name, - }); - }; - - try kv.value_ptr.mod.deps.put(arena, dep.expose, dep_mod.mod); - } - } - } - - if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null) - strip = true; - - if (arg_mode == .translate_c and c_source_files.items.len != 1) { - fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len}); + if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) { + fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len}); } if (root_src_file == null and arg_mode == .zig_test) { fatal("`zig test` expects a zig source file argument", .{}); } - const root_name = if (provided_name) |n| n else blk: { - if (arg_mode == .zig_test) { - break :blk "test"; - } else if (root_src_file) |file| { - const basename = fs.path.basename(file); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (c_source_files.items.len >= 1) { - const basename = fs.path.basename(c_source_files.items[0].src_path); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (link_objects.items.len >= 1) { - const basename = fs.path.basename(link_objects.items[0].path); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (emit_bin == .yes) { - const basename = fs.path.basename(emit_bin.yes); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (rc_source_files.items.len >= 1) { - const basename = fs.path.basename(rc_source_files.items[0].src_path); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (res_files.items.len >= 1) { - const basename = fs.path.basename(res_files.items[0].path); - break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; - } else if (show_builtin) { - break :blk "builtin"; - } else if (arg_mode == .run) { - fatal("`zig run` expects at least one positional argument", .{}); - // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented - // is solved, remove the above fatal() and uncomment the `break` below. - //break :blk "run"; - } else { - fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{}); - } - }; - - var target_parse_options: std.Target.Query.ParseOptions = .{ - .arch_os_abi = target_arch_os_abi, - .cpu_features = target_mcpu, - .dynamic_linker = target_dynamic_linker, - .object_format = target_ofmt, - }; - - // Before passing the mcpu string in for parsing, we convert any -m flags that were - // passed in via zig cc to zig-style. - if (llvm_m_args.items.len != 0) { - // If this returns null, we let it fall through to the case below which will - // run the full parse function and do proper error handling. - if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| { - var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa); - defer llvm_to_zig_name.deinit(); - - for (cpu_arch.allFeaturesList()) |feature| { - const llvm_name = feature.llvm_name orelse continue; - try llvm_to_zig_name.put(llvm_name, feature.name); - } - - var mcpu_buffer = std.ArrayList(u8).init(gpa); - defer mcpu_buffer.deinit(); - - try mcpu_buffer.appendSlice(target_mcpu orelse "baseline"); - - for (llvm_m_args.items) |llvm_m_arg| { - if (mem.startsWith(u8, llvm_m_arg, "mno-")) { - const llvm_name = llvm_m_arg["mno-".len..]; - const zig_name = llvm_to_zig_name.get(llvm_name) orelse { - fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ - @tagName(cpu_arch), llvm_name, - }); - }; - try mcpu_buffer.append('-'); - try mcpu_buffer.appendSlice(zig_name); - } else if (mem.startsWith(u8, llvm_m_arg, "m")) { - const llvm_name = llvm_m_arg["m".len..]; - const zig_name = llvm_to_zig_name.get(llvm_name) orelse { - fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ - @tagName(cpu_arch), llvm_name, - }); - }; - try mcpu_buffer.append('+'); - try mcpu_buffer.appendSlice(zig_name); - } else { - unreachable; - } - } - - const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items); - std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu}); - target_parse_options.cpu_features = adjusted_target_mcpu; - } - } - - const target_query = try parseTargetQueryOrReportFatalError(arena, target_parse_options); - const target = try std.zig.system.resolveTargetQuery(target_query); - - if (target.os.tag != .freestanding) { - if (ensure_libc_on_non_freestanding) - link_libc = true; - if (ensure_libcpp_on_non_freestanding) - link_libcpp = true; - } - - if (linker_force_entry) |force| { - if (!force) { - entry = null; - } else if (entry == null and output_mode == .Exe) { - entry = switch (target.ofmt) { - .coff => "wWinMainCRTStartup", - .macho => "_main", - .elf, .plan9 => "_start", - .wasm => defaultWasmEntryName(wasi_exec_model), - else => |tag| fatal("No default entry point available for output format {s}", .{@tagName(tag)}), - }; - } - } else if (entry == null and target.isWasm() and output_mode == .Exe) { - // For WebAssembly the compiler defaults to setting the entry name when no flags are set. - entry = defaultWasmEntryName(wasi_exec_model); - } - - if (target.ofmt == .coff) { - // Now that we know the target supports resources, - // we can add the res files as link objects. - for (res_files.items) |res_file| { - try link_objects.append(res_file); - } - } else { - if (manifest_file != null) { - fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{}); - } - if (rc_source_files.items.len != 0) { - fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{}); - } - if (res_files.items.len != 0) { - fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{}); - } - } - - if (target.cpu.arch.isWasm()) blk: { - if (single_threaded == null) { - single_threaded = true; - } - if (link_mode) |mode| { - if (mode == .Dynamic) { - if (linker_export_memory != null and linker_export_memory.?) { - fatal("flags '-dynamic' and '--export-memory' are incompatible", .{}); - } - // User did not supply `--export-memory` which is incompatible with -dynamic, therefore - // set the flag to false to ensure it does not get enabled by default. - linker_export_memory = false; - } - } - if (wasi_exec_model != null and wasi_exec_model.? == .reactor) { - if (entry) |entry_name| { - if (!mem.eql(u8, "_initialize", entry_name)) { - fatal("the entry symbol of the reactor model must be '_initialize', but found '{s}'", .{entry_name}); - } - } - } - if (linker_shared_memory) { - if (output_mode == .Obj) { - fatal("shared memory is not allowed in object files", .{}); - } - - if (!target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or - !target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.bulk_memory))) - { - fatal("'atomics' and 'bulk-memory' features must be enabled to use shared memory", .{}); - } - break :blk; - } - - // Single-threaded is the default for WebAssembly, so only when the user specified `-fno_single-threaded` - // can they enable multithreaded WebAssembly builds. - const is_single_threaded = single_threaded.?; - if (!is_single_threaded) { - fatal("'-fno-single-threaded' requires the linker feature shared-memory to be enabled using '--shared-memory'", .{}); + if (root_src_file) |unresolved_src_path| { + if (create_module.modules.count() != 0) { + fatal("main module provided both by '--mod {s} {}{s}' and by positional argument '{s}'", .{ + create_module.modules.keys()[0], + create_module.modules.values()[0].paths.root, + create_module.modules.values()[0].paths.root_src_path, + unresolved_src_path, + }); } - } - if (use_lld) |opt| { - if (opt and target.isDarwin()) { - fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{}); - } - } + // See duplicate logic: ModCreationGlobalFlags + create_module.opts.have_zcu = true; + if (mod_opts.single_threaded == false) + create_module.opts.any_non_single_threaded = true; + if (mod_opts.sanitize_thread == true) + create_module.opts.any_sanitize_thread = true; + if (mod_opts.unwind_tables == true) + create_module.opts.any_unwind_tables = true; - if (want_lto) |opt| { - if (opt and target.isDarwin()) { - fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{}); - } + const src_path = try introspect.resolvePath(arena, unresolved_src_path); + try create_module.modules.put(arena, "main", .{ + .paths = .{ + .root = .{ + .root_dir = Cache.Directory.cwd(), + .sub_path = fs.path.dirname(src_path) orelse "", + }, + .root_src_path = fs.path.basename(src_path), + }, + .cc_argv = try clang_argv.toOwnedSlice(arena), + .inherited = mod_opts, + .target_arch_os_abi = target_arch_os_abi, + .target_mcpu = target_mcpu, + .deps = try deps.toOwnedSlice(arena), + .resolved = null, + .c_source_files_start = c_source_files_owner_index, + .c_source_files_end = create_module.c_source_files.items.len, + .rc_source_files_start = rc_source_files_owner_index, + .rc_source_files_end = create_module.rc_source_files.items.len, + }); + cssan.reset(); + mod_opts = .{}; + target_arch_os_abi = null; + target_mcpu = null; + c_source_files_owner_index = create_module.c_source_files.items.len; + rc_source_files_owner_index = create_module.rc_source_files.items.len; } - if (comptime builtin.target.isDarwin()) { - // If we want to link against frameworks, we need system headers. - if (framework_dirs.items.len > 0 or frameworks.count() > 0) - want_native_include_dirs = true; + if (c_source_files_owner_index != create_module.c_source_files.items.len) { + fatal("C source file '{s}' has no parent module", .{ + create_module.c_source_files.items[c_source_files_owner_index].src_path, + }); } - // Resolve the library path arguments with respect to sysroot. - var lib_dirs = std.ArrayList([]const u8).init(arena); - if (sysroot) |root| { - for (lib_dir_args.items) |dir| { - if (fs.path.isAbsolute(dir)) { - const stripped_dir = dir[fs.path.diskDesignator(dir).len..]; - const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir }); - try lib_dirs.append(full_path); - } - try lib_dirs.append(dir); - } - } else { - lib_dirs = lib_dir_args; + if (rc_source_files_owner_index != create_module.rc_source_files.items.len) { + fatal("resource file '{s}' has no parent module", .{ + create_module.rc_source_files.items[rc_source_files_owner_index].src_path, + }); } - lib_dir_args = undefined; // From here we use lib_dirs instead. const self_exe_path: ?[]const u8 = if (!process.can_spawn) null @@ -2757,87 +2620,185 @@ fn buildOutputType( }; defer zig_lib_directory.handle.close(); - // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. - // We need to know whether the set of system libraries contains anything besides these - // to decide whether to trigger native path detection logic. - var external_system_libs: std.MultiArrayList(struct { - name: []const u8, - info: SystemLib, - }) = .{}; + var global_cache_directory: Compilation.Directory = l: { + if (override_global_cache_dir) |p| { + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + } + if (builtin.os.tag == .wasi) { + break :l getWasiPreopen("/cache"); + } + const p = try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); - var resolved_system_libs: std.MultiArrayList(struct { - name: []const u8, - lib: Compilation.SystemLib, - }) = .{}; + create_module.global_cache_directory = global_cache_directory; + create_module.opts.emit_llvm_ir = emit_llvm_ir != .no; + create_module.opts.emit_llvm_bc = emit_llvm_bc != .no; + create_module.opts.emit_bin = emit_bin != .no; + create_module.opts.c_source_files_len = create_module.c_source_files.items.len; - var libc_installation: ?LibCInstallation = null; - if (libc_paths_file) |paths_file| { - libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| { - fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) }); - }; + const main_mod = try createModule(gpa, arena, &create_module, 0, null, zig_lib_directory); + for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| { + if (cli_mod.resolved == null) + fatal("module '{s}' declared but not used", .{key}); } - for (system_libs.keys(), system_libs.values()) |lib_name, info| { - if (target.is_libc_lib_name(lib_name)) { - link_libc = true; - continue; - } - if (target.is_libcpp_lib_name(lib_name)) { - link_libcpp = true; - continue; - } - switch (target_util.classifyCompilerRtLibName(target, lib_name)) { - .none => {}, - .only_libunwind, .both => { - link_libunwind = true; - continue; - }, - .only_compiler_rt => { - warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name}); - continue; + // When you're testing std, the main module is std. In that case, + // we'll just set the std module to the main one, since avoiding + // the errors caused by duplicating it is more effort than it's + // worth. + const main_mod_is_std = m: { + const std_path = try fs.path.resolve(arena, &.{ + zig_lib_directory.path orelse ".", "std", "std.zig", + }); + const main_path = try fs.path.resolve(arena, &.{ + main_mod.root.root_dir.path orelse ".", + main_mod.root.sub_path, + main_mod.root_src_path, + }); + break :m mem.eql(u8, main_path, std_path); + }; + + const std_mod = m: { + if (main_mod_is_std) break :m main_mod; + if (create_module.modules.get("std")) |cli_mod| break :m cli_mod.resolved.?; + + break :m try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = zig_lib_directory, + .sub_path = "std", + }, + .root_src_path = "std.zig", }, - } + .fully_qualified_name = "std", + .cc_argv = &.{}, + .inherited = .{}, + .global = create_module.resolved_options, + .parent = main_mod, + .builtin_mod = main_mod.getBuiltinDependency(), + }); + }; - if (target.isMinGW()) { - const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| { - fatal("failed to check zig installation for DLL import libs: {s}", .{ - @errorName(err), - }); - }; - if (exists) { - try resolved_system_libs.append(arena, .{ - .name = lib_name, - .lib = .{ - .needed = true, - .weak = false, - .path = null, + const root_mod = if (arg_mode == .zig_test) root_mod: { + const test_mod = if (test_runner_path) |test_runner| test_mod: { + const test_mod = try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = Cache.Directory.cwd(), + .sub_path = fs.path.dirname(test_runner) orelse "", }, - }); - continue; - } + .root_src_path = fs.path.basename(test_runner), + }, + .fully_qualified_name = "root", + .cc_argv = &.{}, + .inherited = .{}, + .global = create_module.resolved_options, + .parent = main_mod, + .builtin_mod = main_mod.getBuiltinDependency(), + }); + test_mod.deps = try main_mod.deps.clone(arena); + break :test_mod test_mod; + } else try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = zig_lib_directory, + }, + .root_src_path = "test_runner.zig", + }, + .fully_qualified_name = "root", + .cc_argv = &.{}, + .inherited = .{}, + .global = create_module.resolved_options, + .parent = main_mod, + .builtin_mod = main_mod.getBuiltinDependency(), + }); + + break :root_mod test_mod; + } else main_mod; + + const target = main_mod.resolved_target.result; + + if (target.ofmt != .coff) { + if (manifest_file != null) { + fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{}); + } + if (create_module.rc_source_files.items.len != 0) { + fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{}); } + if (contains_res_file) { + fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{}); + } + } - if (fs.path.isAbsolute(lib_name)) { - fatal("cannot use absolute path as a system library: {s}", .{lib_name}); + const root_name = if (provided_name) |n| n else blk: { + if (arg_mode == .zig_test) { + break :blk "test"; + } else if (root_src_file) |file| { + const basename = fs.path.basename(file); + break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; + } else if (create_module.c_source_files.items.len >= 1) { + const basename = fs.path.basename(create_module.c_source_files.items[0].src_path); + break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; + } else if (link_objects.items.len >= 1) { + const basename = fs.path.basename(link_objects.items[0].path); + break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; + } else if (emit_bin == .yes) { + const basename = fs.path.basename(emit_bin.yes); + break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; + } else if (create_module.rc_source_files.items.len >= 1) { + const basename = fs.path.basename(create_module.rc_source_files.items[0].src_path); + break :blk basename[0 .. basename.len - fs.path.extension(basename).len]; + } else if (show_builtin) { + break :blk "builtin"; + } else if (arg_mode == .run) { + fatal("`zig run` expects at least one positional argument", .{}); + // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented + // is solved, remove the above fatal() and uncomment the `break` below. + //break :blk "run"; + } else { + fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{}); } + }; - if (target.os.tag == .wasi) { - if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { - try wasi_emulated_libs.append(crt_file); - continue; + // Resolve the library path arguments with respect to sysroot. + var lib_dirs: std.ArrayListUnmanaged([]const u8) = .{}; + if (sysroot) |root| { + try lib_dirs.ensureUnusedCapacity(arena, lib_dir_args.items.len * 2); + for (lib_dir_args.items) |dir| { + if (fs.path.isAbsolute(dir)) { + const stripped_dir = dir[fs.path.diskDesignator(dir).len..]; + const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir }); + lib_dirs.appendAssumeCapacity(full_path); } + lib_dirs.appendAssumeCapacity(dir); } + } else { + lib_dirs = lib_dir_args; + } + lib_dir_args = undefined; // From here we use lib_dirs instead. - try external_system_libs.append(arena, .{ - .name = lib_name, - .info = info, - }); + if (main_mod.resolved_target.is_native_os and target.isDarwin()) { + // If we want to link against frameworks, we need system headers. + if (framework_dirs.items.len > 0 or frameworks.count() > 0) + want_native_include_dirs = true; } - // After this point, external_system_libs is used instead of system_libs. // Trigger native system library path detection if necessary. - if (sysroot == null and target_query.isNativeOs() and target_query.isNativeAbi() and - (external_system_libs.len != 0 or want_native_include_dirs)) + if (sysroot == null and + main_mod.resolved_target.is_native_os and + main_mod.resolved_target.is_native_abi and + (create_module.external_system_libs.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena, target) catch |err| { fatal("unable to detect native system paths: {s}", .{@errorName(err)}); @@ -2846,20 +2807,27 @@ fn buildOutputType( warn("{s}", .{warning}); } - try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2); + try clang_argv.ensureUnusedCapacity(arena, paths.include_dirs.items.len * 2); for (paths.include_dirs.items) |include_dir| { clang_argv.appendAssumeCapacity("-isystem"); clang_argv.appendAssumeCapacity(include_dir); } - try framework_dirs.appendSlice(paths.framework_dirs.items); - try lib_dirs.appendSlice(paths.lib_dirs.items); - try rpath_list.appendSlice(paths.rpaths.items); + try framework_dirs.appendSlice(arena, paths.framework_dirs.items); + try lib_dirs.appendSlice(arena, paths.lib_dirs.items); + try rpath_list.appendSlice(arena, paths.rpaths.items); + } + + var libc_installation: ?LibCInstallation = null; + if (libc_paths_file) |paths_file| { + libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| { + fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) }); + }; } if (builtin.target.os.tag == .windows and target.abi == .msvc and - external_system_libs.len != 0) + create_module.external_system_libs.len != 0) { if (libc_installation == null) { libc_installation = try LibCInstallation.findNative(.{ @@ -2868,7 +2836,10 @@ fn buildOutputType( .target = target, }); - try lib_dirs.appendSlice(&.{ libc_installation.?.msvc_lib_dir.?, libc_installation.?.kernel32_lib_dir.? }); + try lib_dirs.appendSlice(arena, &.{ + libc_installation.?.msvc_lib_dir.?, + libc_installation.?.kernel32_lib_dir.?, + }); } } @@ -2888,7 +2859,7 @@ fn buildOutputType( preferred_mode: std.builtin.LinkMode, }).init(arena); - syslib: for (external_system_libs.items(.name), external_system_libs.items(.info)) |lib_name, info| { + syslib: for (create_module.external_system_libs.items(.name), create_module.external_system_libs.items(.info)) |lib_name, info| { // Checked in the first pass above while looking for libc libraries. assert(!fs.path.isAbsolute(lib_name)); @@ -2908,8 +2879,8 @@ fn buildOutputType( )) { const path = try arena.dupe(u8, test_path.items); switch (info.preferred_mode) { - .Static => try link_objects.append(.{ .path = path }), - .Dynamic => try resolved_system_libs.append(arena, .{ + .Static => try link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ .name = lib_name, .lib = .{ .needed = info.needed, @@ -2942,8 +2913,8 @@ fn buildOutputType( )) { const path = try arena.dupe(u8, test_path.items); switch (info.fallbackMode()) { - .Static => try link_objects.append(.{ .path = path }), - .Dynamic => try resolved_system_libs.append(arena, .{ + .Static => try link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ .name = lib_name, .lib = .{ .needed = info.needed, @@ -2976,8 +2947,8 @@ fn buildOutputType( )) { const path = try arena.dupe(u8, test_path.items); switch (info.preferred_mode) { - .Static => try link_objects.append(.{ .path = path }), - .Dynamic => try resolved_system_libs.append(arena, .{ + .Static => try link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ .name = lib_name, .lib = .{ .needed = info.needed, @@ -3000,8 +2971,8 @@ fn buildOutputType( )) { const path = try arena.dupe(u8, test_path.items); switch (info.fallbackMode()) { - .Static => try link_objects.append(.{ .path = path }), - .Dynamic => try resolved_system_libs.append(arena, .{ + .Static => try link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ .name = lib_name, .lib = .{ .needed = info.needed, @@ -3035,7 +3006,8 @@ fn buildOutputType( process.exit(1); } } - // After this point, resolved_system_libs is used instead of external_system_libs. + // After this point, create_module.resolved_system_libs is used instead of + // create_module.external_system_libs. // We now repeat part of the process for frameworks. var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena); @@ -3090,10 +3062,10 @@ fn buildOutputType( } // After this point, resolved_frameworks is used instead of frameworks. - if (output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) { - const total_obj_count = c_source_files.items.len + + if (create_module.opts.output_mode == .Obj and (target.ofmt == .coff or target.ofmt == .macho)) { + const total_obj_count = create_module.c_source_files.items.len + @intFromBool(root_src_file != null) + - rc_source_files.items.len + + create_module.rc_source_files.items.len + link_objects.items.len; if (total_obj_count > 1) { fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)}); @@ -3141,8 +3113,8 @@ fn buildOutputType( .basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, - .output_mode = output_mode, - .link_mode = link_mode, + .output_mode = create_module.opts.output_mode, + .link_mode = create_module.opts.link_mode, .version = optional_version, }), }, @@ -3260,9 +3232,9 @@ fn buildOutputType( }; defer emit_docs_resolved.deinit(); - const is_exe_or_dyn_lib = switch (output_mode) { + const is_exe_or_dyn_lib = switch (create_module.opts.output_mode) { .Obj => false, - .Lib => (link_mode orelse .Static) == .Dynamic, + .Lib => (create_module.opts.link_mode orelse .Static) == .Dynamic, .Exe => true, }; // Note that cmake when targeting Windows will try to execute @@ -3294,76 +3266,10 @@ fn buildOutputType( }; defer emit_implib_resolved.deinit(); - const main_mod: ?*Package.Module = if (root_src_file) |unresolved_src_path| blk: { - const src_path = try introspect.resolvePath(arena, unresolved_src_path); - if (main_mod_path) |unresolved_main_mod_path| { - const p = try introspect.resolvePath(arena, unresolved_main_mod_path); - break :blk try Package.Module.create(arena, .{ - .root = .{ - .root_dir = Cache.Directory.cwd(), - .sub_path = p, - }, - .root_src_path = if (p.len == 0) - src_path - else - try fs.path.relative(arena, p, src_path), - .fully_qualified_name = "root", - }); - } else { - break :blk try Package.Module.create(arena, .{ - .root = .{ - .root_dir = Cache.Directory.cwd(), - .sub_path = fs.path.dirname(src_path) orelse "", - }, - .root_src_path = fs.path.basename(src_path), - .fully_qualified_name = "root", - }); - } - } else null; - - // Transfer packages added with --deps to the root package - if (main_mod) |mod| { - var it = ModuleDepIterator.init(root_deps_str orelse ""); - while (it.next()) |dep| { - if (dep.expose.len == 0) { - fatal("root module depends on '{s}' with a blank name", .{dep.name}); - } - - for ([_][]const u8{ "std", "root", "builtin" }) |name| { - if (mem.eql(u8, dep.expose, name)) { - fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose }); - } - } - - const dep_mod = modules.get(dep.name) orelse - fatal("root module depends on module '{s}' which does not exist", .{dep.name}); - - try mod.deps.put(arena, dep.expose, dep_mod.mod); - } - } - var thread_pool: ThreadPool = undefined; try thread_pool.init(.{ .allocator = gpa }); defer thread_pool.deinit(); - var global_cache_directory: Compilation.Directory = l: { - if (override_global_cache_dir) |p| { - break :l .{ - .handle = try fs.cwd().makeOpenPath(p, .{}), - .path = p, - }; - } - if (builtin.os.tag == .wasi) { - break :l getWasiPreopen("/cache"); - } - const p = try introspect.resolveGlobalCacheDir(arena); - break :l .{ - .handle = try fs.cwd().makeOpenPath(p, .{}), - .path = p, - }; - }; - defer global_cache_directory.handle.close(); - var cleanup_local_cache_dir: ?fs.Dir = null; defer if (cleanup_local_cache_dir) |*dir| dir.close(); @@ -3379,37 +3285,37 @@ fn buildOutputType( if (arg_mode == .run) { break :l global_cache_directory; } - if (main_mod != null) { - // search upwards from cwd until we find directory with build.zig - const cwd_path = try process.getCwdAlloc(arena); - const zig_cache = "zig-cache"; - var dirname: []const u8 = cwd_path; - while (true) { - const joined_path = try fs.path.join(arena, &.{ - dirname, Package.build_zig_basename, - }); - if (fs.cwd().access(joined_path, .{})) |_| { - const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache }); - const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{}); - cleanup_local_cache_dir = dir; - break :l .{ .handle = dir, .path = cache_dir_path }; - } else |err| switch (err) { - error.FileNotFound => { - dirname = fs.path.dirname(dirname) orelse { - break :l global_cache_directory; - }; - continue; - }, - else => break :l global_cache_directory, - } + + // search upwards from cwd until we find directory with build.zig + const cwd_path = try process.getCwdAlloc(arena); + const zig_cache = "zig-cache"; + var dirname: []const u8 = cwd_path; + while (true) { + const joined_path = try fs.path.join(arena, &.{ + dirname, Package.build_zig_basename, + }); + if (fs.cwd().access(joined_path, .{})) |_| { + const cache_dir_path = try fs.path.join(arena, &.{ dirname, zig_cache }); + const dir = try fs.cwd().makeOpenPath(cache_dir_path, .{}); + cleanup_local_cache_dir = dir; + break :l .{ .handle = dir, .path = cache_dir_path }; + } else |err| switch (err) { + error.FileNotFound => { + dirname = fs.path.dirname(dirname) orelse { + break :l global_cache_directory; + }; + continue; + }, + else => break :l global_cache_directory, } } + // Otherwise we really don't have a reasonable place to put the local cache directory, // so we utilize the global one. break :l global_cache_directory; }; - for (c_source_files.items) |*src| { + for (create_module.c_source_files.items) |*src| { if (!mem.eql(u8, src.src_path, "-")) continue; const ext = src.ext orelse @@ -3452,13 +3358,14 @@ fn buildOutputType( .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, + .thread_pool = &thread_pool, + .self_exe_path = self_exe_path, + .config = create_module.resolved_options, .root_name = root_name, - .target = target, - .is_native_os = target_query.isNativeOs(), - .is_native_abi = target_query.isNativeAbi(), .sysroot = sysroot, - .output_mode = output_mode, .main_mod = main_mod, + .root_mod = root_mod, + .std_mod = std_mod, .emit_bin = emit_bin_loc, .emit_h = emit_h_resolved.data, .emit_asm = emit_asm_resolved.data, @@ -3466,43 +3373,22 @@ fn buildOutputType( .emit_llvm_bc = emit_llvm_bc_resolved.data, .emit_docs = emit_docs_resolved.data, .emit_implib = emit_implib_resolved.data, - .link_mode = link_mode, .dll_export_fns = dll_export_fns, - .optimize_mode = optimize_mode, .keep_source_files_loaded = false, - .clang_argv = clang_argv.items, .lib_dirs = lib_dirs.items, .rpath_list = rpath_list.items, .symbol_wrap_set = symbol_wrap_set, - .c_source_files = c_source_files.items, - .rc_source_files = rc_source_files.items, + .c_source_files = create_module.c_source_files.items, + .rc_source_files = create_module.rc_source_files.items, .manifest_file = manifest_file, .rc_includes = rc_includes, .link_objects = link_objects.items, .framework_dirs = framework_dirs.items, .frameworks = resolved_frameworks.items, - .system_lib_names = resolved_system_libs.items(.name), - .system_lib_infos = resolved_system_libs.items(.lib), - .wasi_emulated_libs = wasi_emulated_libs.items, - .link_libc = link_libc, - .link_libcpp = link_libcpp, - .link_libunwind = link_libunwind, - .want_pic = want_pic, - .want_pie = want_pie, - .want_lto = want_lto, - .want_unwind_tables = want_unwind_tables, - .want_sanitize_c = want_sanitize_c, - .want_stack_check = want_stack_check, - .want_stack_protector = want_stack_protector, - .want_red_zone = want_red_zone, - .omit_frame_pointer = omit_frame_pointer, - .want_valgrind = want_valgrind, - .want_tsan = want_tsan, + .system_lib_names = create_module.resolved_system_libs.items(.name), + .system_lib_infos = create_module.resolved_system_libs.items(.lib), + .wasi_emulated_libs = create_module.wasi_emulated_libs.items, .want_compiler_rt = want_compiler_rt, - .use_llvm = use_llvm, - .use_lib_llvm = use_lib_llvm, - .use_lld = use_lld, - .use_clang = use_clang, .hash_style = hash_style, .rdynamic = rdynamic, .linker_script = linker_script, @@ -3513,14 +3399,11 @@ fn buildOutputType( .linker_gc_sections = linker_gc_sections, .linker_allow_shlib_undefined = linker_allow_shlib_undefined, .linker_bind_global_refs_locally = linker_bind_global_refs_locally, - .linker_import_memory = linker_import_memory, - .linker_export_memory = linker_export_memory, .linker_import_symbols = linker_import_symbols, .linker_import_table = linker_import_table, .linker_export_table = linker_export_table, .linker_initial_memory = linker_initial_memory, .linker_max_memory = linker_max_memory, - .linker_shared_memory = linker_shared_memory, .linker_print_gc_sections = linker_print_gc_sections, .linker_print_icf_sections = linker_print_icf_sections, .linker_print_map = linker_print_map, @@ -3546,18 +3429,13 @@ fn buildOutputType( .minor_subsystem_version = minor_subsystem_version, .link_eh_frame_hdr = link_eh_frame_hdr, .link_emit_relocs = link_emit_relocs, - .entry = entry, .force_undefined_symbols = force_undefined_symbols, .stack_size_override = stack_size_override, .image_base_override = image_base_override, - .strip = strip, .formatted_panics = formatted_panics, - .single_threaded = single_threaded, .function_sections = function_sections, .data_sections = data_sections, .no_builtin = no_builtin, - .self_exe_path = self_exe_path, - .thread_pool = &thread_pool, .clang_passthrough_mode = clang_passthrough_mode, .clang_preprocessor_mode = clang_preprocessor_mode, .version = optional_version, @@ -3571,21 +3449,17 @@ fn buildOutputType( .verbose_llvm_bc = verbose_llvm_bc, .verbose_cimport = verbose_cimport, .verbose_llvm_cpu_features = verbose_llvm_cpu_features, - .machine_code_model = machine_code_model, .color = color, .time_report = time_report, .stack_report = stack_report, - .is_test = arg_mode == .zig_test, .each_lib_rpath = each_lib_rpath, .build_id = build_id, - .test_evented_io = test_evented_io, .test_filter = test_filter, .test_name_prefix = test_name_prefix, .test_runner_path = test_runner_path, .disable_lld_caching = !output_to_cache, .subsystem = subsystem, .dwarf_format = dwarf_format, - .wasi_exec_model = wasi_exec_model, .debug_compile_errors = debug_compile_errors, .enable_link_snapshots = enable_link_snapshots, .install_name = install_name, @@ -3595,7 +3469,6 @@ fn buildOutputType( .headerpad_max_install_names = headerpad_max_install_names, .dead_strip_dylibs = dead_strip_dylibs, .reference_trace = reference_trace, - .error_tracing = error_tracing, .pdb_out_path = pdb_out_path, .error_limit = error_limit, .want_structured_cfg = want_structured_cfg, @@ -3702,7 +3575,7 @@ fn buildOutputType( try test_exec_args.appendSlice(&.{ "-I", p }); } - if (link_libc) { + if (create_module.resolved_options.link_libc) { try test_exec_args.append("-lc"); } else if (target.os.tag == .windows) { try test_exec_args.appendSlice(&.{ @@ -3711,14 +3584,15 @@ fn buildOutputType( }); } - if (!mem.eql(u8, target_arch_os_abi, "native")) { + const first_cli_mod = create_module.modules.values()[0]; + if (first_cli_mod.target_arch_os_abi) |triple| { try test_exec_args.append("-target"); - try test_exec_args.append(target_arch_os_abi); + try test_exec_args.append(triple); } - if (target_mcpu) |mcpu| { + if (first_cli_mod.target_mcpu) |mcpu| { try test_exec_args.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu})); } - if (target_dynamic_linker) |dl| { + if (create_module.dynamic_linker) |dl| { try test_exec_args.append("--dynamic-linker"); try test_exec_args.append(dl); } @@ -3742,7 +3616,7 @@ fn buildOutputType( &comp_destroyed, all_args, runtime_args_start, - link_libc, + create_module.resolved_options.link_libc, ); } @@ -3750,6 +3624,243 @@ fn buildOutputType( return cleanExit(); } +const CreateModule = struct { + global_cache_directory: Cache.Directory, + modules: std.StringArrayHashMapUnmanaged(CliModule), + opts: Compilation.Config.Options, + dynamic_linker: ?[]const u8, + object_format: ?[]const u8, + /// undefined until createModule() for the root module is called. + resolved_options: Compilation.Config, + + /// This one is used while collecting CLI options. The set of libs is used + /// directly after computing the target and used to compute link_libc, + /// link_libcpp, and then the libraries are filtered into + /// `external_system_libs` and `resolved_system_libs`. + system_libs: std.StringArrayHashMapUnmanaged(SystemLib), + external_system_libs: std.MultiArrayList(struct { + name: []const u8, + info: SystemLib, + }), + resolved_system_libs: std.MultiArrayList(struct { + name: []const u8, + lib: Compilation.SystemLib, + }), + wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CRTFile), + + c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), + rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), + + // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. + // This array is populated by zig cc frontend and then has to be converted to zig-style + // CPU features. + llvm_m_args: std.ArrayListUnmanaged([]const u8), +}; + +fn createModule( + gpa: Allocator, + arena: Allocator, + create_module: *CreateModule, + index: usize, + parent: ?*Package.Module, + zig_lib_directory: Cache.Directory, +) Allocator.Error!*Package.Module { + const cli_mod = &create_module.modules.values()[index]; + if (cli_mod.resolved) |m| return m; + + const name = create_module.modules.keys()[index]; + + cli_mod.inherited.resolved_target = t: { + // If the target is not overridden, use the parent's target. Of course, + // if this is the root module then we need to proceed to resolve the + // target. + if (cli_mod.target_arch_os_abi == null and + cli_mod.target_mcpu == null and + create_module.dynamic_linker == null and + create_module.object_format == null) + { + if (parent) |p| break :t p.resolved_target; + } + + var target_parse_options: std.Target.Query.ParseOptions = .{ + .arch_os_abi = cli_mod.target_arch_os_abi orelse "native", + .cpu_features = cli_mod.target_mcpu, + .dynamic_linker = create_module.dynamic_linker, + .object_format = create_module.object_format, + }; + + // Before passing the mcpu string in for parsing, we convert any -m flags that were + // passed in via zig cc to zig-style. + if (create_module.llvm_m_args.items.len != 0) { + // If this returns null, we let it fall through to the case below which will + // run the full parse function and do proper error handling. + if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| { + var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa); + defer llvm_to_zig_name.deinit(); + + for (cpu_arch.allFeaturesList()) |feature| { + const llvm_name = feature.llvm_name orelse continue; + try llvm_to_zig_name.put(llvm_name, feature.name); + } + + var mcpu_buffer = std.ArrayList(u8).init(gpa); + defer mcpu_buffer.deinit(); + + try mcpu_buffer.appendSlice(cli_mod.target_mcpu orelse "baseline"); + + for (create_module.llvm_m_args.items) |llvm_m_arg| { + if (mem.startsWith(u8, llvm_m_arg, "mno-")) { + const llvm_name = llvm_m_arg["mno-".len..]; + const zig_name = llvm_to_zig_name.get(llvm_name) orelse { + fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ + @tagName(cpu_arch), llvm_name, + }); + }; + try mcpu_buffer.append('-'); + try mcpu_buffer.appendSlice(zig_name); + } else if (mem.startsWith(u8, llvm_m_arg, "m")) { + const llvm_name = llvm_m_arg["m".len..]; + const zig_name = llvm_to_zig_name.get(llvm_name) orelse { + fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{ + @tagName(cpu_arch), llvm_name, + }); + }; + try mcpu_buffer.append('+'); + try mcpu_buffer.appendSlice(zig_name); + } else { + unreachable; + } + } + + const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items); + std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu}); + target_parse_options.cpu_features = adjusted_target_mcpu; + } + } + + const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options); + const target = resolveTargetQueryOrFatal(target_query); + break :t .{ + .result = target, + .is_native_os = target_query.isNativeOs(), + .is_native_abi = target_query.isNativeAbi(), + }; + }; + + if (parent == null) { + // This block is for initializing the fields of + // `Compilation.Config.Options` that require knowledge of the + // target (which was just now resolved for the root module above). + const resolved_target = cli_mod.inherited.resolved_target.?; + create_module.opts.resolved_target = resolved_target; + create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode; + const target = resolved_target.result; + + // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. + // We need to know whether the set of system libraries contains anything besides these + // to decide whether to trigger native path detection logic. + for (create_module.system_libs.keys(), create_module.system_libs.values()) |lib_name, info| { + if (target.is_libc_lib_name(lib_name)) { + create_module.opts.link_libc = true; + continue; + } + if (target.is_libcpp_lib_name(lib_name)) { + create_module.opts.link_libcpp = true; + continue; + } + switch (target_util.classifyCompilerRtLibName(target, lib_name)) { + .none => {}, + .only_libunwind, .both => { + create_module.opts.link_libunwind = true; + continue; + }, + .only_compiler_rt => { + warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name}); + continue; + }, + } + + if (target.isMinGW()) { + const exists = mingw.libExists(arena, target, zig_lib_directory, lib_name) catch |err| { + fatal("failed to check zig installation for DLL import libs: {s}", .{ + @errorName(err), + }); + }; + if (exists) { + try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = true, + .weak = false, + .path = null, + }, + }); + continue; + } + } + + if (fs.path.isAbsolute(lib_name)) { + fatal("cannot use absolute path as a system library: {s}", .{lib_name}); + } + + if (target.os.tag == .wasi) { + if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { + try create_module.wasi_emulated_libs.append(arena, crt_file); + continue; + } + } + + try create_module.external_system_libs.append(arena, .{ + .name = lib_name, + .info = info, + }); + } + // After this point, external_system_libs is used instead of system_libs. + + create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) { + else => fatal("unable to resolve compilation options: {s}", .{@errorName(err)}), + }; + } + + const mod = Package.Module.create(arena, .{ + .global_cache_directory = create_module.global_cache_directory, + .paths = cli_mod.paths, + .fully_qualified_name = name, + + .cc_argv = cli_mod.cc_argv, + .inherited = cli_mod.inherited, + .global = create_module.resolved_options, + .parent = parent, + .builtin_mod = null, + }) catch |err| switch (err) { + error.ValgrindUnsupportedOnTarget => fatal("unable to create module '{s}': valgrind does not support the selected target CPU architecture", .{name}), + error.TargetRequiresSingleThreaded => fatal("unable to create module '{s}': the selected target does not support multithreading", .{name}), + error.BackendRequiresSingleThreaded => fatal("unable to create module '{s}': the selected machine code backend is limited to single-threaded applications", .{name}), + error.TargetRequiresPic => fatal("unable to create module '{s}': the selected target requires position independent code", .{name}), + error.PieRequiresPic => fatal("unable to create module '{s}': making a Position Independent Executable requires enabling Position Independent Code", .{name}), + error.DynamicLinkingRequiresPic => fatal("unable to create module '{s}': dynamic linking requires enabling Position Independent Code", .{name}), + error.TargetHasNoRedZone => fatal("unable to create module '{s}': the selected target does not have a red zone", .{name}), + error.StackCheckUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack checking", .{name}), + error.StackProtectorUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack protection", .{name}), + error.StackProtectorUnavailableWithoutLibC => fatal("unable to create module '{s}': enabling stack protection requires libc", .{name}), + error.OutOfMemory => return error.OutOfMemory, + }; + cli_mod.resolved = mod; + + for (create_module.c_source_files.items[cli_mod.c_source_files_start..cli_mod.c_source_files_end]) |*item| item.owner = mod; + + for (create_module.rc_source_files.items[cli_mod.rc_source_files_start..cli_mod.rc_source_files_end]) |*item| item.owner = mod; + + for (cli_mod.deps) |dep| { + const dep_index = create_module.modules.getIndex(dep.key) orelse + fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key }); + const dep_mod = try createModule(gpa, arena, create_module, dep_index, mod, zig_lib_directory); + try mod.deps.put(arena, dep.key, dep_mod); + } + + return mod; +} + fn saveState(comp: *Compilation, debug_incremental: bool) void { if (debug_incremental) { comp.saveState() catch |err| { @@ -3984,36 +4095,10 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { } } -const ModuleDepIterator = struct { - split: mem.SplitIterator(u8, .scalar), - - fn init(deps_str: []const u8) ModuleDepIterator { - return .{ .split = mem.splitScalar(u8, deps_str, ',') }; - } - - const Dependency = struct { - expose: []const u8, - name: []const u8, - }; - - fn next(it: *ModuleDepIterator) ?Dependency { - if (it.split.buffer.len == 0) return null; // don't return "" for the first iteration on "" - const str = it.split.next() orelse return null; - if (mem.indexOfScalar(u8, str, '=')) |i| { - return .{ - .expose = str[0..i], - .name = str[i + 1 ..], - }; - } else { - return .{ .expose = str, .name = str }; - } - } -}; - fn parseTargetQueryOrReportFatalError( allocator: Allocator, opts: std.Target.Query.ParseOptions, -) !std.Target.Query { +) std.Target.Query { var opts_with_diags = opts; var diags: std.Target.Query.ParseOptions.Diagnostics = .{}; if (opts_with_diags.diagnostics == null) { @@ -4057,7 +4142,9 @@ fn parseTargetQueryOrReportFatalError( } fatal("unknown object format: '{s}'", .{opts.object_format.?}); }, - else => |e| return e, + else => |e| fatal("unable to parse target query '{s}': {s}", .{ + opts.arch_os_abi, @errorName(e), + }), }; } @@ -4667,7 +4754,7 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: .os_tag = .windows, .abi = .msvc, }; - const target = try std.zig.system.resolveTargetQuery(target_query); + const target = resolveTargetQueryOrFatal(target_query); const is_native_abi = target_query.isNativeAbi(); const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| { if (cur_includes == .any) { @@ -4695,7 +4782,7 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: .os_tag = .windows, .abi = .gnu, }; - const target = try std.zig.system.resolveTargetQuery(target_query); + const target = resolveTargetQueryOrFatal(target_query); const is_native_abi = target_query.isNativeAbi(); const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null); return .{ @@ -4757,10 +4844,10 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { } } - const target_query = try parseTargetQueryOrReportFatalError(gpa, .{ + const target_query = parseTargetQueryOrReportFatalError(gpa, .{ .arch_os_abi = target_arch_os_abi, }); - const target = try std.zig.system.resolveTargetQuery(target_query); + const target = resolveTargetQueryOrFatal(target_query); if (print_includes) { var arena_state = std.heap.ArenaAllocator.init(gpa); @@ -5024,7 +5111,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi if (!build_options.enable_logging) { warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}); } else { - try log_scopes.append(gpa, args[i]); + try log_scopes.append(arena, args[i]); } continue; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { @@ -5115,7 +5202,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi gimmeMoreOfThoseSweetSweetFileDescriptors(); const target_query: std.Target.Query = .{}; - const target = try std.zig.system.resolveTargetQuery(target_query); + const target = resolveTargetQueryOrFatal(target_query); const exe_basename = try std.zig.binNameAlloc(arena, .{ .root_name = "build", @@ -5130,29 +5217,80 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi try thread_pool.init(.{ .allocator = gpa }); defer thread_pool.deinit(); - var main_mod: Package.Module = if (override_build_runner) |build_runner_path| - .{ + const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_build_runner) |runner| .{ + .root = .{ + .root_dir = Cache.Directory.cwd(), + .sub_path = fs.path.dirname(runner) orelse "", + }, + .root_src_path = fs.path.basename(runner), + } else .{ + .root = .{ .root_dir = zig_lib_directory }, + .root_src_path = "build_runner.zig", + }; + + const config = try Compilation.Config.resolve(.{ + .output_mode = .Exe, + .resolved_target = .{ + .result = target, + .is_native_os = true, + .is_native_abi = true, + }, + .have_zcu = true, + .emit_bin = true, + .is_test = false, + }); + + const root_mod = try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = main_mod_paths, + .fully_qualified_name = "root", + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = null, + .builtin_mod = null, + }); + + const builtin_mod = root_mod.getBuiltinDependency(); + const std_mod = try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = .{ .root = .{ - .root_dir = Cache.Directory.cwd(), - .sub_path = fs.path.dirname(build_runner_path) orelse "", + .root_dir = zig_lib_directory, + .sub_path = "std", }, - .root_src_path = fs.path.basename(build_runner_path), - .fully_qualified_name = "root", - } - else - .{ - .root = .{ .root_dir = zig_lib_directory }, - .root_src_path = "build_runner.zig", - .fully_qualified_name = "root", - }; + .root_src_path = "std.zig", + }, + .fully_qualified_name = "std", + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = root_mod, + .builtin_mod = builtin_mod, + }); - var build_mod: Package.Module = .{ - .root = .{ .root_dir = build_root.directory }, - .root_src_path = build_root.build_zig_basename, + const build_mod = try Package.Module.create(arena, .{ + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = .{ .root_dir = build_root.directory }, + .root_src_path = build_root.build_zig_basename, + }, .fully_qualified_name = "root.@build", - }; + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = root_mod, + .builtin_mod = builtin_mod, + }); if (build_options.only_core_functionality) { - try createEmptyDependenciesModule(arena, &main_mod, local_cache_directory); + try createEmptyDependenciesModule( + arena, + root_mod, + global_cache_directory, + local_cache_directory, + builtin_mod, + config, + ); } else { var http_client: std.http.Client = .{ .allocator = gpa }; defer http_client.deinit(); @@ -5196,7 +5334,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .has_build_zig = true, .oom_flag = false, - .module = &build_mod, + .module = build_mod, }; job_queue.all_fetches.appendAssumeCapacity(&fetch); @@ -5225,8 +5363,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi const deps_mod = try createDependenciesModule( arena, source_buf.items, - &main_mod, + root_mod, + global_cache_directory, local_cache_directory, + builtin_mod, + config, ); { @@ -5242,13 +5383,21 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi if (!f.has_build_zig) continue; const m = try Package.Module.create(arena, .{ - .root = try f.package_root.clone(arena), - .root_src_path = Package.build_zig_basename, + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = try f.package_root.clone(arena), + .root_src_path = Package.build_zig_basename, + }, .fully_qualified_name = try std.fmt.allocPrint( arena, "root.@dependencies.{s}", .{&hash}, ), + .cc_argv = &.{}, + .inherited = .{}, + .global = config, + .parent = root_mod, + .builtin_mod = builtin_mod, }); const hash_cloned = try arena.dupe(u8, &hash); deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m); @@ -5276,21 +5425,19 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } } - try main_mod.deps.put(arena, "@build", &build_mod); + try root_mod.deps.put(arena, "@build", build_mod); const comp = Compilation.create(gpa, .{ .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, .root_name = "build", - .target = target, - .is_native_os = target_query.isNativeOs(), - .is_native_abi = target_query.isNativeAbi(), - .output_mode = .Exe, - .main_mod = &main_mod, + .config = config, + .root_mod = root_mod, + .main_mod = build_mod, + .std_mod = std_mod, .emit_bin = emit_bin, .emit_h = null, - .optimize_mode = .Debug, .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .verbose_cc = verbose_cc, @@ -5514,7 +5661,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void .root_decl = .none, }; - file.mod = try Package.Module.create(arena, .{ + file.mod = try Package.Module.createLimited(arena, .{ .root = Package.Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", @@ -5724,7 +5871,7 @@ fn fmtPathFile( .root_decl = .none, }; - file.mod = try Package.Module.create(fmt.arena, .{ + file.mod = try Package.Module.createLimited(fmt.arena, .{ .root = Package.Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", @@ -5804,15 +5951,13 @@ pub fn putAstErrorsIntoBundle( .tree = tree, .tree_loaded = true, .zir = undefined, - .mod = undefined, + .mod = try Package.Module.createLimited(gpa, .{ + .root = Package.Path.cwd(), + .root_src_path = path, + .fully_qualified_name = "root", + }), .root_decl = .none, }; - - file.mod = try Package.Module.create(gpa, .{ - .root = Package.Path.cwd(), - .root_src_path = file.sub_file_path, - .fully_qualified_name = "root", - }); defer gpa.destroy(file.mod); file.zir = try AstGen.generate(gpa, file.tree); @@ -6373,7 +6518,7 @@ pub fn cmdAstCheck( file.stat.size = source.len; } - file.mod = try Package.Module.create(arena, .{ + file.mod = try Package.Module.createLimited(arena, .{ .root = Package.Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", @@ -6546,7 +6691,7 @@ pub fn cmdChangelist( .root_decl = .none, }; - file.mod = try Package.Module.create(arena, .{ + file.mod = try Package.Module.createLimited(arena, .{ .root = Package.Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", @@ -6669,7 +6814,7 @@ fn warnAboutForeignBinaries( link_libc: bool, ) !void { const host_query: std.Target.Query = .{}; - const host_target = try std.zig.system.resolveTargetQuery(host_query); + const host_target = resolveTargetQueryOrFatal(host_query); switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) { .native => return, @@ -6809,18 +6954,22 @@ fn parseSubSystem(next_arg: []const u8) !std.Target.SubSystem { /// Silently ignore superfluous search dirs. /// Warn when a dir is added to multiple searchlists. const ClangSearchSanitizer = struct { - argv: *std.ArrayList([]const u8), - map: std.StringHashMap(Membership), + map: std.StringHashMapUnmanaged(Membership) = .{}, - fn init(gpa: Allocator, argv: *std.ArrayList([]const u8)) @This() { - return .{ - .argv = argv, - .map = std.StringHashMap(Membership).init(gpa), - }; + fn reset(self: *@This()) void { + self.map.clearRetainingCapacity(); } - fn addIncludePath(self: *@This(), group: Group, arg: []const u8, dir: []const u8, joined: bool) !void { - const gopr = try self.map.getOrPut(dir); + fn addIncludePath( + self: *@This(), + ally: Allocator, + argv: *std.ArrayListUnmanaged([]const u8), + group: Group, + arg: []const u8, + dir: []const u8, + joined: bool, + ) !void { + const gopr = try self.map.getOrPut(ally, dir); const m = gopr.value_ptr; if (!gopr.found_existing) { // init empty membership @@ -6867,8 +7016,9 @@ const ClangSearchSanitizer = struct { if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" }); }, } - try self.argv.append(arg); - if (!joined) try self.argv.append(dir); + try argv.ensureUnusedCapacity(ally, 2); + argv.appendAssumeCapacity(arg); + if (!joined) argv.appendAssumeCapacity(dir); } const Group = enum { I, isystem, iwithsysroot, idirafter, iframework, iframeworkwithsysroot }; @@ -7244,11 +7394,22 @@ fn cmdFetch( fn createEmptyDependenciesModule( arena: Allocator, main_mod: *Package.Module, + global_cache_directory: Cache.Directory, local_cache_directory: Cache.Directory, + builtin_mod: *Package.Module, + global_options: Compilation.Config, ) !void { var source = std.ArrayList(u8).init(arena); try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source); - _ = try createDependenciesModule(arena, source.items, main_mod, local_cache_directory); + _ = try createDependenciesModule( + arena, + source.items, + main_mod, + global_cache_directory, + local_cache_directory, + builtin_mod, + global_options, + ); } /// Creates the dependencies.zig file and corresponding `Package.Module` for the @@ -7257,7 +7418,10 @@ fn createDependenciesModule( arena: Allocator, source: []const u8, main_mod: *Package.Module, + global_cache_directory: Cache.Directory, local_cache_directory: Cache.Directory, + builtin_mod: *Package.Module, + global_options: Compilation.Config, ) !*Package.Module { // Atomically create the file in a directory named after the hash of its contents. const basename = "dependencies.zig"; @@ -7283,25 +7447,25 @@ fn createDependenciesModule( ); const deps_mod = try Package.Module.create(arena, .{ - .root = .{ - .root_dir = local_cache_directory, - .sub_path = o_dir_sub_path, + .global_cache_directory = global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = local_cache_directory, + .sub_path = o_dir_sub_path, + }, + .root_src_path = basename, }, - .root_src_path = basename, .fully_qualified_name = "root.@dependencies", + .parent = main_mod, + .builtin_mod = builtin_mod, + .cc_argv = &.{}, + .inherited = .{}, + .global = global_options, }); try main_mod.deps.put(arena, "@dependencies", deps_mod); return deps_mod; } -fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 { - const model = exec_model orelse .command; - if (model == .reactor) { - return "_initialize"; - } - return "_start"; -} - const BuildRoot = struct { directory: Cache.Directory, build_zig_basename: []const u8, @@ -7509,3 +7673,18 @@ fn findTemplates(gpa: Allocator, arena: Allocator) Templates { .buffer = std.ArrayList(u8).init(gpa), }; } + +fn parseOptimizeMode(s: []const u8) std.builtin.OptimizeMode { + return std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse + fatal("unrecognized optimization mode: '{s}'", .{s}); +} + +fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel { + return std.meta.stringToEnum(std.builtin.WasiExecModel, s) orelse + fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s}); +} + +fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target { + return std.zig.system.resolveTargetQuery(target_query) catch |err| + fatal("unable to resolve target: {s}", .{@errorName(err)}); +} diff --git a/src/target.zig b/src/target.zig index 624a67d043..f610e55fb0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -3,6 +3,8 @@ const Type = @import("type.zig").Type; const AddressSpace = std.builtin.AddressSpace; const Alignment = @import("InternPool.zig").Alignment; +pub const default_stack_protector_buffer_size = 4; + pub const ArchOsAbi = struct { arch: std.Target.Cpu.Arch, os: std.Target.Os.Tag, @@ -204,11 +206,18 @@ pub fn supports_fpic(target: std.Target) bool { return target.os.tag != .windows and target.os.tag != .uefi; } -pub fn isSingleThreaded(target: std.Target) bool { +pub fn alwaysSingleThreaded(target: std.Target) bool { _ = target; return false; } +pub fn defaultSingleThreaded(target: std.Target) bool { + return switch (target.cpu.arch) { + .wasm32, .wasm64 => true, + else => false, + }; +} + /// Valgrind supports more, but Zig does not support them yet. pub fn hasValgrindSupport(target: std.Target) bool { switch (target.cpu.arch) { @@ -375,12 +384,17 @@ pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerR } pub fn hasDebugInfo(target: std.Target) bool { - if (target.cpu.arch.isNvptx()) { - // TODO: not sure how to test "ptx >= 7.5" with featureset - return std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75); - } - - return true; + return switch (target.cpu.arch) { + .nvptx, .nvptx64 => std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75) or + std.Target.nvptx.featureSetHas(target.cpu.features, .ptx76) or + std.Target.nvptx.featureSetHas(target.cpu.features, .ptx77) or + std.Target.nvptx.featureSetHas(target.cpu.features, .ptx78) or + std.Target.nvptx.featureSetHas(target.cpu.features, .ptx80) or + std.Target.nvptx.featureSetHas(target.cpu.features, .ptx81), + .wasm32, .wasm64 => false, + .bpfel, .bpfeb => false, + else => true, + }; } pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.OptimizeMode { @@ -619,3 +633,36 @@ pub fn fnCallConvAllowsZigTypes(target: std.Target, cc: std.builtin.CallingConve else => false, }; } + +pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend { + if (use_llvm) return .stage2_llvm; + if (target.ofmt == .c) return .stage2_c; + return switch (target.cpu.arch) { + .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, + .arm, .armeb, .thumb, .thumbeb => .stage2_arm, + .x86_64 => .stage2_x86_64, + .x86 => .stage2_x86, + .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, + .riscv64 => .stage2_riscv64, + .sparc64 => .stage2_sparc64, + .spirv64 => .stage2_spirv64, + else => .other, + }; +} + +pub fn defaultEntrySymbolName( + target: std.Target, + /// May be `undefined` when `target` is not WASI. + wasi_exec_model: std.builtin.WasiExecModel, +) ?[]const u8 { + return switch (target.ofmt) { + .coff => "wWinMainCRTStartup", + .macho => "_main", + .elf, .plan9 => "_start", + .wasm => switch (wasi_exec_model) { + .reactor => "_initialize", + .command => "_start", + }, + else => null, + }; +} -- cgit v1.2.3 From 0789e91eeb08495c5084067df2b2dab410197364 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Dec 2023 16:25:28 -0700 Subject: linkers: update references to "options" field --- src/Compilation.zig | 91 ++++++----- src/Compilation/Config.zig | 2 +- src/Sema.zig | 27 ++-- src/codegen/llvm.zig | 4 +- src/libtsan.zig | 2 +- src/link.zig | 3 +- src/link/Coff/lld.zig | 94 ++++++------ src/link/Dwarf.zig | 10 +- src/link/Elf.zig | 295 ++++++++++++++++++------------------ src/link/Elf/Atom.zig | 13 +- src/link/Elf/Object.zig | 2 +- src/link/Elf/ZigObject.zig | 2 +- src/link/Elf/synthetic_sections.zig | 24 +-- src/link/MachO.zig | 36 +++-- src/link/MachO/DebugSymbols.zig | 7 +- src/link/MachO/dead_strip.zig | 2 +- src/link/MachO/load_commands.zig | 34 ++--- src/link/MachO/zld.zig | 9 +- src/link/NvPtx.zig | 3 +- src/link/Plan9.zig | 32 ++-- src/link/SpirV.zig | 3 +- src/link/Wasm.zig | 245 ++++++++++++++++++------------ 22 files changed, 511 insertions(+), 429 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index 2859090edc..cedbf5024f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -76,6 +76,7 @@ framework_dirs: []const []const u8, system_libs: std.StringArrayHashMapUnmanaged(SystemLib), version: ?std.SemanticVersion, libc_installation: ?*const LibCInstallation, +skip_linker_dependencies: bool, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = @@ -940,7 +941,7 @@ pub const InitOptions = struct { /// * getpid /// * mman /// * signal - wasi_emulated_libs: []const wasi_libc.CRTFile = &[0]wasi_libc.CRTFile{}, + wasi_emulated_libs: []const wasi_libc.CRTFile = &.{}, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. @@ -1238,7 +1239,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const sysroot = options.sysroot orelse libc_dirs.sysroot; const include_compiler_rt = options.want_compiler_rt orelse - (!options.skip_linker_dependencies and is_exe_or_dyn_lib); + (!comp.skip_linker_dependencies and is_exe_or_dyn_lib); if (include_compiler_rt and output_mode == .Obj) { // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");` @@ -1639,6 +1640,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .objects = options.link_objects, .framework_dirs = options.framework_dirs, .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit, + .skip_linker_dependencies = options.skip_linker_dependencies, }; if (bin_file_emit) |emit| { @@ -1695,7 +1697,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .soname = options.soname, .compatibility_version = options.compatibility_version, .dll_export_fns = dll_export_fns, - .skip_linker_dependencies = options.skip_linker_dependencies, .parent_compilation_link_libc = options.parent_compilation_link_libc, .each_lib_rpath = each_lib_rpath, .build_id = build_id, @@ -1765,9 +1766,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; + const have_bin_emit = comp.bin_file != null or comp.whole_bin_sub_path != null; - if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and target.ofmt != .c) { + if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) { if (target.isDarwin()) { switch (target.abi) { .none, @@ -1808,27 +1809,34 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .{ .musl_crt_file = .crt1_o }, .{ .musl_crt_file = .scrt1_o }, .{ .musl_crt_file = .rcrt1_o }, - switch (comp.bin_file.options.link_mode) { + switch (comp.config.link_mode) { .Static => .{ .musl_crt_file = .libc_a }, .Dynamic => .{ .musl_crt_file = .libc_so }, }, }); } - if (comp.wantBuildWasiLibcFromSource()) { - if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; - const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs; - try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components - for (wasi_emulated_libs) |crt_file| { - comp.work_queue.writeItemAssumeCapacity(.{ - .wasi_libc_crt_file = crt_file, - }); + if (comp.bin_file) |lf| { + if (lf.cast(link.File.Wasm)) |wasm| { + if (comp.wantBuildWasiLibcFromSource()) { + if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; + + // worst-case we need all components + try comp.work_queue.ensureUnusedCapacity(wasm.wasi_emulated_libs.len + 2); + + for (wasm.wasi_emulated_libs) |crt_file| { + comp.work_queue.writeItemAssumeCapacity(.{ + .wasi_libc_crt_file = crt_file, + }); + } + comp.work_queue.writeAssumeCapacity(&[_]Job{ + .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) }, + .{ .wasi_libc_crt_file = .libc_a }, + }); + } } - comp.work_queue.writeAssumeCapacity(&[_]Job{ - .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(options.config.wasi_exec_model) }, - .{ .wasi_libc_crt_file = .libc_a }, - }); } + if (comp.wantBuildMinGWFromSource()) { if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; @@ -1863,27 +1871,29 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (comp.wantBuildLibUnwindFromSource()) { try comp.work_queue.writeItem(.{ .libunwind = {} }); } - if (build_options.have_llvm and is_exe_or_dyn_lib and comp.bin_file.options.link_libcpp) { + if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.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) { + if (build_options.have_llvm and comp.config.any_sanitize_thread) { try comp.work_queue.writeItem(.libtsan); } - if (comp.getTarget().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 (comp.bin_file) |lf| { + if (comp.getTarget().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 (comp.getTarget().cpu.arch) { - .x86 => "__tls_index", - else => "_tls_index", - }; + const tls_index_sym = switch (comp.getTarget().cpu.arch) { + .x86 => "__tls_index", + else => "_tls_index", + }; - try comp.bin_file.options.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); + try lf.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); + } } - if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { + 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; @@ -1895,8 +1905,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - if (!comp.bin_file.options.skip_linker_dependencies and is_exe_or_dyn_lib and - !comp.bin_file.options.link_libc and capable_of_building_zig_libc) + if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and + !comp.config.link_libc and capable_of_building_zig_libc) { try comp.work_queue.writeItem(.{ .zig_libc = {} }); } @@ -2425,7 +2435,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.test_evented_io); man.hash.addOptionalBytes(comp.test_filter); man.hash.addOptionalBytes(comp.test_name_prefix); - man.hash.add(comp.bin_file.options.skip_linker_dependencies); + man.hash.add(comp.skip_linker_dependencies); man.hash.add(comp.formatted_panics); man.hash.add(mod.emit_h != null); man.hash.add(mod.error_limit); @@ -2477,7 +2487,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); man.hash.add(comp.bin_file.options.each_lib_rpath); man.hash.add(comp.bin_file.build_id); - man.hash.add(comp.bin_file.options.skip_linker_dependencies); + man.hash.add(comp.skip_linker_dependencies); man.hash.add(comp.bin_file.options.z_nodelete); man.hash.add(comp.bin_file.options.z_notext); man.hash.add(comp.bin_file.options.z_defs); @@ -2489,7 +2499,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.z_max_page_size orelse 0); man.hash.add(comp.bin_file.options.hash_style); man.hash.add(comp.bin_file.options.compress_debug_sections); - man.hash.add(comp.bin_file.options.include_compiler_rt); + man.hash.add(comp.include_compiler_rt); man.hash.add(comp.bin_file.options.sort_section); if (comp.config.link_libc) { man.hash.add(comp.bin_file.options.libc_installation != null); @@ -3836,7 +3846,7 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { // Also nothing that applies only to compiling .zig code. man.hash.add(comp.sanitize_c); man.hash.addListOfBytes(comp.clang_argv); - man.hash.add(comp.bin_file.options.link_libcpp); + man.hash.add(comp.config.link_libcpp); // When libc_installation is null it means that Zig generated this dir list // based on the zig library directory alone. The zig lib directory file @@ -4909,7 +4919,7 @@ pub fn addCCArgs( try argv.append("-fno-builtin"); } - if (comp.bin_file.options.link_libcpp) { + if (comp.config.link_libcpp) { const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "libcxx", "include", }); @@ -4954,7 +4964,7 @@ pub fn addCCArgs( try argv.append(libunwind_include_path); } - if (comp.bin_file.options.link_libc and target.isGnuLibC()) { + if (comp.config.link_libc and target.isGnuLibC()) { const target_version = target.os.version_range.linux.glibc; const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{ target_version.minor, @@ -5944,7 +5954,7 @@ fn wantBuildLibCFromSource(comp: Compilation) bool { .Lib => comp.bin_file.options.link_mode == .Dynamic, .Exe => true, }; - return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and + return comp.config.link_libc and is_exe_or_dyn_lib and comp.bin_file.options.libc_installation == null and comp.bin_file.options.target.ofmt != .c; } @@ -6164,6 +6174,7 @@ fn buildOutputFromZig( .have_zcu = true, .emit_bin = true, .root_optimize_mode = comp.compilerRtOptMode(), + .link_libc = comp.config.link_libc, }); const root_mod = Package.Module.create(.{ @@ -6224,7 +6235,6 @@ fn buildOutputFromZig( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - .link_libc = comp.bin_file.options.link_libc, .want_structured_cfg = comp.bin_file.options.want_structured_cfg, }); defer sub_compilation.destroy(); @@ -6305,7 +6315,6 @@ pub fn build_crt_file( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - .link_libc = comp.bin_file.options.link_libc, .want_structured_cfg = comp.bin_file.options.want_structured_cfg, }); defer sub_compilation.destroy(); @@ -6327,7 +6336,7 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { // 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.skip_linker_dependencies) return; + if (comp.skip_linker_dependencies) 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 diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 0e379212cc..aba21e4bfe 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -335,7 +335,7 @@ pub fn resolve(options: Options) !Config { break :b .Static; }; - const import_memory = options.import_memory orelse false; + const import_memory = options.import_memory orelse (options.output_mode == .Obj); const export_memory = b: { if (link_mode == .Dynamic) { if (options.export_memory == true) return error.ExportMemoryAndDynamicIncompatible; diff --git a/src/Sema.zig b/src/Sema.zig index 9d1f65584e..910b1cca47 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5742,7 +5742,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr const msg = try sema.errMsg(&child_block, src, "C import failed", .{}); errdefer msg.destroy(gpa); - if (!comp.bin_file.options.link_libc) + if (!comp.config.link_libc) try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); const gop = try mod.cimport_errors.getOrPut(gpa, sema.owner_decl_index); @@ -9058,7 +9058,7 @@ fn resolveGenericBody( /// Given a library name, examines if the library name should end up in /// `link.File.Options.system_libs` table (for example, libc is always -/// specified via dedicated flag `link.File.Options.link_libc` instead), +/// specified via dedicated flag `link_libc` instead), /// and puts it there if it doesn't exist. /// It also dupes the library name which can then be saved as part of the /// respective `Decl` (either `ExternFn` or `Var`). @@ -9076,7 +9076,7 @@ fn handleExternLibName( const target = mod.getTarget(); log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); if (target.is_libc_lib_name(lib_name)) { - if (!comp.bin_file.options.link_libc) { + if (!comp.config.link_libc) { return sema.fail( block, src_loc, @@ -9087,18 +9087,21 @@ fn handleExternLibName( break :blk; } if (target.is_libcpp_lib_name(lib_name)) { - if (!comp.bin_file.options.link_libcpp) { - return sema.fail( - block, - src_loc, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } + if (!comp.config.link_libcpp) return sema.fail( + block, + src_loc, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); break :blk; } if (mem.eql(u8, lib_name, "unwind")) { - comp.bin_file.options.link_libunwind = true; + if (!comp.config.link_libunwind) return sema.fail( + block, + src_loc, + "dependency on libunwind must be explicitly specified in the build command", + .{}, + ); break :blk; } if (!target.isWasm() and !comp.bin_file.options.pic) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a072c6a069..522da0f8c9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3083,9 +3083,7 @@ pub const Object = struct { if (comp.unwind_tables) { try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); } - if (comp.bin_file.options.skip_linker_dependencies or - comp.bin_file.options.no_builtin) - { + if (comp.skip_linker_dependencies or comp.bin_file.options.no_builtin) { // The intent here is for compiler-rt and libc functions to not generate // infinite recursion. For example, if we are compiling the memcpy function, // and llvm detects that the body is equivalent to memcpy, it may replace the diff --git a/src/libtsan.zig b/src/libtsan.zig index 2489eb21af..d0af65e4c6 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -119,7 +119,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { }); } - const to_c_or_not_to_c_sources = if (comp.bin_file.options.link_libc) + const to_c_or_not_to_c_sources = if (comp.config.link_libc) &sanitizer_libcdep_sources else &sanitizer_nolibc_sources; diff --git a/src/link.zig b/src/link.zig index 95a3282186..612c05f82b 100644 --- a/src/link.zig +++ b/src/link.zig @@ -133,7 +133,6 @@ pub const File = struct { export_symbol_names: []const []const u8, global_base: ?u64, dll_export_fns: bool, - skip_linker_dependencies: bool, parent_compilation_link_libc: bool, each_lib_rpath: bool, build_id: std.zig.BuildId, @@ -1099,7 +1098,7 @@ pub const File = struct { } pub fn isStatic(self: File) bool { - return self.base.comp.config.link_mode == .Static; + return self.comp.config.link_mode == .Static; } pub fn isObject(self: File) bool { diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index a321d28fb3..1719d5fa67 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -48,7 +48,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const is_lib = self.base.comp.config.output_mode == .Lib; const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or self.base.comp.config.output_mode == .Exe; - const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib; + const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib; const target = self.base.comp.root_mod.resolved_target.result; const optimize_mode = self.base.comp.root_mod.optimize_mode; @@ -56,17 +56,17 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!self.base.options.disable_lld_caching) man.deinit(); + defer if (!self.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { man = comp.cache_parent.obtain(); self.base.releaseLock(); comptime assert(Compilation.link_hash_implementation_version == 10); - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -79,12 +79,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } try man.addOptionalFile(module_obj_path); - man.hash.addOptionalBytes(self.base.options.entry); + man.hash.addOptionalBytes(comp.config.entry); man.hash.add(self.base.stack_size); man.hash.addOptional(self.image_base); - man.hash.addListOfBytes(self.base.options.lib_dirs); - man.hash.add(self.base.options.skip_linker_dependencies); - if (self.base.options.link_libc) { + man.hash.addListOfBytes(self.lib_dirs); + man.hash.add(comp.skip_linker_dependencies); + if (comp.config.link_libc) { man.hash.add(self.base.comp.libc_installation != null); if (self.base.comp.libc_installation) |libc_installation| { man.hash.addBytes(libc_installation.crt_dir.?); @@ -95,18 +95,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } try link.hashAddSystemLibs(&man, self.base.comp.system_libs); - man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); - man.hash.addOptional(self.base.options.subsystem); - man.hash.add(self.base.options.is_test); - man.hash.add(self.base.options.tsaware); - man.hash.add(self.base.options.nxcompat); - man.hash.add(self.base.options.dynamicbase); + man.hash.addListOfBytes(self.base.force_undefined_symbols.keys()); + man.hash.addOptional(self.subsystem); + man.hash.add(comp.config.is_test); + man.hash.add(self.tsaware); + man.hash.add(self.nxcompat); + man.hash.add(self.dynamicbase); man.hash.addOptional(self.base.allow_shlib_undefined); // strip does not need to go into the linker hash because it is part of the hash namespace - man.hash.addOptional(self.base.options.major_subsystem_version); - man.hash.addOptional(self.base.options.minor_subsystem_version); - man.hash.addOptional(self.base.options.version); - try man.addOptionalFile(self.base.options.module_definition_file); + man.hash.addOptional(self.major_subsystem_version); + man.hash.addOptional(self.minor_subsystem_version); + man.hash.addOptional(comp.version); + try man.addOptionalFile(self.module_definition_file); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -141,8 +141,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (self.base.options.objects.len != 0) - break :blk self.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -171,21 +171,21 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-ERRORLIMIT:0"); try argv.append("-NOLOGO"); - if (!self.base.options.strip) { + if (self.base.debug_format != .strip) { try argv.append("-DEBUG"); const out_ext = std.fs.path.extension(full_out_path); - const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ + const out_pdb = self.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ full_out_path[0 .. full_out_path.len - out_ext.len], }); try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb})); } - if (self.base.options.version) |version| { + if (comp.version) |version| { try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); } - if (self.base.options.lto) { + if (comp.config.lto) { switch (optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("-OPT:lldlto=2"), @@ -209,7 +209,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (self.base.options.force_undefined_symbols.keys()) |symbol| { + for (self.base.force_undefined_symbols.keys()) |symbol| { try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); } @@ -217,17 +217,17 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-DLL"); } - if (self.base.options.entry) |entry| { + if (comp.config.entry) |entry| { try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry})); } - if (self.base.options.tsaware) { + if (self.tsaware) { try argv.append("-tsaware"); } - if (self.base.options.nxcompat) { + if (self.nxcompat) { try argv.append("-nxcompat"); } - if (!self.base.options.dynamicbase) { + if (!self.dynamicbase) { try argv.append("-dynamicbase:NO"); } if (self.base.allow_shlib_undefined) { @@ -236,12 +236,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); - if (self.base.options.implib_emit) |emit| { + if (self.implib_emit) |emit| { const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); } - if (self.base.options.link_libc) { + if (comp.config.link_libc) { if (self.base.comp.libc_installation) |libc_installation| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); @@ -252,12 +252,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); } - try argv.ensureUnusedCapacity(self.base.options.objects.len); - for (self.base.options.objects) |obj| { + try argv.ensureUnusedCapacity(comp.objects.len); + for (comp.objects) |obj| { if (obj.must_link) { argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); } else { @@ -279,18 +279,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(p); } - if (self.base.options.module_definition_file) |def| { + if (self.module_definition_file) |def| { try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); } const resolved_subsystem: ?std.Target.SubSystem = blk: { - if (self.base.options.subsystem) |explicit| break :blk explicit; + if (self.subsystem) |explicit| break :blk explicit; switch (target.os.tag) { .windows => { if (self.base.comp.module) |module| { if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) break :blk null; - if (module.stage1_flags.have_c_main or self.base.options.is_test or + if (module.stage1_flags.have_c_main or comp.config.is_test or module.stage1_flags.have_winmain_crt_startup or module.stage1_flags.have_wwinmain_crt_startup) { @@ -310,8 +310,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const mode: Mode = mode: { if (resolved_subsystem) |subsystem| { const subsystem_suffix = ss: { - if (self.base.options.major_subsystem_version) |major| { - if (self.base.options.minor_subsystem_version) |minor| { + if (self.major_subsystem_version) |major| { + if (self.minor_subsystem_version) |minor| { break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor }); } else { break :ss try allocPrint(arena, ",{d}", .{major}); @@ -447,7 +447,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } else { try argv.append("-NODEFAULTLIB"); - if (!is_lib and self.base.options.entry == null) { + if (!is_lib and comp.config.entry == null) { if (self.base.comp.module) |module| { if (module.stage1_flags.have_winmain_crt_startup) { try argv.append("-ENTRY:WinMainCRTStartup"); @@ -463,18 +463,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } - if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { - if (!self.base.options.link_libc) { + if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { + if (!comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); } @@ -492,13 +492,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod argv.appendAssumeCapacity(crt_file.full_object_path); continue; } - if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } if (target.abi.isGnu()) { const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); - if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, fallback_name, self.lib_dirs)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } @@ -582,7 +582,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index ef803a45f1..5bd65b2227 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1022,16 +1022,18 @@ const min_nop_size = 2; /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; -pub fn init(allocator: Allocator, bin_file: *File, format: Format) Dwarf { - const target = &bin_file.options.target; +pub fn init(lf: *File, format: Format) Dwarf { + const comp = lf.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 0...32 => .p32, 33...64 => .p64, else => unreachable, }; return .{ - .allocator = allocator, - .bin_file = bin_file, + .allocator = gpa, + .bin_file = lf, .format = format, .ptr_width = ptr_width, .dbg_line_header = switch (target.cpu.arch) { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index df83878494..a116c46d2f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,5 +1,6 @@ base: link.File, image_base: u64, +rdynamic: bool, ptr_width: PtrWidth, @@ -358,19 +359,21 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, .function_sections = options.function_sections, .data_sections = options.data_sections, - - .image_base = b: { - if (is_dyn_lib) break :b 0; - if (output_mode == .Exe and comp.config.pie) return 0; - return options.image_base orelse switch (ptr_width) { - .p32 => 0x1000, - .p64 => 0x1000000, - }; - }, }, .ptr_width = ptr_width, .page_size = page_size, .default_sym_version = default_sym_version, + + .image_base = b: { + if (is_dyn_lib) break :b 0; + if (output_mode == .Exe and comp.config.pie) break :b 0; + break :b options.image_base orelse switch (ptr_width) { + .p32 => 0x1000, + .p64 => 0x1000000, + }; + }, + + .rdynamic = options.rdynamic, }; if (use_llvm and comp.config.have_zcu) { self.llvm_object = try LlvmObject.create(arena, options); @@ -1006,7 +1009,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node break :blk path; } } else null; - const gc_sections = self.base.gc_sections; // --verbose-link if (self.base.comp.verbose_link) try self.dumpArgv(comp); @@ -1047,14 +1049,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node var rpath_table = std.StringArrayHashMap(void).init(gpa); defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { _ = try rpath_table.put(rpath, {}); } - if (self.base.options.each_lib_rpath) { + if (self.each_lib_rpath) { var test_path = std.ArrayList(u8).init(gpa); defer test_path.deinit(); - for (self.base.options.lib_dirs) |lib_dir_path| { + for (self.lib_dirs) |lib_dir_path| { for (self.base.comp.system_libs.keys()) |link_lib| { if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic))) continue; @@ -1076,9 +1078,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } // libc - if (!self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) - { + if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try positionals.append(.{ .path = lib.full_object_path }); } @@ -1263,10 +1263,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.entry_index = if (entry) |name| self.globalByName(name) else null; } - if (gc_sections) { + if (self.base.gc_sections) { try gc.gcAtoms(self); - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try gc.dumpPrunedAtoms(self); } } @@ -1347,13 +1347,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = self.base.comp.gpa; + const gpa = comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); - positionals.appendSliceAssumeCapacity(self.base.options.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1495,8 +1495,8 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); - positionals.appendSliceAssumeCapacity(self.base.options.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1606,7 +1606,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append(full_out_path); if (self.base.isRelocatable()) { - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { try argv.append(obj.path); } @@ -1626,28 +1626,28 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (self.base.isDynLib()) { - if (self.base.options.soname) |name| { + if (self.soname) |name| { try argv.append("-soname"); try argv.append(name); } } - if (self.base.options.entry) |entry| { + if (comp.config.entry) |entry| { try argv.append("--entry"); try argv.append(entry); } - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { try argv.append("-rpath"); try argv.append(rpath); } - if (self.base.options.each_lib_rpath) { - for (self.base.options.lib_dirs) |lib_dir_path| { + if (self.each_lib_rpath) { + for (self.lib_dirs) |lib_dir_path| { try argv.append("-rpath"); try argv.append(lib_dir_path); } - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -1669,29 +1669,29 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append("--gc-sections"); } - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try argv.append("--print-gc-sections"); } - if (self.base.options.eh_frame_hdr) { + if (self.eh_frame_hdr) { try argv.append("--eh-frame-hdr"); } - if (self.base.options.rdynamic) { + if (self.rdynamic) { try argv.append("--export-dynamic"); } - if (self.base.options.z_notext) { + if (self.z_notext) { try argv.append("-z"); try argv.append("notext"); } - if (self.base.options.z_nocopyreloc) { + if (self.z_nocopyreloc) { try argv.append("-z"); try argv.append("nocopyreloc"); } - if (self.base.options.z_now) { + if (self.z_now) { try argv.append("-z"); try argv.append("now"); } @@ -1702,11 +1702,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append("-shared"); } - if (self.base.options.pie and self.base.isExe()) { + if (comp.config.pie and self.base.isExe()) { try argv.append("-pie"); } - if (self.base.options.strip) { + if (self.base.debug_format == .strip) { try argv.append("-s"); } @@ -1715,12 +1715,12 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { if (csu.crti) |v| try argv.append(v); if (csu.crtbegin) |v| try argv.append(v); - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); } - if (self.base.options.link_libc) { + if (comp.config.link_libc) { if (self.base.comp.libc_installation) |libc_installation| { try argv.append("-L"); try argv.append(libc_installation.crt_dir.?); @@ -1728,7 +1728,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } var whole_archive = false; - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -1756,15 +1756,12 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append(p); } - // TSAN - if (self.base.options.tsan) { + if (comp.config.any_sanitize_thread) { try argv.append(comp.tsan_static_lib.?.full_object_path); } // libc - if (!self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) - { + if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); } @@ -1799,18 +1796,18 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } // libc dep - if (self.base.options.link_libc) { + if (comp.config.link_libc) { if (self.base.comp.libc_installation != null) { const needs_grouping = link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); @@ -1963,8 +1960,6 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { defer script.deinit(gpa); try script.parse(data, self); - const lib_dirs = self.base.options.lib_dirs; - var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -1981,7 +1976,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { // TODO I think technically we should re-use the mechanism used by the frontend here. // Maybe we should hoist search-strategy all the way here? - for (lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { if (!self.isStatic()) { if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Dynamic)) break :success; @@ -1998,7 +1993,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } else |_| {} try checked_paths.append(try gpa.dupe(u8, scr_obj.path)); - for (lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, scr_obj.path, null)) break :success; } @@ -2338,7 +2333,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const link_mode = self.base.comp.config.link_mode; const is_dyn_lib = link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; - const have_dynamic_linker = self.base.options.link_libc and + const have_dynamic_linker = comp.config.link_libc and link_mode == .Dynamic and is_exe_or_dyn_lib; const target = self.base.comp.root_mod.resolved_target.result; const compiler_rt_path: ?[]const u8 = blk: { @@ -2358,11 +2353,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!self.base.options.disable_lld_caching) man.deinit(); + defer if (!self.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. @@ -2370,9 +2365,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v comptime assert(Compilation.link_hash_implementation_version == 10); - try man.addOptionalFile(self.base.options.linker_script); - try man.addOptionalFile(self.base.options.version_script); - for (self.base.options.objects) |obj| { + try man.addOptionalFile(self.linker_script); + try man.addOptionalFile(self.version_script); + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); @@ -2385,34 +2380,34 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. - man.hash.addOptionalBytes(self.base.options.entry); + man.hash.addOptionalBytes(comp.config.entry); man.hash.addOptional(self.image_base); man.hash.add(self.base.gc_sections); - man.hash.addOptional(self.base.options.sort_section); - man.hash.add(self.base.options.eh_frame_hdr); - man.hash.add(self.base.options.emit_relocs); - man.hash.add(self.base.options.rdynamic); - man.hash.addListOfBytes(self.base.options.lib_dirs); - man.hash.addListOfBytes(self.base.options.rpath_list); - man.hash.add(self.base.options.each_lib_rpath); + man.hash.addOptional(self.sort_section); + man.hash.add(self.eh_frame_hdr); + man.hash.add(self.emit_relocs); + man.hash.add(self.rdynamic); + man.hash.addListOfBytes(self.lib_dirs); + man.hash.addListOfBytes(self.base.rpath_list); + man.hash.add(self.each_lib_rpath); if (output_mode == .Exe) { man.hash.add(self.base.stack_size); man.hash.add(self.base.build_id); } - man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys()); - man.hash.add(self.base.options.skip_linker_dependencies); - man.hash.add(self.base.options.z_nodelete); - man.hash.add(self.base.options.z_notext); - man.hash.add(self.base.options.z_defs); - man.hash.add(self.base.options.z_origin); - man.hash.add(self.base.options.z_nocopyreloc); - man.hash.add(self.base.options.z_now); - man.hash.add(self.base.options.z_relro); - man.hash.add(self.base.options.z_common_page_size orelse 0); - man.hash.add(self.base.options.z_max_page_size orelse 0); - man.hash.add(self.base.options.hash_style); + man.hash.addListOfBytes(self.symbol_wrap_set.keys()); + man.hash.add(comp.skip_linker_dependencies); + man.hash.add(self.z_nodelete); + man.hash.add(self.z_notext); + man.hash.add(self.z_defs); + man.hash.add(self.z_origin); + man.hash.add(self.z_nocopyreloc); + man.hash.add(self.z_now); + man.hash.add(self.z_relro); + man.hash.add(self.z_common_page_size orelse 0); + man.hash.add(self.z_max_page_size orelse 0); + man.hash.add(self.hash_style); // strip does not need to go into the linker hash because it is part of the hash namespace - if (self.base.options.link_libc) { + if (comp.config.link_libc) { man.hash.add(self.base.comp.libc_installation != null); if (self.base.comp.libc_installation) |libc_installation| { man.hash.addBytes(libc_installation.crt_dir.?); @@ -2421,16 +2416,16 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.addOptionalBytes(target.dynamic_linker.get()); } } - man.hash.addOptionalBytes(self.base.options.soname); - man.hash.addOptional(self.base.options.version); + man.hash.addOptionalBytes(self.soname); + man.hash.addOptional(comp.version); try link.hashAddSystemLibs(&man, self.base.comp.system_libs); - man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); + man.hash.addListOfBytes(self.base.force_undefined_symbols.keys()); man.hash.add(self.base.allow_shlib_undefined); - man.hash.add(self.base.options.bind_global_refs_locally); - man.hash.add(self.base.options.compress_debug_sections); - man.hash.add(self.base.options.tsan); - man.hash.addOptionalBytes(self.base.options.sysroot); - man.hash.add(self.base.options.linker_optimization); + man.hash.add(self.bind_global_refs_locally); + man.hash.add(self.compress_debug_sections); + man.hash.add(comp.config.any_sanitize_thread); + man.hash.addOptionalBytes(self.sysroot); + man.hash.add(self.optimization); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -2466,14 +2461,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails // before even generating the relocatable. if (output_mode == .Obj and - (self.base.options.lto or target.isBpfFreestanding())) + (comp.config.lto or target.isBpfFreestanding())) { // In this case we must do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (self.base.options.objects.len != 0) - break :blk self.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -2505,32 +2500,32 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--error-limit=0"); - if (self.base.options.sysroot) |sysroot| { + if (self.sysroot) |sysroot| { try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); } - if (self.base.options.lto) { - switch (self.base.options.optimize_mode) { + if (comp.config.lto) { + switch (comp.root_mod.optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("--lto-O2"), .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), } } try argv.append(try std.fmt.allocPrint(arena, "-O{d}", .{ - self.base.options.linker_optimization, + self.optimization, })); - if (self.base.options.entry) |entry| { + if (comp.config.entry) |entry| { try argv.append("--entry"); try argv.append(entry); } - for (self.base.options.force_undefined_symbols.keys()) |sym| { + for (self.base.force_undefined_symbols.keys()) |sym| { try argv.append("-u"); try argv.append(sym); } - switch (self.base.options.hash_style) { + switch (self.hash_style) { .gnu => try argv.append("--hash-style=gnu"), .sysv => try argv.append("--hash-style=sysv"), .both => {}, // this is the default @@ -2559,12 +2554,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); - if (self.base.options.linker_script) |linker_script| { + if (self.linker_script) |linker_script| { try argv.append("-T"); try argv.append(linker_script); } - if (self.base.options.sort_section) |how| { + if (self.sort_section) |how| { const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); try argv.append(arg); } @@ -2573,67 +2568,67 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--gc-sections"); } - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try argv.append("--print-gc-sections"); } - if (self.base.options.print_icf_sections) { + if (self.print_icf_sections) { try argv.append("--print-icf-sections"); } - if (self.base.options.print_map) { + if (self.print_map) { try argv.append("--print-map"); } - if (self.base.options.eh_frame_hdr) { + if (self.eh_frame_hdr) { try argv.append("--eh-frame-hdr"); } - if (self.base.options.emit_relocs) { + if (self.emit_relocs) { try argv.append("--emit-relocs"); } - if (self.base.options.rdynamic) { + if (self.rdynamic) { try argv.append("--export-dynamic"); } - if (self.base.options.strip) { + if (self.base.debug_format == .strip) { try argv.append("-s"); } - if (self.base.options.z_nodelete) { + if (self.z_nodelete) { try argv.append("-z"); try argv.append("nodelete"); } - if (self.base.options.z_notext) { + if (self.z_notext) { try argv.append("-z"); try argv.append("notext"); } - if (self.base.options.z_defs) { + if (self.z_defs) { try argv.append("-z"); try argv.append("defs"); } - if (self.base.options.z_origin) { + if (self.z_origin) { try argv.append("-z"); try argv.append("origin"); } - if (self.base.options.z_nocopyreloc) { + if (self.z_nocopyreloc) { try argv.append("-z"); try argv.append("nocopyreloc"); } - if (self.base.options.z_now) { + if (self.z_now) { // LLD defaults to -zlazy try argv.append("-znow"); } - if (!self.base.options.z_relro) { + if (!self.z_relro) { // LLD defaults to -zrelro try argv.append("-znorelro"); } - if (self.base.options.z_common_page_size) |size| { + if (self.z_common_page_size) |size| { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); } - if (self.base.options.z_max_page_size) |size| { + if (self.z_max_page_size) |size| { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); } @@ -2658,7 +2653,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("-shared"); } - if (self.base.options.pie and output_mode == .Exe) { + if (comp.config.pie and output_mode == .Exe) { try argv.append("-pie"); } @@ -2683,20 +2678,20 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // rpaths var rpath_table = std.StringHashMap(void).init(gpa); defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { if ((try rpath_table.fetchPut(rpath, {})) == null) { try argv.append("-rpath"); try argv.append(rpath); } } - for (self.base.options.symbol_wrap_set.keys()) |symbol_name| { + for (self.symbol_wrap_set.keys()) |symbol_name| { try argv.appendSlice(&.{ "-wrap", symbol_name }); } - if (self.base.options.each_lib_rpath) { + if (self.each_lib_rpath) { var test_path = std.ArrayList(u8).init(arena); - for (self.base.options.lib_dirs) |lib_dir_path| { + for (self.lib_dirs) |lib_dir_path| { for (self.base.comp.system_libs.keys()) |link_lib| { if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic))) continue; @@ -2706,7 +2701,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } } - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -2719,12 +2714,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); } - if (self.base.options.link_libc) { + if (comp.config.link_libc) { if (self.base.comp.libc_installation) |libc_installation| { try argv.append("-L"); try argv.append(libc_installation.crt_dir.?); @@ -2739,11 +2734,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } if (is_dyn_lib) { - if (self.base.options.soname) |soname| { + if (self.soname) |soname| { try argv.append("-soname"); try argv.append(soname); } - if (self.base.options.version_script) |version_script| { + if (self.version_script) |version_script| { try argv.append("-version-script"); try argv.append(version_script); } @@ -2751,7 +2746,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // Positional arguments to the linker such as object files. var whole_archive = false; - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -2779,15 +2774,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(p); } - // TSAN - if (self.base.options.tsan) { + if (comp.config.any_sanitize_thread) { try argv.append(comp.tsan_static_lib.?.full_object_path); } // libc if (is_exe_or_dyn_lib and - !self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) + !comp.skip_linker_dependencies and + !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); @@ -2832,19 +2826,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } // libc dep self.error_flags.missing_libc = false; - if (self.base.options.link_libc) { + if (comp.config.link_libc) { if (self.base.comp.libc_installation != null) { const needs_grouping = link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); @@ -2884,13 +2878,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--allow-shlib-undefined"); } - switch (self.base.options.compress_debug_sections) { + switch (self.compress_debug_sections) { .none => {}, .zlib => try argv.append("--compress-debug-sections=zlib"), .zstd => try argv.append("--compress-debug-sections=zstd"), } - if (self.base.options.bind_global_refs_locally) { + if (self.bind_global_refs_locally) { try argv.append("-Bsymbolic"); } @@ -2964,7 +2958,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -3101,7 +3095,8 @@ fn writeElfHeader(self: *Elf) !void { }; index += 1; - const target = self.base.comp.root_mod.resolved_target.result; + const comp = self.base.comp; + const target = comp.root_mod.resolved_target.result; const endian = target.cpu.arch.endian(); hdr_buf[index] = switch (endian) { .little => elf.ELFDATA2LSB, @@ -3120,10 +3115,10 @@ fn writeElfHeader(self: *Elf) !void { assert(index == 16); - const output_mode = self.base.comp.config.output_mode; - const link_mode = self.base.comp.config.link_mode; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; const elf_type: elf.ET = switch (output_mode) { - .Exe => if (self.base.options.pie) .DYN else .EXEC, + .Exe => if (comp.config.pie) .DYN else .EXEC, .Obj => .REL, .Lib => switch (link_mode) { .Static => @as(elf.ET, .REL), @@ -3283,7 +3278,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self); self.end_index = try linker_defined.addGlobal("_end", self); - if (self.base.options.eh_frame_hdr) { + if (self.eh_frame_hdr) { self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self); } @@ -3314,7 +3309,8 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { } fn allocateLinkerDefinedSymbols(self: *Elf) void { - const link_mode = self.base.comp.config.link_mode; + const comp = self.base.comp; + const link_mode = comp.config.link_mode; // _DYNAMIC if (self.dynamic_section_index) |shndx| { @@ -3398,7 +3394,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __rela_iplt_start, __rela_iplt_end if (self.rela_dyn_section_index) |shndx| blk: { - if (link_mode != .Static or self.base.options.pie) break :blk; + if (link_mode != .Static or comp.config.pie) break :blk; const shdr = &self.shdrs.items[shndx]; const end_addr = shdr.sh_addr + shdr.sh_size; const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); @@ -3445,7 +3441,8 @@ fn initOutputSections(self: *Elf) !void { } fn initSyntheticSections(self: *Elf) !void { - const target = self.base.comp.root_mod.resolved_target.result; + const comp = self.base.comp; + const target = comp.root_mod.resolved_target.result; const ptr_size = self.ptrWidthBytes(); const needs_eh_frame = for (self.objects.items) |index| { @@ -3460,7 +3457,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); - if (self.base.options.eh_frame_hdr) { + if (self.eh_frame_hdr) { self.eh_frame_hdr_section_index = try self.addSection(.{ .name = ".eh_frame_hdr", .type = elf.SHT_PROGBITS, @@ -3554,7 +3551,7 @@ fn initSyntheticSections(self: *Elf) !void { // In this case, if we do generate .interp section and segment, we will get // a segfault in the dynamic linker trying to load a binary that is static // and doesn't contain .dynamic section. - if (self.isStatic() and !self.base.options.pie) break :blk false; + if (self.isStatic() and !comp.config.pie) break :blk false; break :blk target.dynamic_linker.get() != null; }; if (needs_interp) { @@ -3567,7 +3564,7 @@ fn initSyntheticSections(self: *Elf) !void { }); } - if (self.base.isDynLib() or self.shared_objects.items.len > 0 or self.base.options.pie) { + if (self.base.isDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { self.dynstrtab_section_index = try self.addSection(.{ .name = ".dynstr", .flags = elf.SHF_ALLOC, @@ -3859,7 +3856,7 @@ fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { } if (self.base.isDynLib()) { - if (self.base.options.soname) |soname| { + if (self.soname) |soname| { try self.dynamic.setSoname(soname, self); } } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index b3d7b51d3e..dc330fa54b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -543,7 +543,7 @@ fn scanReloc( try self.reportPicError(symbol, rel, elf_file), .copyrel => { - if (elf_file.base.options.z_nocopyreloc) { + if (elf_file.z_nocopyreloc) { if (symbol.isAbs(elf_file)) try self.reportNoPicError(symbol, rel, elf_file) else @@ -553,9 +553,9 @@ fn scanReloc( }, .dyn_copyrel => { - if (is_writeable or elf_file.base.options.z_nocopyreloc) { + if (is_writeable or elf_file.z_nocopyreloc) { if (!is_writeable) { - if (elf_file.base.options.z_notext) { + if (elf_file.z_notext) { elf_file.has_text_reloc = true; } else { try self.reportTextRelocError(symbol, rel, elf_file); @@ -587,7 +587,7 @@ fn scanReloc( .dynrel, .baserel, .ifunc => { if (!is_writeable) { - if (elf_file.base.options.z_notext) { + if (elf_file.z_notext) { elf_file.has_text_reloc = true; } else { try self.reportTextRelocError(symbol, rel, elf_file); @@ -657,11 +657,12 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { } fn outputType(elf_file: *Elf) u2 { + const comp = elf_file.base.comp; assert(!elf_file.isRelocatable()); return switch (elf_file.base.comp.config.output_mode) { .Obj => unreachable, .Lib => 0, - .Exe => if (elf_file.base.options.pie) 1 else 2, + .Exe => if (comp.config.pie) 1 else 2, }; } @@ -979,7 +980,7 @@ fn resolveDynAbsReloc( => try writer.writeInt(i32, @as(i32, @truncate(S + A)), .little), .dyn_copyrel => { - if (is_writeable or elf_file.base.options.z_nocopyreloc) { + if (is_writeable or elf_file.z_nocopyreloc) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, .sym = target.extra(elf_file).?.dynamic, diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 347f96d451..5fa31711e6 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -299,7 +299,7 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; - if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + if (elf_file.base.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 15ae3bb943..05ff55dd18 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -97,7 +97,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { symbol_ptr.esym_index = esym_index; if (elf_file.base.debug_format != .strip) { - self.dwarf = Dwarf.init(gpa, &elf_file.base, .dwarf32); + self.dwarf = Dwarf.init(&elf_file.base, .dwarf32); } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 1e7f2f4f9f..92d5318c02 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -32,7 +32,7 @@ pub const DynamicSection = struct { fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 { _ = dt; var flags: u64 = 0; - if (elf_file.base.options.z_now) { + if (elf_file.z_now) { flags |= elf.DF_BIND_NOW; } for (elf_file.got.entries.items) |entry| switch (entry.tag) { @@ -49,15 +49,16 @@ pub const DynamicSection = struct { } fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 { + const comp = elf_file.base.comp; _ = dt; var flags_1: u64 = 0; - if (elf_file.base.options.z_now) { + if (elf_file.z_now) { flags_1 |= elf.DF_1_NOW; } - if (elf_file.isExe() and elf_file.base.options.pie) { + if (elf_file.isExe() and comp.config.pie) { flags_1 |= elf.DF_1_PIE; } - // if (elf_file.base.options.z_nodlopen) { + // if (elf_file.z_nodlopen) { // flags_1 |= elf.DF_1_NOOPEN; // } return if (flags_1 > 0) flags_1 else null; @@ -246,12 +247,13 @@ pub const ZigGotSection = struct { } pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { + const comp = elf_file.base.comp; const index = try zig_got.allocateEntry(elf_file.base.allocator); const entry = &zig_got.entries.items[index]; entry.* = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_zig_got = true; - if (elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) { + if (elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) { zig_got.flags.needs_rela = true; } if (symbol.extra(elf_file)) |extra| { @@ -478,6 +480,7 @@ pub const GotSection = struct { } pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { + const comp = elf_file.base.comp; const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .got; @@ -485,7 +488,7 @@ pub const GotSection = struct { const symbol = elf_file.symbol(sym_index); symbol.flags.has_got = true; if (symbol.flags.import or symbol.isIFunc(elf_file) or - ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and !symbol.isAbs(elf_file))) + ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file))) { got.flags.needs_rela = true; } @@ -561,6 +564,7 @@ pub const GotSection = struct { } pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { + const comp = elf_file.base.comp; const is_dyn_lib = elf_file.isDynLib(); const apply_relocs = true; // TODO add user option for this @@ -576,7 +580,7 @@ pub const GotSection = struct { if (symbol.?.flags.import) break :blk 0; if (symbol.?.isIFunc(elf_file)) break :blk if (apply_relocs) value else 0; - if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + if ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { break :blk if (apply_relocs) value else 0; @@ -623,6 +627,7 @@ pub const GotSection = struct { } pub fn addRela(got: GotSection, elf_file: *Elf) !void { + const comp = elf_file.base.comp; const is_dyn_lib = elf_file.isDynLib(); try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); @@ -652,7 +657,7 @@ pub const GotSection = struct { }); continue; } - if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + if ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { elf_file.addRelaDynAssumeCapacity(.{ @@ -725,6 +730,7 @@ pub const GotSection = struct { } pub fn numRela(got: GotSection, elf_file: *Elf) usize { + const comp = elf_file.base.comp; const is_dyn_lib = elf_file.isDynLib(); var num: usize = 0; for (got.entries.items) |entry| { @@ -734,7 +740,7 @@ pub const GotSection = struct { }; switch (entry.tag) { .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or - ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + ((elf_file.isDynLib() or (elf_file.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file))) { num += 1; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3da076ba56..87faec6537 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -252,7 +252,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { self.d_sym = .{ .allocator = gpa, - .dwarf = link.File.Dwarf.init(gpa, &self.base, .dwarf32), + .dwarf = link.File.Dwarf.init(&self.base, .dwarf32), .file = d_sym_file, }; } @@ -573,7 +573,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.writeLinkeditSegmentData(); - var codesig: ?CodeSignature = if (requiresCodeSignature(&self.base.options)) blk: { + var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. @@ -619,12 +619,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No }); }, .Lib => if (self.base.comp.config.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(gpa, &self.base.options, lc_writer); + try load_commands.writeDylibIdLC(self, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(gpa, &self.base.options, lc_writer); + try load_commands.writeRpathLCs(self, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); @@ -713,7 +713,7 @@ pub fn resolveLibSystem( success: { if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { .sdk => { - const dir = try fs.path.join(arena, &[_][]const u8{ self.base.options.sysroot.?, "usr", "lib" }); + const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" }); if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success; }, .vendored => { @@ -1156,7 +1156,8 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { // 2) afterwards, we parse dependents of the included dylibs // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const gpa = comp.gpa; while (dependent_libs.readItem()) |dep_id| { defer dep_id.id.deinit(gpa); @@ -1176,7 +1177,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { var checked_paths = std.ArrayList([]const u8).init(arena); success: { - if (self.base.options.sysroot) |root| { + if (comp.sysroot) |root| { const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname }); if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success; } @@ -1713,12 +1714,12 @@ pub fn resolveSymbols(self: *MachO) !void { // we search for it in libraries should there be no object files specified // on the linker line. if (output_mode == .Exe) { - const entry_name = self.base.options.entry orelse load_commands.default_entry_point; + const entry_name = comp.config.entry.?; _ = try self.addUndefined(entry_name, .{}); } // Force resolution of any symbols requested by the user. - for (self.base.options.force_undefined_symbols.keys()) |sym_name| { + for (self.base.force_undefined_symbols.keys()) |sym_name| { _ = try self.addUndefined(sym_name, .{}); } @@ -4367,7 +4368,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { // We generate stabs last in order to ensure that the strtab always has debug info // strings trailing - if (!self.base.options.strip) { + if (self.base.debug_format != .strip) { for (self.objects.items) |object| { assert(self.d_sym == null); // TODO try self.generateSymbolStabs(object, &locals); @@ -5171,7 +5172,8 @@ pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { /// Returns symbol location corresponding to the set entrypoint if any. /// Asserts output mode is executable. pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { - const entry_name = self.base.options.entry orelse load_commands.default_entry_point; + const comp = self.base.comp; + const entry_name = comp.config.entry orelse return null; const global = self.getGlobal(entry_name) orelse return null; return global; } @@ -5189,11 +5191,13 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { }; } -pub fn requiresCodeSignature(options: *const link.Options) bool { - if (options.entitlements) |_| return true; - const cpu_arch = options.target.cpu.arch; - const os_tag = options.target.os.tag; - const abi = options.target.abi; +pub fn requiresCodeSignature(m: *MachO) bool { + if (m.entitlements) |_| return true; + const comp = m.base.comp; + const target = comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; + const os_tag = target.os.tag; + const abi = target.abi; if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true; return false; } diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 2e7f8e49af..74a4afeb54 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -198,10 +198,10 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 } pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { + const comp = macho_file.base.comp; // TODO This linker code currently assumes there is only 1 compilation unit // and it corresponds to the Zig source code. - const options = macho_file.base.options; - const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const zcu = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; for (self.relocs.items) |*reloc| { const sym = switch (reloc.type) { @@ -245,7 +245,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?]; const low_pc = text_section.addr; const high_pc = text_section.addr + text_section.size; - try self.dwarf.writeDbgInfoHeader(module, low_pc, high_pc); + try self.dwarf.writeDbgInfoHeader(zcu, low_pc, high_pc); self.debug_info_header_dirty = false; } @@ -572,6 +572,5 @@ const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Dwarf = @import("../Dwarf.zig"); const MachO = @import("../MachO.zig"); -const Module = @import("../../Module.zig"); const StringTable = @import("../StringTable.zig"); const Type = @import("../../type.zig").Type; diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 51a43351ba..a92126c07e 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -60,7 +60,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void { } // Add all symbols force-defined by the user. - for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| { + for (macho_file.base.force_undefined_symbols.keys()) |sym_name| { const global_index = macho_file.resolver.get(sym_name).?; const global = macho_file.globals.items[global_index]; const sym = macho_file.getSymbol(global); diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index be8ac63642..3bffc7f73e 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -1,6 +1,3 @@ -/// Default implicit entrypoint symbol name. -pub const default_entry_point: []const u8 = "_main"; - /// Default path to dyld. pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; @@ -17,7 +14,9 @@ const CalcLCsSizeCtx = struct { wants_function_starts: bool = true, }; -fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 { +fn calcLCsSize(m: *MachO, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 { + const comp = m.base.comp; + const gpa = comp.gpa; var has_text_segment: bool = false; var sizeofcmds: u64 = 0; for (ctx.segments) |seg| { @@ -46,15 +45,15 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx false, ); // LC_MAIN - if (options.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { sizeofcmds += @sizeOf(macho.entry_point_command); } // LC_ID_DYLIB - if (options.output_mode == .Lib and options.link_mode == .Dynamic) { + if (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic) { sizeofcmds += blk: { - const emit = options.emit.?; - const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); - defer if (options.install_name == null) gpa.free(install_name); + const emit = m.base.emit; + const install_name = m.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); + defer if (m.install_name == null) gpa.free(install_name); break :blk calcInstallNameLen( @sizeOf(macho.dylib_command), install_name, @@ -64,7 +63,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx } // LC_RPATH { - var it = RpathIterator.init(gpa, options.rpath_list); + var it = RpathIterator.init(gpa, m.rpath_list); defer it.deinit(); while (try it.next()) |rpath| { sizeofcmds += calcInstallNameLen( @@ -78,7 +77,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx sizeofcmds += @sizeOf(macho.source_version_command); // LC_BUILD_VERSION or LC_VERSION_MIN_ or nothing { - const platform = Platform.fromTarget(options.target); + const target = comp.root_mod.resolved_target.result; + const platform = Platform.fromTarget(target); if (platform.isBuildVersionCompatible()) { // LC_BUILD_VERSION sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); @@ -100,19 +100,19 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx ); } // LC_CODE_SIGNATURE - if (MachO.requiresCodeSignature(options)) { + if (m.requiresCodeSignature()) { sizeofcmds += @sizeOf(macho.linkedit_data_command); } - return @as(u32, @intCast(sizeofcmds)); + return @intCast(sizeofcmds); } -pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 { - var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0); +pub fn calcMinHeaderPad(m: *MachO, ctx: CalcLCsSizeCtx) !u64 { + var padding: u32 = (try calcLCsSize(m, ctx, false)) + m.headerpad_size; log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)}); - if (options.headerpad_max_install_names) { - const min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true); + if (m.headerpad_max_install_names) { + const min_headerpad_size: u32 = try calcLCsSize(m, ctx, true); log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{ min_headerpad_size + @sizeOf(macho.mach_header_64), }); diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 8b0aa90f96..f40aaea4db 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -499,7 +499,7 @@ pub fn linkWithZld( } // Write code signature padding if required - var codesig: ?CodeSignature = if (MachO.requiresCodeSignature(&macho_file.base.options)) blk: { + var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. @@ -547,12 +547,12 @@ pub fn linkWithZld( }); }, .Lib => if (link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer); + try load_commands.writeDylibIdLC(macho_file, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(gpa, &macho_file.base.options, lc_writer); + try load_commands.writeRpathLCs(macho_file, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); @@ -1072,11 +1072,10 @@ fn calcSectionSizes(macho_file: *MachO) !void { } fn allocateSegments(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; for (macho_file.segments.items, 0..) |*segment, segment_index| { const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT"); const base_size = if (is_text_segment) - try load_commands.calcMinHeaderPad(gpa, &macho_file.base.options, .{ + try load_commands.calcMinHeaderPad(macho_file, .{ .segments = macho_file.segments.items, .dylibs = macho_file.dylibs.items, .referenced_dylibs = macho_file.referenced_dylibs.keys(), diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index 1cfc14cef0..846e496ec1 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -57,6 +57,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, + .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, .function_sections = options.function_sections, .data_sections = options.data_sections, }, @@ -66,7 +67,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { return nvptx; } -pub fn open(arena: Allocator, options: link.FileOpenOptions) !*NvPtx { +pub fn open(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { const target = options.comp.root_mod.resolved_target.result; assert(target.ofmt == .nvptx); return createEmpty(arena, options); diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 247986d767..b3c08fb66b 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -295,26 +295,36 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { } pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { - _ = arena; - const target = options.comp.root_mod.resolved_target.result; - const gpa = options.comp.gpa; + const comp = options.comp; + const target = comp.root_mod.resolved_target.result; + const gpa = comp.gpa; + const optimize_mode = comp.root_mod.optimize_mode; + const output_mode = comp.config.output_mode; - const sixtyfour_bit: bool = switch (options.target.ptrBitWidth()) { + const sixtyfour_bit: bool = switch (target.ptrBitWidth()) { 0...32 => false, 33...64 => true, else => return error.UnsupportedP9Architecture, }; - const arena_allocator = std.heap.ArenaAllocator.init(gpa); - - const self = try gpa.create(Plan9); + const self = try arena.create(Plan9); self.* = .{ - .path_arena = arena_allocator, + .path_arena = std.heap.ArenaAllocator.init(gpa), .base = .{ .tag = .plan9, - .options = options, - .allocator = gpa, + .comp = comp, + .emit = options.emit, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .stack_size = options.stack_size orelse 16777216, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, + .force_undefined_symbols = options.force_undefined_symbols, + .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, + .function_sections = options.function_sections, + .data_sections = options.data_sections, }, .sixtyfour_bit = sixtyfour_bit, .bases = undefined, @@ -602,7 +612,7 @@ pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) li const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; assert(!use_lld); - switch (self.base.options.effectiveOutputMode()) { + switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { .Exe => {}, // plan9 object files are totally different .Obj => return error.TODOImplementPlan9Objs, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 414ebcf987..d8f78c32f1 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -67,6 +67,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV { .force_undefined_symbols = options.force_undefined_symbols, .function_sections = options.function_sections, .data_sections = options.data_sections, + .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, }, .object = codegen.Object.init(gpa), }; @@ -102,7 +103,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*SpirV { errdefer spirv.base.destroy(); // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.?.directory.handle.createFile(options.emit.sub_path, .{ + const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{ .truncate = true, .read = true, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 0a728c7b6b..74d83e6c3c 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -37,6 +37,13 @@ pub const Relocation = types.Relocation; pub const base_tag: link.File.Tag = .wasm; base: link.File, +import_symbols: bool, +export_symbol_names: []const []const u8, +rdynamic: bool, +global_base: ?u64, +initial_memory: ?u64, +max_memory: ?u64, +wasi_emulated_libs: []const wasi_libc.CRTFile, /// Output name of the file name: []const u8, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. @@ -368,13 +375,15 @@ pub const StringTable = struct { pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { if (build_options.only_c) unreachable; - const gpa = options.comp.gpa; - const target = options.comp.root_mod.resolved_target.result; + const comp = options.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .wasm); - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; - const output_mode = options.comp.config.output_mode; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; + const shared_memory = comp.config.shared_memory; const wasm = try createEmpty(arena, options); errdefer wasm.base.destroy(); @@ -395,7 +404,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { }; // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + const file = try options.emit.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) @@ -480,7 +489,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { } // shared-memory symbols for TLS support - if (wasm.base.options.shared_memory) { + if (shared_memory) { { const loc = try wasm.createSyntheticSymbol("__tls_base", .global); const symbol = loc.getSymbol(wasm); @@ -522,14 +531,15 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { } pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { - const use_llvm = options.comp.config.use_llvm; - const output_mode = options.comp.config.output_mode; + const comp = options.comp; + const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; const wasm = try arena.create(Wasm); wasm.* = .{ .base = .{ .tag = .wasm, - .comp = options.comp, + .comp = comp, .emit = options.emit, .gc_sections = options.gc_sections orelse (output_mode != .Obj), .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB @@ -546,6 +556,13 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { .name = undefined, .import_table = options.import_table, .export_table = options.export_table, + .import_symbols = options.import_symbols, + .export_symbol_names = options.export_symbol_names, + .rdynamic = options.rdynamic, + .global_base = options.global_base, + .initial_memory = options.initial_memory, + .max_memory = options.max_memory, + .wasi_emulated_libs = options.wasi_emulated_libs, }; if (use_llvm) { @@ -909,7 +926,10 @@ fn writeI32Const(writer: anytype, val: u32) !void { } fn setupInitMemoryFunction(wasm: *Wasm) !void { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; // Passive segments are used to avoid memory being reinitialized on each // thread's instantiation. These passive segments are initialized and @@ -920,7 +940,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { return; } - const flag_address: u32 = if (wasm.base.options.shared_memory) address: { + const flag_address: u32 = if (shared_memory) address: { // when we have passive initialization segments and shared memory // `setupMemory` will create this symbol and set its virtual address. const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?; @@ -934,7 +954,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { // we have 0 locals try leb.writeULEB128(writer, @as(u32, 0)); - if (wasm.base.options.shared_memory) { + if (shared_memory) { // destination blocks // based on values we jump to corresponding label try writer.writeByte(std.wasm.opcode(.block)); // $drop @@ -968,14 +988,14 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { var segment_index: u32 = 0; while (it.next()) |entry| : (segment_index += 1) { const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { + if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { // For passive BSS segments we can simple issue a memory.fill(0). // For non-BSS segments we do a memory.init. Both these // instructions take as their first argument the destination // address. try writeI32Const(writer, segment.offset); - if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { + if (shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { // When we initialize the TLS segment we also set the `__tls_base` // global. This allows the runtime to use this static copy of the // TLS data for the first/main thread. @@ -1000,7 +1020,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { } } - if (wasm.base.options.shared_memory) { + if (shared_memory) { // we set the init memory flag to value '2' try writeI32Const(writer, flag_address); try writeI32Const(writer, 2); @@ -1043,12 +1063,12 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { while (it.next()) |entry| : (segment_index += 1) { const name = entry.key_ptr.*; const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, name) and + if (segment.needsPassiveInitialization(import_memory, name) and !std.mem.eql(u8, name, ".bss")) { // The TLS region should not be dropped since its is needed // during the initialization of each thread (__wasm_init_tls). - if (wasm.base.options.shared_memory and std.mem.eql(u8, name, ".tdata")) { + if (shared_memory and std.mem.eql(u8, name, ".tdata")) { continue; } @@ -1071,11 +1091,13 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { /// Constructs a synthetic function that performs runtime relocations for /// TLS symbols. This function is called by `__wasm_init_tls`. fn setupTLSRelocationsFunction(wasm: *Wasm) !void { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; // When we have TLS GOT entries and shared memory is enabled, // we must perform runtime relocations or else we don't create the function. - if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) { + if (!shared_memory or !wasm.requiresTLSReloc()) { return; } @@ -1119,7 +1141,9 @@ fn validateFeatures( to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool, emit_features_count: *u32, ) !void { - const target = wasm.base.comp.root_mod.resolved_target.result; + const comp = wasm.base.comp; + const target = comp.root_mod.resolved_target.result; + const shared_memory = comp.config.shared_memory; const cpu_features = target.cpu.features; const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects. const known_features_count = @typeInfo(types.Feature.Tag).Enum.fields.len; @@ -1190,7 +1214,7 @@ fn validateFeatures( return error.InvalidFeatureSet; } - if (wasm.base.options.shared_memory) { + if (shared_memory) { const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; if (@as(u1, @truncate(disallowed_feature)) != 0) { log.err( @@ -1255,7 +1279,9 @@ fn validateFeatures( /// if one or multiple undefined references exist. When none exist, the symbol will /// not be created, ensuring we don't unneccesarily emit unreferenced symbols. fn resolveLazySymbols(wasm: *Wasm) !void { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; if (wasm.string_table.getOffset("__heap_base")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { @@ -1273,7 +1299,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void { } } - if (!wasm.base.options.shared_memory) { + if (!shared_memory) { if (wasm.string_table.getOffset("__tls_base")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global); @@ -1306,8 +1332,9 @@ pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { } fn checkUndefinedSymbols(wasm: *const Wasm) !void { - if (wasm.base.comp.config.output_mode == .Obj) return; - if (wasm.base.options.import_symbols) return; + const comp = wasm.base.comp; + if (comp.config.output_mode == .Obj) return; + if (wasm.import_symbols) return; var found_undefined_symbols = false; for (wasm.undefs.values()) |undef| { @@ -2182,7 +2209,10 @@ const Kind = union(enum) { /// Parses an Atom and inserts its metadata into the corresponding sections. fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; const atom = wasm.getAtomPtr(atom_index); const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); const do_garbage_collect = wasm.base.gc_sections; @@ -2233,7 +2263,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { // we set the entire region of it to zeroes. // We do not have to do this when exporting the memory (the default) because the runtime // will do it for us, and we do not emit the bss segment at all. - if ((wasm.base.comp.config.output_mode == .Obj or wasm.base.options.import_memory) and kind.data == .uninitialized) { + if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) { @memset(atom.code.items, 0); } @@ -2250,7 +2280,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { } else { const index: u32 = @intCast(wasm.segments.items.len); var flags: u32 = 0; - if (wasm.base.options.shared_memory) { + if (shared_memory) { flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); } try wasm.segments.append(gpa, .{ @@ -2679,9 +2709,11 @@ fn setupStartSection(wasm: *Wasm) !void { } fn initializeTLSFunction(wasm: *Wasm) !void { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; - if (!wasm.base.options.shared_memory) return; + if (!shared_memory) return; var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); @@ -2940,7 +2972,7 @@ fn setupExports(wasm: *Wasm) !void { if (wasm.base.comp.config.output_mode == .Obj) return; log.debug("Building exports from symbols", .{}); - const force_exp_names = wasm.base.options.export_symbol_names; + const force_exp_names = wasm.export_symbol_names; if (force_exp_names.len > 0) { var failed_exports = false; @@ -2962,7 +2994,7 @@ fn setupExports(wasm: *Wasm) !void { for (wasm.resolved_symbols.keys()) |sym_loc| { const symbol = sym_loc.getSymbol(wasm); - if (!symbol.isExported(wasm.base.options.rdynamic)) continue; + if (!symbol.isExported(wasm.rdynamic)) continue; const sym_name = sym_loc.getName(wasm); const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: { @@ -2997,8 +3029,9 @@ fn setupExports(wasm: *Wasm) !void { } fn setupStart(wasm: *Wasm) !void { + const comp = wasm.base.comp; // do not export entry point if user set none or no default was set. - const entry_name = wasm.base.options.entry orelse return; + const entry_name = comp.config.entry orelse return; const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); @@ -3012,13 +3045,15 @@ fn setupStart(wasm: *Wasm) !void { } // Ensure the symbol is exported so host environment can access it - if (wasm.base.comp.config.output_mode != .Obj) { + if (comp.config.output_mode != .Obj) { symbol.setFlag(.WASM_SYM_EXPORTED); } } /// Sets up the memory section of the wasm module, as well as the stack. fn setupMemory(wasm: *Wasm) !void { + const comp = wasm.base.comp; + const shared_memory = comp.config.shared_memory; log.debug("Setting up memory layout", .{}); const page_size = std.wasm.page_size; // 64kb const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention @@ -3027,12 +3062,12 @@ fn setupMemory(wasm: *Wasm) !void { // Always place the stack at the start by default // unless the user specified the global-base flag var place_stack_first = true; - var memory_ptr: u64 = if (wasm.base.options.global_base) |base| blk: { + var memory_ptr: u64 = if (wasm.global_base) |base| blk: { place_stack_first = false; break :blk base; } else 0; - const is_obj = wasm.base.comp.config.output_mode == .Obj; + const is_obj = comp.config.output_mode == .Obj; if (place_stack_first and !is_obj) { memory_ptr = stack_alignment.forward(memory_ptr); @@ -3059,7 +3094,7 @@ fn setupMemory(wasm: *Wasm) !void { } if (wasm.findGlobalSymbol("__tls_base")) |loc| { const sym = loc.getSymbol(wasm); - wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory) + wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory) @as(i32, 0) else @as(i32, @intCast(memory_ptr)); @@ -3072,7 +3107,7 @@ fn setupMemory(wasm: *Wasm) !void { } // create the memory init flag which is used by the init memory function - if (wasm.base.options.shared_memory and wasm.hasPassiveInitializationSegments()) { + if (shared_memory and wasm.hasPassiveInitializationSegments()) { // align to pointer size memory_ptr = mem.alignForward(u64, memory_ptr, 4); const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); @@ -3098,7 +3133,7 @@ fn setupMemory(wasm: *Wasm) !void { // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 const max_memory_allowed: u64 = (1 << 32) - 1; - if (wasm.base.options.initial_memory) |initial_memory| { + if (wasm.initial_memory) |initial_memory| { if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { log.err("Initial memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -3124,7 +3159,7 @@ fn setupMemory(wasm: *Wasm) !void { symbol.virtual_address = @as(u32, @intCast(memory_ptr)); } - if (wasm.base.options.max_memory) |max_memory| { + if (wasm.max_memory) |max_memory| { if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { log.err("Maximum memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -3139,7 +3174,7 @@ fn setupMemory(wasm: *Wasm) !void { } wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); - if (wasm.base.options.shared_memory) { + if (shared_memory) { wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED); } log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); @@ -3150,20 +3185,22 @@ fn setupMemory(wasm: *Wasm) !void { /// index of the segment within the final data section. When the segment does not yet /// exist, a new one will be initialized and appended. The new index will be returned in that case. pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 { - const gpa = wasm.base.comp.gpa; + const comp = wasm.base.comp; + const gpa = comp.gpa; const object: Object = wasm.objects.items[object_index]; const symbol = object.symtable[symbol_index]; const index: u32 = @intCast(wasm.segments.items.len); + const shared_memory = comp.config.shared_memory; switch (symbol.tag) { .data => { const segment_info = object.segment_info[symbol.index]; - const merge_segment = wasm.base.comp.config.output_mode != .Obj; + const merge_segment = comp.config.output_mode != .Obj; const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); if (!result.found_existing) { result.value_ptr.* = index; var flags: u32 = 0; - if (wasm.base.options.shared_memory) { + if (shared_memory) { flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); } try wasm.segments.append(gpa, .{ @@ -3445,6 +3482,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l defer tracy.end(); const gpa = wasm.base.comp.gpa; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; // Used for all temporary memory allocated during flushin var arena_instance = std.heap.ArenaAllocator.init(gpa); @@ -3509,8 +3548,8 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l man.hash.addOptionalBytes(wasm.base.comp.config.entry); man.hash.add(wasm.base.stack_size); man.hash.add(wasm.base.build_id); - man.hash.add(wasm.base.comp.config.import_memory); - man.hash.add(wasm.base.comp.config.shared_memory); + man.hash.add(import_memory); + man.hash.add(shared_memory); man.hash.add(wasm.import_table); man.hash.add(wasm.export_table); man.hash.addOptional(wasm.initial_memory); @@ -3726,8 +3765,12 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) { // for safe build modes, we store the atom in the data segment, // whereas for unsafe build modes we store it in bss. - const is_initialized = wasm.base.options.optimize_mode == .Debug or - wasm.base.options.optimize_mode == .ReleaseSafe; + const decl_namespace = mod.namespacePtr(decl.src_namespace); + const optimize_mode = decl_namespace.file_scope.mod.optimize_mode; + const is_initialized = switch (optimize_mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized }); } else { // when the decl is all zeroes, we store the atom in the bss segment, @@ -3788,9 +3831,13 @@ fn writeToFile( feature_count: u32, arena: Allocator, ) !void { - const gpa = wasm.base.comp.gpa; - const use_llvm = wasm.base.comp.config.use_llvm; - const use_lld = build_options.have_llvm and wasm.base.comp.config.use_lld; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const use_llvm = comp.config.use_llvm; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; + const export_memory = comp.config.export_memory; // Size of each section header const header_size = 5 + 1; @@ -3800,7 +3847,7 @@ fn writeToFile( var code_section_index: ?u32 = null; // Index of the data section. Used to tell relocation table where the section lives. var data_section_index: ?u32 = null; - const is_obj = wasm.base.comp.config.output_mode == .Obj or (!use_llvm and use_lld); + const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld); var binary_bytes = std.ArrayList(u8).init(gpa); defer binary_bytes.deinit(); @@ -3840,8 +3887,6 @@ fn writeToFile( } // Import section - const import_memory = wasm.base.options.import_memory or is_obj; - const export_memory = wasm.base.options.export_memory; if (wasm.imports.count() != 0 or import_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); @@ -4018,7 +4063,7 @@ fn writeToFile( // When the shared-memory option is enabled, we *must* emit the 'data count' section. const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory); - if (data_segments_count != 0 and wasm.base.options.shared_memory) { + if (data_segments_count != 0 and shared_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); try writeVecSectionHeader( binary_bytes.items, @@ -4160,11 +4205,11 @@ fn writeToFile( if (data_section_index) |data_index| { try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); } - } else if (!wasm.base.options.strip) { + } else if (wasm.base.debug_format != .strip) { try wasm.emitNameSection(&binary_bytes, arena); } - if (!wasm.base.options.strip) { + if (wasm.base.debug_format != .strip) { // The build id must be computed on the main sections only, // so we have to do it now, before the debug sections. switch (wasm.base.build_id) { @@ -4193,7 +4238,7 @@ fn writeToFile( } // if (wasm.dwarf) |*dwarf| { - // const mod = wasm.base.comp.module.?; + // const mod = comp.module.?; // try dwarf.writeDbgAbbrev(); // // for debug info and ranges, the address is always 0, // // as locations are always offsets relative to 'code' section. @@ -4380,6 +4425,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con } fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { + const comp = wasm.base.comp; + const import_memory = comp.config.import_memory; const Name = struct { index: u32, name: []const u8, @@ -4418,7 +4465,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem for (wasm.data_segments.keys()) |key| { // bss section is not emitted when this condition holds true, so we also // do not output a name for it. - if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; + if (!import_memory and std.mem.eql(u8, key, ".bss")) continue; segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); data_segment_index += 1; } @@ -4528,6 +4575,11 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const tracy = trace(@src()); defer tracy.end(); + const shared_memory = comp.config.shared_memory; + const export_memory = comp.config.export_memory; + const import_memory = comp.config.import_memory; + const target = comp.root_mod.resolved_target.result; + const gpa = wasm.base.comp.gpa; var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); @@ -4560,8 +4612,6 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! break :blk null; }; - const target = wasm.base.comp.root_mod.resolved_target.result; - const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; @@ -4577,7 +4627,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! comptime assert(Compilation.link_hash_implementation_version == 10); - for (wasm.base.options.objects) |obj| { + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -4589,17 +4639,17 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! man.hash.addOptionalBytes(wasm.base.comp.config.entry); man.hash.add(wasm.base.stack_size); man.hash.add(wasm.base.build_id); - man.hash.add(wasm.base.options.import_memory); - man.hash.add(wasm.base.options.export_memory); + man.hash.add(import_memory); + man.hash.add(export_memory); man.hash.add(wasm.import_table); man.hash.add(wasm.export_table); - man.hash.addOptional(wasm.base.options.initial_memory); - man.hash.addOptional(wasm.base.options.max_memory); - man.hash.add(wasm.base.options.shared_memory); - man.hash.addOptional(wasm.base.options.global_base); - man.hash.add(wasm.base.options.export_symbol_names.len); + man.hash.addOptional(wasm.initial_memory); + man.hash.addOptional(wasm.max_memory); + man.hash.add(shared_memory); + man.hash.addOptional(wasm.global_base); + man.hash.add(wasm.export_symbol_names.len); // strip does not need to go into the linker hash because it is part of the hash namespace - for (wasm.base.options.export_symbol_names) |symbol_name| { + for (wasm.export_symbol_names) |symbol_name| { man.hash.addBytes(symbol_name); } @@ -4637,8 +4687,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (wasm.base.options.objects.len != 0) - break :blk wasm.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -4666,19 +4716,19 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); try argv.append("--error-limit=0"); - if (wasm.base.options.lto) { - switch (wasm.base.options.optimize_mode) { + if (comp.config.lto) { + switch (comp.root_mod.optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("-O2"), .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), } } - if (wasm.base.options.import_memory) { + if (import_memory) { try argv.append("--import-memory"); } - if (wasm.base.options.export_memory) { + if (export_memory) { try argv.append("--export-memory"); } @@ -4698,25 +4748,25 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append("--no-gc-sections"); } - if (wasm.base.options.strip) { + if (wasm.base.debug_format == .strip) { try argv.append("-s"); } - if (wasm.base.options.initial_memory) |initial_memory| { + if (wasm.initial_memory) |initial_memory| { const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); try argv.append(arg); } - if (wasm.base.options.max_memory) |max_memory| { + if (wasm.max_memory) |max_memory| { const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); try argv.append(arg); } - if (wasm.base.options.shared_memory) { + if (shared_memory) { try argv.append("--shared-memory"); } - if (wasm.base.options.global_base) |global_base| { + if (wasm.global_base) |global_base| { const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); try argv.append(arg); } else { @@ -4728,16 +4778,16 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } // Users are allowed to specify which symbols they want to export to the wasm host. - for (wasm.base.options.export_symbol_names) |symbol_name| { + for (wasm.export_symbol_names) |symbol_name| { const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); } - if (wasm.base.options.rdynamic) { + if (wasm.rdynamic) { try argv.append("--export-dynamic"); } - if (wasm.base.options.entry) |entry| { + if (comp.config.entry) |entry| { try argv.append("--entry"); try argv.append(entry); } else { @@ -4749,14 +4799,14 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}), }); - if (wasm.base.options.import_symbols) { + if (wasm.import_symbols) { try argv.append("--allow-undefined"); } if (wasm.base.comp.config.output_mode == .Lib and wasm.base.comp.config.link_mode == .Dynamic) { try argv.append("--shared"); } - if (wasm.base.options.pie) { + if (comp.config.pie) { try argv.append("--pie"); } @@ -4782,15 +4832,15 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! )); } - if (wasm.base.options.link_libc) { + if (comp.config.link_libc) { try argv.append(try comp.get_libc_crt_file( arena, - wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), + wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), )); try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); } - if (wasm.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(comp.libcxxabi_static_lib.?.full_object_path); } @@ -4799,7 +4849,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // Positional arguments to the linker such as object files. var whole_archive = false; - for (wasm.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -4822,8 +4872,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } if (wasm.base.comp.config.output_mode != .Obj and - !wasm.base.options.skip_linker_dependencies and - !wasm.base.options.link_libc) + !comp.skip_linker_dependencies and + !comp.config.link_libc) { try argv.append(comp.libc_static_lib.?.full_object_path); } @@ -5168,10 +5218,13 @@ fn emitDataRelocations( } fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { + const comp = wasm.base.comp; + const import_memory = comp.config.import_memory; + var it = wasm.data_segments.iterator(); while (it.next()) |entry| { const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { + if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { return true; } } @@ -5227,14 +5280,14 @@ fn markReferences(wasm: *Wasm) !void { for (wasm.resolved_symbols.keys()) |sym_loc| { const sym = sym_loc.getSymbol(wasm); - if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { + if (sym.isExported(wasm.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { try wasm.mark(sym_loc); continue; } // Debug sections may require to be parsed and marked when it contains // relocations to alive symbols. - if (sym.tag == .section and !wasm.base.options.strip) { + if (sym.tag == .section and wasm.base.debug_format != .strip) { const file = sym_loc.file orelse continue; // Incremental debug info is done independently const object = &wasm.objects.items[file]; const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm); -- cgit v1.2.3 From f54471b54c471bb6f8e51a1383be09d01c24d0c3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Dec 2023 16:41:20 -0700 Subject: compiler: miscellaneous branch progress implement builtin.zig file population for all modules rather than assuming there is only one global builtin.zig module. move some fields from link.File to Compilation move some fields from Module to Compilation compute debug_format in global Compilation config resolution wire up C compilation to the concept of owner modules make whole cache mode call link.File.createEmpty() instead of link.File.open() --- src/Builtin.zig | 62 ++++++++++++++- src/Compilation.zig | 183 +++++++++++++++++++++++++-------------------- src/Compilation/Config.zig | 31 ++++++++ src/Module.zig | 69 ----------------- src/Package/Module.zig | 31 ++++++-- src/Sema.zig | 7 +- src/codegen/llvm.zig | 16 +--- src/libunwind.zig | 3 +- src/link.zig | 41 +++++----- src/link/C.zig | 26 +++---- src/link/Coff.zig | 42 ++++++----- src/link/Coff/lld.zig | 2 +- src/link/Elf.zig | 48 ++++++------ src/link/Elf/Object.zig | 3 +- src/link/Elf/ZigObject.zig | 12 ++- src/link/MachO.zig | 39 +++++----- src/link/NvPtx.zig | 33 ++++---- src/link/Plan9.zig | 36 +++++---- src/link/SpirV.zig | 35 +++++---- src/link/Wasm.zig | 41 +++++----- src/main.zig | 33 ++++---- 21 files changed, 457 insertions(+), 336 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Builtin.zig b/src/Builtin.zig index 7224a6fd24..4c8038019c 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -20,8 +20,11 @@ wasi_exec_model: std.builtin.WasiExecModel, pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 { var buffer = std.ArrayList(u8).init(allocator); - defer buffer.deinit(); + try append(opts, &buffer); + return buffer.toOwnedSliceSentinel(0); +} +pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { const target = opts.target; const generic_arch_name = target.cpu.arch.genericName(); const zig_backend = opts.zig_backend; @@ -231,10 +234,65 @@ pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 { ); } } +} - return buffer.toOwnedSliceSentinel(0); +pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void { + assert(file.source_loaded == true); + + if (mod.root.statFile(mod.root_src_path)) |stat| { + if (stat.size != file.source.len) { + std.log.warn( + "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++ + "Overwriting with correct file contents now", + .{ mod.root, mod.root_src_path, file.source.len, stat.size }, + ); + + try writeFile(file, mod); + } else { + file.stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }; + } + } else |err| switch (err) { + error.BadPathName => unreachable, // it's always "builtin.zig" + error.NameTooLong => unreachable, // it's always "builtin.zig" + error.PipeBusy => unreachable, // it's not a pipe + error.WouldBlock => unreachable, // not asking for non-blocking I/O + + error.FileNotFound => try writeFile(file, mod), + + else => |e| return e, + } + + file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig); + file.tree_loaded = true; + assert(file.tree.errors.len == 0); // builtin.zig must parse + + file.zir = try AstGen.generate(comp.gpa, file.tree); + file.zir_loaded = true; + file.status = .success_zir; +} + +fn writeFile(file: *File, mod: *Module) !void { + var af = try mod.root.atomicFile(mod.root_src_path, .{}); + defer af.deinit(); + try af.file.writeAll(file.source); + try af.finish(); + + file.stat = .{ + .size = file.source.len, + .inode = 0, // dummy value + .mtime = 0, // dummy value + }; } const std = @import("std"); const Allocator = std.mem.Allocator; const build_options = @import("build_options"); +const Module = @import("Package/Module.zig"); +const assert = std.debug.assert; +const AstGen = @import("AstGen.zig"); +const File = @import("Module.zig").File; +const Compilation = @import("Compilation.zig"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 8cf7a66fbc..35ee243617 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -37,6 +37,7 @@ const Zir = @import("Zir.zig"); const Autodoc = @import("Autodoc.zig"); const Color = @import("main.zig").Color; const resinator = @import("resinator.zig"); +const Builtin = @import("Builtin.zig"); pub const Config = @import("Compilation/Config.zig"); @@ -59,7 +60,10 @@ root_mod: *Package.Module, /// User-specified settings that have all the defaults resolved into concrete values. config: Config, -/// This is `null` when `-fno-emit-bin` is used. +/// The main output file. +/// In whole cache mode, this is null except for during the body of the update +/// function. In incremental cache mode, this is a long-lived object. +/// In both cases, this is `null` when `-fno-emit-bin` is used. bin_file: ?*link.File, /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) @@ -80,6 +84,8 @@ version: ?std.SemanticVersion, libc_installation: ?*const LibCInstallation, skip_linker_dependencies: bool, no_builtin: bool, +function_sections: bool, +data_sections: bool, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = @@ -120,7 +126,6 @@ failed_win32_resources: if (build_options.only_core_functionality) void else std /// Miscellaneous things that can fail. misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, -keep_source_files_loaded: bool, /// When this is `true` it means invoking clang as a sub-process is expected to inherit /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. /// Otherwise we attempt to parse the error messages and expose them via the Compilation API. @@ -144,6 +149,7 @@ debug_compiler_runtime_libs: bool, debug_compile_errors: bool, job_queued_compiler_rt_lib: bool = false, job_queued_compiler_rt_obj: bool = false, +job_queued_update_builtin_zig: bool, alloc_failure_occurred: bool = false, formatted_panics: bool = false, last_update_was_cache_hit: bool = false, @@ -814,13 +820,13 @@ pub const cache_helpers = struct { addEmitLoc(hh, optional_emit_loc orelse return); } - pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?link.File.DebugFormat) void { + pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void { hh.add(x != null); addDebugFormat(hh, x orelse return); } - pub fn addDebugFormat(hh: *Cache.HashHelper, x: link.File.DebugFormat) void { - const tag: @typeInfo(link.File.DebugFormat).Union.tag_type.? = x; + pub fn addDebugFormat(hh: *Cache.HashHelper, x: Config.DebugFormat) void { + const tag: @typeInfo(Config.DebugFormat).Union.tag_type.? = x; hh.add(tag); switch (x) { .strip, .code_view => {}, @@ -860,11 +866,11 @@ pub const SystemLib = link.SystemLib; pub const CacheMode = enum { incremental, whole }; -pub const CacheUse = union(CacheMode) { +const CacheUse = union(CacheMode) { incremental: *Incremental, whole: *Whole, - pub const Whole = struct { + const Whole = struct { /// This is a pointer to a local variable inside `update()`. cache_manifest: ?*Cache.Manifest = null, cache_manifest_mutex: std.Thread.Mutex = .{}, @@ -873,12 +879,14 @@ pub const CacheUse = union(CacheMode) { /// of exactly the correct size for "o/[digest]/[basename]". /// The basename is of the outputted binary file in case we don't know the directory yet. bin_sub_path: ?[]u8, - /// Same as `whole_bin_sub_path` but for implibs. + /// Same as `bin_sub_path` but for implibs. implib_sub_path: ?[]u8, docs_sub_path: ?[]u8, + lf_open_opts: link.File.OpenOptions, + tmp_artifact_directory: ?Cache.Directory, }; - pub const Incremental = struct { + const Incremental = struct { /// Where build artifacts and incremental compilation metadata serialization go. artifact_directory: Compilation.Directory, }; @@ -937,7 +945,6 @@ pub const InitOptions = struct { /// this flag would be set to disable this machinery to avoid false positives. disable_lld_caching: bool = false, cache_mode: CacheMode = .incremental, - keep_source_files_loaded: bool = false, lib_dirs: []const []const u8 = &[0][]const u8{}, rpath_list: []const []const u8 = &[0][]const u8{}, symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{}, @@ -1040,7 +1047,6 @@ pub const InitOptions = struct { test_name_prefix: ?[]const u8 = null, test_runner_path: ?[]const u8 = null, subsystem: ?std.Target.SubSystem = null, - debug_format: ?link.File.DebugFormat = null, /// (Zig compiler development) Enable dumping linker's state as JSON. enable_link_snapshots: bool = false, /// (Darwin) Install name of the dylib @@ -1327,7 +1333,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { cache.hash.add(options.config.link_libcpp); cache.hash.add(options.config.link_libunwind); cache.hash.add(output_mode); - cache_helpers.addOptionalDebugFormat(&cache.hash, options.debug_format); + cache_helpers.addDebugFormat(&cache.hash, comp.config.debug_format); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs); @@ -1380,7 +1386,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; errdefer if (opt_zcu) |zcu| zcu.deinit(); - const system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init( + var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init( gpa, options.system_lib_names, options.system_lib_infos, @@ -1409,7 +1415,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa), .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), - .keep_source_files_loaded = options.keep_source_files_loaded, .c_source_files = options.c_source_files, .rc_source_files = options.rc_source_files, .cache_parent = cache, @@ -1451,10 +1456,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit, .skip_linker_dependencies = options.skip_linker_dependencies, .no_builtin = options.no_builtin, + .job_queued_update_builtin_zig = have_zcu, + .function_sections = options.function_sections, + .data_sections = options.data_sections, }; const lf_open_opts: link.File.OpenOptions = .{ - .comp = comp, .linker_script = options.linker_script, .z_nodelete = options.linker_z_nodelete, .z_notext = options.linker_z_notext, @@ -1471,8 +1478,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .lib_dirs = options.lib_dirs, .rpath_list = options.rpath_list, .symbol_wrap_set = options.symbol_wrap_set, - .function_sections = options.function_sections, - .data_sections = options.data_sections, .allow_shlib_undefined = options.linker_allow_shlib_undefined, .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, .compress_debug_sections = options.linker_compress_debug_sections orelse .none, @@ -1507,7 +1512,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .build_id = build_id, .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .subsystem = options.subsystem, - .debug_format = options.debug_format, .hash_style = options.hash_style, .enable_link_snapshots = options.enable_link_snapshots, .install_name = options.install_name, @@ -1572,17 +1576,17 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .directory = emit_bin.directory orelse artifact_directory, .sub_path = emit_bin.basename, }; - comp.bin_file = try link.File.open(arena, emit, lf_open_opts); + comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); } - if (options.implib_emit) |emit_implib| { + if (options.emit_implib) |emit_implib| { comp.implib_emit = .{ .directory = emit_implib.directory orelse artifact_directory, .sub_path = emit_implib.basename, }; } - if (options.docs_emit) |emit_docs| { + if (options.emit_docs) |emit_docs| { comp.docs_emit = .{ .directory = emit_docs.directory orelse artifact_directory, .sub_path = emit_docs.basename, @@ -1610,6 +1614,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin), .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib), .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs), + .tmp_artifact_directory = null, }; comp.cache_use = .{ .whole = whole }; }, @@ -1662,7 +1667,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - const have_bin_emit = comp.bin_file != null or comp.whole_bin_sub_path != null; + const have_bin_emit = switch (comp.cache_use) { + .whole => |whole| whole.bin_sub_path != null, + .incremental => comp.bin_file != null, + }; if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) { if (target.isDarwin()) { @@ -1814,8 +1822,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { pub fn destroy(self: *Compilation) void { if (self.bin_file) |lf| lf.destroy(); if (self.module) |zcu| zcu.deinit(); + switch (self.cache_use) { + .incremental => |incremental| { + incremental.artifact_directory.handle.close(); + }, + .whole => {}, + } - const gpa = self.gpa; self.work_queue.deinit(); self.anon_work_queue.deinit(); self.c_object_work_queue.deinit(); @@ -1825,6 +1838,9 @@ pub fn destroy(self: *Compilation) void { self.astgen_work_queue.deinit(); self.embed_file_work_queue.deinit(); + const gpa = self.gpa; + self.system_libs.deinit(gpa); + { var it = self.crt_files.iterator(); while (it.next()) |entry| { @@ -1914,7 +1930,7 @@ pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.C } fn cleanupAfterUpdate(comp: *Compilation) void { - switch (comp) { + switch (comp.cache_use) { .incremental => return, .whole => |whole| { if (whole.cache_manifest) |man| { @@ -1971,7 +1987,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); const digest = man.final(); - comp.wholeCacheModeSetBinFilePath(&digest); + comp.wholeCacheModeSetBinFilePath(whole, &digest); assert(comp.bin_file.lock == null); comp.bin_file.lock = man.toOwnedLock(); @@ -2001,21 +2017,21 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void // Now that the directory is known, it is time to create the Emit // objects and call link.File.open. - if (comp.whole_implib_sub_path) |sub_path| { + if (whole.implib_sub_path) |sub_path| { comp.implib_emit = .{ .directory = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } - if (comp.whole_docs_sub_path) |sub_path| { + if (whole.docs_sub_path) |sub_path| { comp.docs_emit = .{ .directory = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } - if (comp.whole_bin_sub_path) |sub_path| { + if (whole.bin_sub_path) |sub_path| { const emit: Emit = .{ .directory = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), @@ -2024,7 +2040,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void // but in practice it won't leak much and usually whole cache mode // will be combined with exactly one call to update(). const arena = comp.arena.allocator(); - comp.bin_file = try link.File.open(arena, emit, whole.lf_open_opts); + comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); } }, .incremental => {}, @@ -2158,7 +2174,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void const o_sub_path = "o" ++ s ++ digest; try renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); - comp.wholeCacheModeSetBinFilePath(&digest); + comp.wholeCacheModeSetBinFilePath(whole, &digest); // Failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { @@ -2170,19 +2186,6 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void }, .incremental => {}, } - - // Unload all source files to save memory. - // The ZIR needs to stay loaded in memory because (1) Decl objects contain references - // to it, and (2) generic instantiations, comptime calls, inline calls will need - // to reference the ZIR. - if (!comp.keep_source_files_loaded) { - if (comp.module) |module| { - for (module.import_table.values()) |file| { - file.unloadTree(comp.gpa); - file.unloadSource(comp.gpa); - } - } - } } /// This function is called by the frontend before flush(). It communicates that @@ -2274,10 +2277,14 @@ fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void { } /// Communicate the output binary location to parent Compilations. -fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_digest_len]u8) void { +fn wholeCacheModeSetBinFilePath( + comp: *Compilation, + whole: *CacheUse.Whole, + digest: *const [Cache.hex_digest_len]u8, +) void { const digest_start = 2; // "o/[digest]/[basename]" - if (comp.whole_bin_sub_path) |sub_path| { + if (whole.bin_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.bin_file.?.emit = .{ @@ -2286,7 +2293,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di }; } - if (comp.whole_implib_sub_path) |sub_path| { + if (whole.implib_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.implib_emit = .{ @@ -2295,7 +2302,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di }; } - if (comp.whole_docs_sub_path) |sub_path| { + if (whole.docs_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.docs_emit = .{ @@ -3232,13 +3239,25 @@ pub fn performAllTheWork( // 1. to avoid race condition of zig processes truncating each other's builtin.zig files // 2. optimization; in the hot path it only incurs a stat() syscall, which happens // in the `astgen_wait_group`. - if (comp.module) |mod| { - if (mod.job_queued_update_builtin_zig) { - mod.job_queued_update_builtin_zig = false; + if (comp.job_queued_update_builtin_zig) b: { + comp.job_queued_update_builtin_zig = false; + const zcu = comp.module orelse break :b; + _ = zcu; + // TODO put all the modules in a flat array to make them easy to iterate. + var seen: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + defer seen.deinit(comp.gpa); + try seen.put(comp.gpa, comp.root_mod); + var i: usize = 0; + while (i < seen.count()) : (i += 1) { + const mod = seen.keys()[i]; + for (mod.deps.values()) |dep| + try seen.put(comp.gpa, dep); + + const file = mod.builtin_file orelse continue; comp.astgen_wait_group.start(); try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ - comp, mod, &comp.astgen_wait_group, + comp, mod, file, &comp.astgen_wait_group, }); } } @@ -3702,19 +3721,17 @@ fn workerAstGenFile( fn workerUpdateBuiltinZigFile( comp: *Compilation, - mod: *Module, + mod: *Package.Module, + file: *Module.File, wg: *WaitGroup, ) void { defer wg.finish(); - - mod.populateBuiltinFile() catch |err| { - const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse "."; - + Builtin.populateFile(comp, mod, file) catch |err| { comp.mutex.lock(); defer comp.mutex.unlock(); - comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{ - dir_path, @errorName(err), + comp.setMiscFailure(.write_builtin_zig, "unable to write '{}{s}': {s}", .{ + mod.root, mod.root_src_path, @errorName(err), }); }; } @@ -3755,14 +3772,17 @@ fn detectEmbedFileUpdate(comp: *Compilation, embed_file: *Module.EmbedFile) !voi @panic("TODO: handle embed file incremental update"); } -pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { +pub fn obtainCObjectCacheManifest( + comp: *const Compilation, + owner_mod: *Package.Module, +) Cache.Manifest { var man = comp.cache_parent.obtain(); // Only things that need to be added on top of the base hash, and only things // that apply both to @cImport and compiling C objects. No linking stuff here! // Also nothing that applies only to compiling .zig code. - man.hash.add(comp.sanitize_c); - man.hash.addListOfBytes(comp.clang_argv); + man.hash.add(owner_mod.sanitize_c); + man.hash.addListOfBytes(owner_mod.clang_argv); man.hash.add(comp.config.link_libcpp); // When libc_installation is null it means that Zig generated this dir list @@ -3797,19 +3817,19 @@ pub const CImportResult = struct { /// Caller owns returned memory. /// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked /// a bit when we want to start using it from self-hosted. -pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { +pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult { if (build_options.only_core_functionality) @panic("@cImport is not available in a zig2.c build"); const tracy_trace = trace(@src()); defer tracy_trace.end(); const cimport_zig_basename = "cimport.zig"; - var man = comp.obtainCObjectCacheManifest(); + var man = comp.obtainCObjectCacheManifest(owner_mod); defer man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects man.hash.addBytes(c_src); - man.hash.add(comp.c_frontend); + man.hash.add(comp.config.c_frontend); // If the previous invocation resulted in clang errors, we will see a hit // here with 0 files in the manifest, in which case it is actually a miss. @@ -3846,15 +3866,15 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { var argv = std.ArrayList([]const u8).init(comp.gpa); defer argv.deinit(); - try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1] - try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path); + try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1] + try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod); try argv.append(out_h_path); if (comp.verbose_cc) { dump_argv(argv.items); } - var tree = switch (comp.c_frontend) { + var tree = switch (comp.config.c_frontend) { .aro => tree: { const translate_c = @import("aro_translate_c.zig"); _ = translate_c; @@ -4119,7 +4139,7 @@ fn reportRetryableEmbedFileError( } fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void { - if (comp.c_frontend == .aro) { + if (comp.config.c_frontend == .aro) { return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{}); } if (!build_options.have_llvm) { @@ -4142,7 +4162,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P _ = comp.failed_c_objects.swapRemove(c_object); } - var man = comp.obtainCObjectCacheManifest(); + var man = comp.obtainCObjectCacheManifest(c_object.src.owner); defer man.deinit(); man.hash.add(comp.clang_preprocessor_mode); @@ -4219,7 +4239,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P if (std.process.can_execv and direct_o and comp.disable_c_depfile and comp.clang_passthrough_mode) { - try comp.addCCArgs(arena, &argv, ext, null); + try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner); try argv.appendSlice(c_object.src.extra_flags); try argv.appendSlice(c_object.src.cache_exempt_flags); @@ -4262,7 +4282,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P null else try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); - try comp.addCCArgs(arena, &argv, ext, out_dep_path); + try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner); try argv.appendSlice(c_object.src.extra_flags); try argv.appendSlice(c_object.src.cache_exempt_flags); @@ -4610,7 +4630,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 // mode. While these defines are not normally present when calling rc.exe directly, // them being defined matches the behavior of how MSVC calls rc.exe which is the more // relevant behavior in this case. - try comp.addCCArgs(arena, &argv, .rc, out_dep_path); + try comp.addCCArgs(arena, &argv, .rc, out_dep_path, rc_src.owner); if (comp.verbose_cc) { dump_argv(argv.items); @@ -4788,11 +4808,12 @@ pub fn addTranslateCCArgs( argv: *std.ArrayList([]const u8), ext: FileExt, out_dep_path: ?[]const u8, + owner_mod: *Package.Module, ) !void { - try argv.appendSlice(&[_][]const u8{ "-x", "c" }); - try comp.addCCArgs(arena, argv, ext, out_dep_path); + try argv.appendSlice(&.{ "-x", "c" }); + try comp.addCCArgs(arena, argv, ext, out_dep_path, owner_mod); // This gives us access to preprocessing entities, presumably at the cost of performance. - try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" }); + try argv.appendSlice(&.{ "-Xclang", "-detailed-preprocessing-record" }); } /// Add common C compiler args between translate-c and C object compilation. @@ -4825,11 +4846,11 @@ pub fn addCCArgs( try argv.append("-fno-caret-diagnostics"); } - if (comp.bin_file.function_sections) { + if (comp.function_sections) { try argv.append("-ffunction-sections"); } - if (comp.bin_file.data_sections) { + if (comp.data_sections) { try argv.append("-fdata-sections"); } @@ -5088,7 +5109,7 @@ pub fn addCCArgs( try argv.append("-fPIC"); } - if (comp.unwind_tables) { + if (mod.unwind_tables) { try argv.append("-funwind-tables"); } else { try argv.append("-fno-unwind-tables"); @@ -5174,7 +5195,7 @@ pub fn addCCArgs( } try argv.ensureUnusedCapacity(2); - switch (comp.bin_file.debug_format) { + switch (comp.config.debug_format) { .strip => {}, .code_view => { // -g is required here because -gcodeview doesn't trigger debug info @@ -5210,7 +5231,7 @@ pub fn addCCArgs( try argv.append("-ffreestanding"); } - try argv.appendSlice(comp.clang_argv); + try argv.appendSlice(mod.cc_argv); } fn failCObj( @@ -6094,6 +6115,7 @@ fn buildOutputFromZig( .have_zcu = true, .emit_bin = true, .root_optimize_mode = comp.compilerRtOptMode(), + .root_strip = comp.compilerRtStrip(), .link_libc = comp.config.link_libc, .any_unwind_tables = unwind_tables, }); @@ -6198,6 +6220,7 @@ pub fn build_crt_file( .have_zcu = false, .emit_bin = true, .root_optimize_mode = comp.compilerRtOptMode(), + .root_strip = comp.compilerRtStrip(), .link_libc = false, .lto = switch (output_mode) { .Lib => comp.config.lto, diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index aba21e4bfe..0afb633c86 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -33,9 +33,16 @@ shared_memory: bool, is_test: bool, test_evented_io: bool, entry: ?[]const u8, +debug_format: DebugFormat, pub const CFrontend = enum { clang, aro }; +pub const DebugFormat = union(enum) { + strip, + dwarf: std.dwarf.Format, + code_view, +}; + pub const Options = struct { output_mode: std.builtin.OutputMode, resolved_target: Module.ResolvedTarget, @@ -43,6 +50,7 @@ pub const Options = struct { have_zcu: bool, emit_bin: bool, root_optimize_mode: ?std.builtin.OptimizeMode = null, + root_strip: ?bool = null, link_mode: ?std.builtin.LinkMode = null, ensure_libc_on_non_freestanding: bool = false, ensure_libcpp_on_non_freestanding: bool = false, @@ -51,6 +59,7 @@ pub const Options = struct { any_unwind_tables: bool = false, any_dyn_libs: bool = false, any_c_source_files: bool = false, + any_non_stripped: bool = false, emit_llvm_ir: bool = false, emit_llvm_bc: bool = false, link_libc: ?bool = null, @@ -74,6 +83,7 @@ pub const Options = struct { export_memory: ?bool = null, shared_memory: ?bool = null, test_evented_io: bool = false, + debug_format: ?Config.DebugFormat = null, }; pub fn resolve(options: Options) !Config { @@ -365,6 +375,26 @@ pub fn resolve(options: Options) !Config { break :b false; }; + const root_strip = b: { + if (options.root_strip) |x| break :b x; + if (root_optimize_mode == .ReleaseSmall) break :b true; + if (!target_util.hasDebugInfo(target)) break :b true; + break :b false; + }; + + const debug_format: DebugFormat = b: { + if (root_strip and !options.any_non_stripped) break :b .strip; + break :b switch (target.ofmt) { + .elf, .macho, .wasm => .{ .dwarf = .@"32" }, + .coff => .code_view, + .c => switch (target.os.tag) { + .windows, .uefi => .code_view, + else => .{ .dwarf = .@"32" }, + }, + .spirv, .nvptx, .dxcontainer, .hex, .raw, .plan9 => .strip, + }; + }; + return .{ .output_mode = options.output_mode, .have_zcu = options.have_zcu, @@ -388,6 +418,7 @@ pub fn resolve(options: Options) !Config { .use_lld = use_lld, .entry = entry, .wasi_exec_model = wasi_exec_model, + .debug_format = debug_format, }; } diff --git a/src/Module.zig b/src/Module.zig index e32bad7295..121f639f6b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -152,8 +152,6 @@ stage1_flags: packed struct { reserved: u2 = 0, } = .{}, -job_queued_update_builtin_zig: bool = true, - compile_log_text: ArrayListUnmanaged(u8) = .{}, emit_h: ?*GlobalEmitH, @@ -2490,7 +2488,6 @@ pub fn deinit(mod: *Module) void { mod.compile_log_text.deinit(gpa); - mod.zig_cache_artifact_directory.handle.close(); mod.local_zir_cache.handle.close(); mod.global_zir_cache.handle.close(); @@ -3075,72 +3072,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { } } -pub fn populateBuiltinFile(mod: *Module) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const comp = mod.comp; - const builtin_mod, const file = blk: { - comp.mutex.lock(); - defer comp.mutex.unlock(); - - const builtin_mod = mod.main_mod.deps.get("builtin").?; - const result = try mod.importPkg(builtin_mod); - break :blk .{ builtin_mod, result.file }; - }; - const gpa = mod.gpa; - file.source = try comp.generateBuiltinZigSource(gpa); - file.source_loaded = true; - - if (builtin_mod.root.statFile(builtin_mod.root_src_path)) |stat| { - if (stat.size != file.source.len) { - log.warn( - "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++ - "Overwriting with correct file contents now", - .{ builtin_mod.root, builtin_mod.root_src_path, file.source.len, stat.size }, - ); - - try writeBuiltinFile(file, builtin_mod); - } else { - file.stat = .{ - .size = stat.size, - .inode = stat.inode, - .mtime = stat.mtime, - }; - } - } else |err| switch (err) { - error.BadPathName => unreachable, // it's always "builtin.zig" - error.NameTooLong => unreachable, // it's always "builtin.zig" - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O - - error.FileNotFound => try writeBuiltinFile(file, builtin_mod), - - else => |e| return e, - } - - file.tree = try Ast.parse(gpa, file.source, .zig); - file.tree_loaded = true; - assert(file.tree.errors.len == 0); // builtin.zig must parse - - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - file.status = .success_zir; -} - -fn writeBuiltinFile(file: *File, builtin_mod: *Package.Module) !void { - var af = try builtin_mod.root.atomicFile(builtin_mod.root_src_path, .{}); - defer af.deinit(); - try af.file.writeAll(file.source); - try af.finish(); - - file.stat = .{ - .size = file.source.len, - .inode = 0, // dummy value - .mtime = 0, // dummy value - }; -} - pub fn mapOldZirToNew( gpa: Allocator, old_zir: Zir, diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 0916742876..051303ca26 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -33,11 +33,16 @@ cc_argv: []const []const u8, /// (SPIR-V) whether to generate a structured control flow graph or not structured_cfg: bool, -/// The contents of `@import("builtin")` for this module. -generated_builtin_source: []const u8, +/// If the module is an `@import("builtin")` module, this is the `File` that +/// is preallocated for it. Otherwise this field is null. +builtin_file: ?*File, pub const Deps = std.StringArrayHashMapUnmanaged(*Module); +pub fn isBuiltin(m: Module) bool { + return m.file != null; +} + pub const Tree = struct { /// Each `Package` exposes a `Module` with build.zig as its root source file. build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module), @@ -329,6 +334,8 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { .wasi_exec_model = options.global.wasi_exec_model, }, arena); + const new_file = try arena.create(File); + const digest = Cache.HashHelper.oneShot(generated_builtin_source); const builtin_sub_path = try arena.dupe(u8, "b" ++ std.fs.path.sep_str ++ digest); const new = try arena.create(Module); @@ -359,12 +366,25 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { .stack_protector = stack_protector, .code_model = code_model, .red_zone = red_zone, - .generated_builtin_source = generated_builtin_source, .sanitize_c = sanitize_c, .sanitize_thread = sanitize_thread, .unwind_tables = unwind_tables, .cc_argv = &.{}, .structured_cfg = structured_cfg, + .builtin_file = new_file, + }; + new_file.* = .{ + .sub_file_path = "builtin.zig", + .source = generated_builtin_source, + .source_loaded = true, + .tree_loaded = false, + .zir_loaded = false, + .stat = undefined, + .tree = undefined, + .zir = undefined, + .status = .never_loaded, + .mod = new, + .root_decl = .none, }; break :b new; }; @@ -391,12 +411,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { .stack_protector = stack_protector, .code_model = code_model, .red_zone = red_zone, - .generated_builtin_source = builtin_mod.generated_builtin_source, .sanitize_c = sanitize_c, .sanitize_thread = sanitize_thread, .unwind_tables = unwind_tables, .cc_argv = options.cc_argv, .structured_cfg = structured_cfg, + .builtin_file = null, }; try mod.deps.ensureUnusedCapacity(arena, 1); @@ -437,8 +457,8 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P .sanitize_thread = undefined, .unwind_tables = undefined, .cc_argv = undefined, - .generated_builtin_source = undefined, .structured_cfg = undefined, + .builtin_file = null, }; return mod; } @@ -457,3 +477,4 @@ const Cache = std.Build.Cache; const Builtin = @import("../Builtin.zig"); const assert = std.debug.assert; const Compilation = @import("../Compilation.zig"); +const File = @import("../Module.zig").File; diff --git a/src/Sema.zig b/src/Sema.zig index 910b1cca47..2d46faf435 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -784,6 +784,11 @@ pub const Block = struct { } } + pub fn ownerModule(block: Block) *Package.Module { + const zcu = block.sema.mod; + return zcu.namespacePtr(block.namespace).file_scope.mod; + } + pub fn startAnonDecl(block: *Block) !WipAnonDecl { return WipAnonDecl{ .block = block, @@ -5733,7 +5738,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr // Ignore the result, all the relevant operations have written to c_import_buf already. _ = try sema.analyzeBodyBreak(&child_block, body); - var c_import_res = comp.cImport(c_import_buf.items) catch |err| + var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); defer c_import_res.deinit(gpa); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ee9e3de086..709b2bf6a9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -854,9 +854,8 @@ pub const Object = struct { /// want to iterate over it while adding entries to it. pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr); - pub fn create(arena: Allocator, options: link.File.OpenOptions) !*Object { + pub fn create(arena: Allocator, comp: *Compilation) !*Object { if (build_options.only_c) unreachable; - const comp = options.comp; const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; const llvm_target_triple = try targetTriple(arena, target); @@ -878,14 +877,7 @@ pub const Object = struct { var target_data: if (build_options.have_llvm) *llvm.TargetData else void = undefined; if (builder.useLibLlvm()) { debug_info: { - const debug_format = options.debug_format orelse b: { - if (strip) break :b .strip; - break :b switch (target.ofmt) { - .coff => .code_view, - else => .{ .dwarf = .@"32" }, - }; - }; - switch (debug_format) { + switch (comp.config.debug_format) { .strip => break :debug_info, .code_view => builder.llvm.module.?.addModuleCodeViewFlag(), .dwarf => |f| builder.llvm.module.?.addModuleDebugInfoFlag(f == .@"64"), @@ -961,8 +953,8 @@ pub const Object = struct { opt_level, reloc_mode, code_model, - options.function_sections orelse false, - options.data_sections orelse false, + comp.function_sections, + comp.data_sections, float_abi, if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, ); diff --git a/src/libunwind.zig b/src/libunwind.zig index 9215e24f7c..bc1c0d0343 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -28,6 +28,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { .have_zcu = false, .emit_bin = true, .root_optimize_mode = comp.compilerRtOptMode(), + .root_strip = comp.compilerRtStrip(), .link_libc = true, // Disable LTO to avoid https://github.com/llvm/llvm-project/issues/56825 .lto = false, @@ -131,7 +132,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { .libc_installation = comp.libc_installation, .emit_bin = emit_bin, .link_mode = link_mode, - .function_sections = comp.bin_file.function_sections, + .function_sections = comp.function_sections, .c_source_files = &c_source_files, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, diff --git a/src/link.zig b/src/link.zig index 2a61e46969..91def2a134 100644 --- a/src/link.zig +++ b/src/link.zig @@ -68,9 +68,6 @@ pub const File = struct { force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), allow_shlib_undefined: bool, stack_size: u64, - debug_format: DebugFormat, - function_sections: bool, - data_sections: bool, /// Prevents other processes from clobbering files in the output directory /// of this linking operation. @@ -78,16 +75,7 @@ pub const File = struct { child_pid: ?std.ChildProcess.Id = null, - pub const DebugFormat = union(enum) { - strip, - dwarf: std.dwarf.Format, - code_view, - }; - pub const OpenOptions = struct { - comp: *Compilation, - emit: Compilation.Emit, - symbol_count_hint: u64 = 32, program_code_size_hint: u64 = 256 * 1024, @@ -95,8 +83,6 @@ pub const File = struct { entry_addr: ?u64, stack_size: ?u64, image_base: ?u64, - function_sections: bool, - data_sections: bool, eh_frame_hdr: bool, emit_relocs: bool, rdynamic: bool, @@ -150,8 +136,6 @@ pub const File = struct { compatibility_version: ?std.SemanticVersion, - debug_format: ?DebugFormat, - // TODO: remove this. libraries are resolved by the frontend. lib_dirs: []const []const u8, rpath_list: []const []const u8, @@ -190,10 +174,29 @@ pub const File = struct { /// rewriting it. A malicious file is detected as incremental link failure /// and does not cause Illegal Behavior. This operation is not atomic. /// `arena` is used for allocations with the same lifetime as the created File. - pub fn open(arena: Allocator, options: OpenOptions) !*File { - switch (Tag.fromObjectFormat(options.comp.root_mod.resolved_target.result.ofmt)) { + pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: OpenOptions, + ) !*File { + switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { + inline else => |tag| { + const ptr = try tag.Type().open(arena, comp, emit, options); + return &ptr.base; + }, + } + } + + pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: OpenOptions, + ) !*File { + switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { inline else => |tag| { - const ptr = try tag.Type().open(arena, options); + const ptr = try tag.Type().createEmpty(arena, comp, emit, options); return &ptr.base; }, } diff --git a/src/link/C.zig b/src/link/C.zig index 043ca0ce6f..ce44cc4c06 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -92,21 +92,24 @@ pub fn addString(this: *C, s: []const u8) Allocator.Error!String { }; } -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C { - const target = options.comp.root_mod.resolved_target.result; +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*C { + const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .c); - const optimize_mode = options.comp.root_mod.optimize_mode; - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; - const output_mode = options.comp.config.output_mode; - const link_mode = options.comp.config.link_mode; + const optimize_mode = comp.root_mod.optimize_mode; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; // These are caught by `Compilation.Config.resolve`. assert(!use_lld); assert(!use_llvm); - const emit = options.emit; - const file = try emit.directory.handle.createFile(emit.sub_path, .{ // Truncation is done on `flush`. .truncate = false, @@ -119,7 +122,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C { c_file.* = .{ .base = .{ .tag = .c, - .comp = options.comp, + .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), .stack_size = options.stack_size orelse 16777216, @@ -129,9 +132,6 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, }; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index db99ba189c..eba24f0d17 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -234,44 +234,49 @@ const ideal_factor = 3; const minimum_text_block_size = 64; pub const min_text_capacity = padToIdeal(minimum_text_block_size); -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Coff { if (build_options.only_c) unreachable; - const target = options.comp.root_mod.resolved_target.result; + const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .coff); - const self = try createEmpty(arena, options); + const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; if (use_lld and use_llvm) { // LLVM emits the object file; LLD links it into the final product. return self; } - const sub_path = if (!use_lld) options.emit.sub_path else p: { + const sub_path = if (!use_lld) emit.sub_path else p: { // Open a temporary object file, not the final output file because we // want to link with LLD. const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ - options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch), + emit.sub_path, target.ofmt.fileExt(target.cpu.arch), }); self.base.intermediary_basename = o_file_path; break :p o_file_path; }; - self.base.file = try options.emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.directory.handle.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.File.determineMode( use_lld, - options.comp.config.output_mode, - options.comp.config.link_mode, + comp.config.output_mode, + comp.config.link_mode, ), }); assert(self.llvm_object == null); - const gpa = self.base.comp.gpa; + const gpa = comp.gpa; try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); @@ -362,8 +367,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff { return self; } -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff { - const comp = options.comp; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Coff { const target = comp.root_mod.resolved_target.result; const optimize_mode = comp.root_mod.optimize_mode; const output_mode = comp.config.output_mode; @@ -380,7 +389,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff { .base = .{ .tag = .coff, .comp = comp, - .emit = options.emit, + .emit = emit, .stack_size = options.stack_size orelse 16777216, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -389,9 +398,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .code_view, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, .ptr_width = ptr_width, .page_size = page_size, @@ -423,7 +429,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff { const use_llvm = comp.config.use_llvm; if (use_llvm and comp.config.have_zcu) { - self.llvm_object = try LlvmObject.create(arena, options); + self.llvm_object = try LlvmObject.create(arena, comp); } return self; } diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 1719d5fa67..ad9e18fa22 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -171,7 +171,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-ERRORLIMIT:0"); try argv.append("-NOLOGO"); - if (self.base.debug_format != .strip) { + if (comp.config.debug_format != .strip) { try argv.append("-DEBUG"); const out_ext = std.fs.path.extension(full_out_path); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b54bde325f..5f96e195ab 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -228,18 +228,23 @@ pub const HashStyle = enum { sysv, gnu, both }; pub const CompressDebugSections = enum { none, zlib, zstd }; pub const SortSection = enum { name, alignment }; -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Elf { if (build_options.only_c) unreachable; - const target = options.comp.root_mod.resolved_target.result; + const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .elf); - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; - const opt_zcu = options.comp.module; - const output_mode = options.comp.config.output_mode; - const link_mode = options.comp.config.link_mode; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const opt_zcu = comp.module; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; - const self = try createEmpty(arena, options); + const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); if (use_lld and use_llvm) { @@ -250,23 +255,23 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf { const is_obj = output_mode == .Obj; const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .Static); - const sub_path = if (!use_lld) options.emit.sub_path else p: { + const sub_path = if (!use_lld) emit.sub_path else p: { // Open a temporary object file, not the final output file because we // want to link with LLD. const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ - options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch), + emit.sub_path, target.ofmt.fileExt(target.cpu.arch), }); self.base.intermediary_basename = o_file_path; break :p o_file_path; }; - self.base.file = try options.emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.directory.handle.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.File.determineMode(use_lld, output_mode, link_mode), }); - const gpa = options.comp.gpa; + const gpa = comp.gpa; // Index 0 is always a null symbol. try self.symbols.append(gpa, .{}); @@ -343,8 +348,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf { return self; } -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { - const comp = options.comp; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Elf { const use_llvm = comp.config.use_llvm; const optimize_mode = comp.root_mod.optimize_mode; const target = comp.root_mod.resolved_target.result; @@ -373,7 +382,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { .base = .{ .tag = .elf, .comp = comp, - .emit = options.emit, + .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, @@ -382,9 +391,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, .ptr_width = ptr_width, .page_size = page_size, @@ -423,7 +429,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf { .version_script = options.version_script, }; if (use_llvm and comp.config.have_zcu) { - self.llvm_object = try LlvmObject.create(arena, options); + self.llvm_object = try LlvmObject.create(arena, comp); } return self; @@ -1753,7 +1759,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append("-pie"); } - if (self.base.debug_format == .strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } @@ -2640,7 +2646,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--export-dynamic"); } - if (self.base.debug_format == .strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 5fa31711e6..0bf1e837e3 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -293,13 +293,14 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem } fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { + const comp = elf_file.base.comp; const shdr = self.shdrs.items[index]; const name = self.getString(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; - if (elf_file.base.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 05ff55dd18..75bc53bb48 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -76,7 +76,8 @@ pub const symbol_mask: u32 = 0x7fffffff; pub const SHN_ATOM: u16 = 0x100; pub fn init(self: *ZigObject, elf_file: *Elf) !void { - const gpa = elf_file.base.comp.gpa; + const comp = elf_file.base.comp; + const gpa = comp.gpa; try self.atoms.append(gpa, 0); // null input section try self.relocs.append(gpa, .{}); // null relocs section @@ -96,8 +97,13 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { esym.st_shndx = elf.SHN_ABS; symbol_ptr.esym_index = esym_index; - if (elf_file.base.debug_format != .strip) { - self.dwarf = Dwarf.init(&elf_file.base, .dwarf32); + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => |v| { + assert(v == .@"32"); + self.dwarf = Dwarf.init(&elf_file.base, .dwarf32); + }, + .code_view => unreachable, } } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 87faec6537..25d59f3de3 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -182,18 +182,21 @@ pub const SdkLayout = enum { vendored, }; -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*MachO { if (build_options.only_c) unreachable; - const comp = options.comp; const target = comp.root_mod.resolved_target.result; const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; assert(target.ofmt == .macho); const gpa = comp.gpa; - const emit = options.emit; const mode: Mode = mode: { - if (use_llvm or comp.module == null or comp.cache_mode == .whole) + if (use_llvm or comp.module == null or comp.cache_use == .whole) break :mode .zld; break :mode .incremental; }; @@ -201,7 +204,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { if (comp.module == null) { // No point in opening a file, we would not write anything to it. // Initialize with empty. - return createEmpty(arena, options); + return createEmpty(arena, comp, emit, options); } // Open a temporary object file, not the final output file because we // want to link with LLD. @@ -210,7 +213,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { }); } else emit.sub_path; - const self = try createEmpty(arena, options); + const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); if (mode == .zld) { @@ -232,7 +235,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { }); self.base.file = file; - if (self.base.debug_format != .strip and comp.module != null) { + if (comp.config.debug_format != .strip and comp.module != null) { // Create dSYM bundle. log.debug("creating {s}.dSYM bundle", .{sub_path}); @@ -279,8 +282,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO { return self; } -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO { - const comp = options.comp; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*MachO { const optimize_mode = comp.root_mod.optimize_mode; const use_llvm = comp.config.use_llvm; @@ -289,7 +296,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO { .base = .{ .tag = .macho, .comp = comp, - .emit = options.emit, + .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -298,11 +305,8 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, - .mode = if (use_llvm or comp.module == null or comp.cache_mode == .whole) + .mode = if (use_llvm or comp.module == null or comp.cache_use == .whole) .zld else .incremental, @@ -317,7 +321,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO { }; if (use_llvm and comp.module != null) { - self.llvm_object = try LlvmObject.create(arena, options); + self.llvm_object = try LlvmObject.create(arena, comp); } log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); @@ -4313,7 +4317,8 @@ fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList } fn writeSymtab(self: *MachO) !SymtabCtx { - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const gpa = comp.gpa; var locals = std.ArrayList(macho.nlist_64).init(gpa); defer locals.deinit(); @@ -4368,7 +4373,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { // We generate stabs last in order to ensure that the strtab always has debug info // strings trailing - if (self.base.debug_format != .strip) { + if (comp.config.debug_format != .strip) { for (self.objects.items) |object| { assert(self.d_sym == null); // TODO try self.generateSymbolStabs(object, &locals); diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index 846e496ec1..57a8352898 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -25,12 +25,17 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; base: link.File, llvm_object: *LlvmObject, -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*NvPtx { if (build_options.only_c) unreachable; - const target = options.comp.root_mod.resolved_target.result; - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; assert(use_llvm); // Caught by Compilation.Config.resolve. assert(!use_lld); // Caught by Compilation.Config.resolve. @@ -42,13 +47,13 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { else => return error.PtxArchNotSupported, } - const llvm_object = try LlvmObject.create(arena, options); + const llvm_object = try LlvmObject.create(arena, comp); const nvptx = try arena.create(NvPtx); nvptx.* = .{ .base = .{ .tag = .nvptx, - .comp = options.comp, - .emit = options.emit, + .comp = comp, + .emit = emit, .gc_sections = options.gc_sections orelse false, .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -57,9 +62,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, .llvm_object = llvm_object, }; @@ -67,10 +69,15 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { return nvptx; } -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*NvPtx { - const target = options.comp.root_mod.resolved_target.result; +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*NvPtx { + const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .nvptx); - return createEmpty(arena, options); + return createEmpty(arena, comp, emit, options); } pub fn deinit(self: *NvPtx) void { diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index b3c08fb66b..c970e72e51 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -294,8 +294,12 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { }; } -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { - const comp = options.comp; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Plan9 { const target = comp.root_mod.resolved_target.result; const gpa = comp.gpa; const optimize_mode = comp.root_mod.optimize_mode; @@ -313,7 +317,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { .base = .{ .tag = .plan9, .comp = comp, - .emit = options.emit, + .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -322,9 +326,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, .sixtyfour_bit = sixtyfour_bit, .bases = undefined, @@ -1308,26 +1309,31 @@ pub fn deinit(self: *Plan9) void { } } -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Plan9 { if (build_options.only_c) unreachable; - const target = options.comp.root_mod.resolved_target.result; - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; assert(!use_llvm); // Caught by Compilation.Config.resolve. assert(!use_lld); // Caught by Compilation.Config.resolve. assert(target.ofmt == .plan9); - const self = try createEmpty(arena, options); + const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); - const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{ + const file = try emit.directory.handle.createFile(emit.sub_path, .{ .read = true, .mode = link.File.determineMode( use_lld, - options.comp.config.output_mode, - options.comp.config.link_mode, + comp.config.output_mode, + comp.config.link_mode, ), }); errdefer file.close(); @@ -1335,7 +1341,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Plan9 { self.bases = defaultBaseAddrs(target.cpu.arch); - const gpa = options.comp.gpa; + const gpa = comp.gpa; try self.syms.appendSlice(gpa, &.{ // we include the global offset table to make it easier for debugging diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index bb278a8e4a..853d97b014 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -49,16 +49,21 @@ object: codegen.Object, pub const base_tag: link.File.Tag = .spirv; -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV { - const gpa = options.comp.gpa; - const target = options.comp.root_mod.resolved_target.result; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*SpirV { + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; const self = try arena.create(SpirV); self.* = .{ .base = .{ .tag = .spirv, - .comp = options.comp, - .emit = options.emit, + .comp = comp, + .emit = emit, .gc_sections = options.gc_sections orelse false, .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -67,9 +72,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .function_sections = options.function_sections, - .data_sections = options.data_sections, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, }, .object = codegen.Object.init(gpa), }; @@ -90,22 +92,27 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV { return self; } -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*SpirV { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*SpirV { if (build_options.only_c) unreachable; - const target = options.comp.root_mod.resolved_target.result; - const use_lld = build_options.have_llvm and options.comp.config.use_lld; - const use_llvm = options.comp.config.use_llvm; + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; assert(!use_llvm); // Caught by Compilation.Config.resolve. assert(!use_lld); // Caught by Compilation.Config.resolve. assert(target.ofmt == .spirv); // Caught by Compilation.Config.resolve. - const spirv = try createEmpty(arena, options); + const spirv = try createEmpty(arena, comp, emit, options); errdefer spirv.base.destroy(); // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{ + const file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 74d83e6c3c..8f63e7c6fd 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -373,9 +373,13 @@ pub const StringTable = struct { } }; -pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Wasm { if (build_options.only_c) unreachable; - const comp = options.comp; const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .wasm); @@ -385,7 +389,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { const output_mode = comp.config.output_mode; const shared_memory = comp.config.shared_memory; - const wasm = try createEmpty(arena, options); + const wasm = try createEmpty(arena, comp, emit, options); errdefer wasm.base.destroy(); if (use_lld and use_llvm) { @@ -393,18 +397,18 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { return wasm; } - const sub_path = if (!use_lld) options.emit.sub_path else p: { + const sub_path = if (!use_lld) emit.sub_path else p: { // Open a temporary object file, not the final output file because we // want to link with LLD. const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ - options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch), + emit.sub_path, target.ofmt.fileExt(target.cpu.arch), }); wasm.base.intermediary_basename = o_file_path; break :p o_file_path; }; // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.directory.handle.createFile(sub_path, .{ + const file = try emit.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) @@ -530,8 +534,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm { return wasm; } -pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { - const comp = options.comp; +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Wasm { const use_llvm = comp.config.use_llvm; const output_mode = comp.config.output_mode; @@ -540,7 +548,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { .base = .{ .tag = .wasm, .comp = comp, - .emit = options.emit, + .emit = emit, .gc_sections = options.gc_sections orelse (output_mode != .Obj), .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB .allow_shlib_undefined = options.allow_shlib_undefined orelse false, @@ -549,9 +557,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { .build_id = options.build_id, .rpath_list = options.rpath_list, .force_undefined_symbols = options.force_undefined_symbols, - .debug_format = options.debug_format orelse .{ .dwarf = .@"32" }, - .function_sections = options.function_sections, - .data_sections = options.data_sections, }, .name = undefined, .import_table = options.import_table, @@ -566,7 +571,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm { }; if (use_llvm) { - wasm.llvm_object = try LlvmObject.create(arena, options); + wasm.llvm_object = try LlvmObject.create(arena, comp); } return wasm; } @@ -4205,11 +4210,11 @@ fn writeToFile( if (data_section_index) |data_index| { try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); } - } else if (wasm.base.debug_format != .strip) { + } else if (comp.config.debug_format != .strip) { try wasm.emitNameSection(&binary_bytes, arena); } - if (wasm.base.debug_format != .strip) { + if (comp.config.debug_format != .strip) { // The build id must be computed on the main sections only, // so we have to do it now, before the debug sections. switch (wasm.base.build_id) { @@ -4748,7 +4753,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append("--no-gc-sections"); } - if (wasm.base.debug_format == .strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } @@ -5276,7 +5281,9 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s fn markReferences(wasm: *Wasm) !void { const tracy = trace(@src()); defer tracy.end(); + const do_garbage_collect = wasm.base.gc_sections; + const comp = wasm.base.comp; for (wasm.resolved_symbols.keys()) |sym_loc| { const sym = sym_loc.getSymbol(wasm); @@ -5287,7 +5294,7 @@ fn markReferences(wasm: *Wasm) !void { // Debug sections may require to be parsed and marked when it contains // relocations to alive symbols. - if (sym.tag == .section and wasm.base.debug_format != .strip) { + if (sym.tag == .section and comp.config.debug_format != .strip) { const file = sym_loc.file orelse continue; // Incremental debug info is done independently const object = &wasm.objects.items[file]; const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm); diff --git a/src/main.zig b/src/main.zig index 9acad1a0ca..dbd9759015 100644 --- a/src/main.zig +++ b/src/main.zig @@ -892,7 +892,6 @@ fn buildOutputType( var contains_res_file: bool = false; var reference_trace: ?u32 = null; var pdb_out_path: ?[]const u8 = null; - var debug_format: ?link.File.DebugFormat = null; var error_limit: ?Module.ErrorInt = null; // These are before resolving sysroot. var lib_dir_args: std.ArrayListUnmanaged([]const u8) = .{}; @@ -1054,6 +1053,8 @@ fn buildOutputType( create_module.opts.any_sanitize_thread = true; if (mod_opts.unwind_tables == true) create_module.opts.any_unwind_tables = true; + if (mod_opts.strip == false) + create_module.opts.any_non_stripped = true; const root_src = try introspect.resolvePath(arena, root_src_orig); try create_module.modules.put(arena, mod_name, .{ @@ -1480,9 +1481,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fno-strip")) { mod_opts.strip = false; } else if (mem.eql(u8, arg, "-gdwarf32")) { - debug_format = .{ .dwarf = .@"32" }; + create_module.opts.debug_format = .{ .dwarf = .@"32" }; } else if (mem.eql(u8, arg, "-gdwarf64")) { - debug_format = .{ .dwarf = .@"64" }; + create_module.opts.debug_format = .{ .dwarf = .@"64" }; } else if (mem.eql(u8, arg, "-fformatted-panics")) { formatted_panics = true; } else if (mem.eql(u8, arg, "-fno-formatted-panics")) { @@ -1989,11 +1990,11 @@ fn buildOutputType( }, .gdwarf32 => { mod_opts.strip = false; - debug_format = .{ .dwarf = .@"32" }; + create_module.opts.debug_format = .{ .dwarf = .@"32" }; }, .gdwarf64 => { mod_opts.strip = false; - debug_format = .{ .dwarf = .@"64" }; + create_module.opts.debug_format = .{ .dwarf = .@"64" }; }, .sanitize => { if (mem.eql(u8, it.only_arg, "undefined")) { @@ -2532,6 +2533,8 @@ fn buildOutputType( create_module.opts.any_sanitize_thread = true; if (mod_opts.unwind_tables == true) create_module.opts.any_unwind_tables = true; + if (mod_opts.strip == false) + create_module.opts.any_non_stripped = true; const src_path = try introspect.resolvePath(arena, unresolved_src_path); try create_module.modules.put(arena, "main", .{ @@ -3359,7 +3362,6 @@ fn buildOutputType( .emit_docs = emit_docs_resolved.data, .emit_implib = emit_implib_resolved.data, .dll_export_fns = dll_export_fns, - .keep_source_files_loaded = false, .lib_dirs = lib_dirs.items, .rpath_list = rpath_list.items, .symbol_wrap_set = symbol_wrap_set, @@ -3443,7 +3445,6 @@ fn buildOutputType( .test_runner_path = test_runner_path, .disable_lld_caching = !output_to_cache, .subsystem = subsystem, - .debug_format = debug_format, .debug_compile_errors = debug_compile_errors, .enable_link_snapshots = enable_link_snapshots, .install_name = install_name, @@ -3484,7 +3485,9 @@ fn buildOutputType( defer if (!comp_destroyed) comp.destroy(); if (show_builtin) { - return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); + const builtin_mod = comp.root_mod.deps.get("builtin").?; + const source = builtin_mod.builtin_file.?.source; + return std.io.getStdOut().writeAll(source); } switch (listen) { .none => {}, @@ -3737,6 +3740,7 @@ fn createModule( const resolved_target = cli_mod.inherited.resolved_target.?; create_module.opts.resolved_target = resolved_target; create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode; + create_module.opts.root_strip = cli_mod.inherited.strip; const target = resolved_target.result; // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. @@ -4366,12 +4370,12 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name}); - var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); + var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod); man.want_shared_lock = false; defer man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects - man.hash.add(comp.c_frontend); + man.hash.add(comp.config.c_frontend); Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; @@ -4380,14 +4384,14 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati const digest = if (try man.hit()) man.final() else digest: { if (fancy_output) |p| p.cache_hit = false; var argv = std.ArrayList([]const u8).init(arena); - try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1] + try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1] var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); defer zig_cache_tmp_dir.close(); const ext = Compilation.classifyFileExt(c_source_file.src_path); const out_dep_path: ?[]const u8 = blk: { - if (comp.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile()) + if (comp.config.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile()) break :blk null; const c_src_basename = fs.path.basename(c_source_file.src_path); @@ -4397,14 +4401,15 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati }; // TODO - if (comp.c_frontend != .aro) try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); + if (comp.config.c_frontend != .aro) + try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path, comp.root_mod); try argv.append(c_source_file.src_path); if (comp.verbose_cc) { Compilation.dump_argv(argv.items); } - var tree = switch (comp.c_frontend) { + var tree = switch (comp.config.c_frontend) { .aro => tree: { const aro = @import("aro"); const translate_c = @import("aro_translate_c.zig"); -- cgit v1.2.3 From 2596f5d92593a5f7dad21d9a54f2d08f33639432 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Dec 2023 19:49:53 -0700 Subject: update bin_file.options references in Sema mainly pertaining to error return tracing --- src/Compilation/Config.zig | 22 ++++++++++++++++ src/Package/Module.zig | 10 ++------ src/Sema.zig | 64 +++++++++++++++++++++++----------------------- src/codegen/llvm.zig | 10 ++++---- src/main.zig | 5 ++++ 5 files changed, 66 insertions(+), 45 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 0afb633c86..efe314095c 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -9,6 +9,10 @@ link_libunwind: bool, any_unwind_tables: bool, any_c_source_files: bool, any_non_single_threaded: bool, +/// This is true if any Module has error_tracing set to true. Function types +/// and function calling convention depend on this global value, however, other +/// kinds of error tracing are omitted depending on the per-Module setting. +any_error_tracing: bool, pie: bool, /// If this is true then linker code is responsible for making an LLVM IR /// Module, outputting it to an object file, and then linking that together @@ -34,6 +38,8 @@ is_test: bool, test_evented_io: bool, entry: ?[]const u8, debug_format: DebugFormat, +root_strip: bool, +root_error_tracing: bool, pub const CFrontend = enum { clang, aro }; @@ -51,6 +57,7 @@ pub const Options = struct { emit_bin: bool, root_optimize_mode: ?std.builtin.OptimizeMode = null, root_strip: ?bool = null, + root_error_tracing: ?bool = null, link_mode: ?std.builtin.LinkMode = null, ensure_libc_on_non_freestanding: bool = false, ensure_libcpp_on_non_freestanding: bool = false, @@ -60,6 +67,7 @@ pub const Options = struct { any_dyn_libs: bool = false, any_c_source_files: bool = false, any_non_stripped: bool = false, + any_error_tracing: bool = false, emit_llvm_ir: bool = false, emit_llvm_bc: bool = false, link_libc: ?bool = null, @@ -395,6 +403,17 @@ pub fn resolve(options: Options) !Config { }; }; + const root_error_tracing = b: { + if (options.root_error_tracing) |x| break :b x; + if (root_strip) break :b false; + break :b switch (root_optimize_mode) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }; + }; + + const any_error_tracing = root_error_tracing or options.any_error_tracing; + return .{ .output_mode = options.output_mode, .have_zcu = options.have_zcu, @@ -407,6 +426,8 @@ pub fn resolve(options: Options) !Config { .any_unwind_tables = any_unwind_tables, .any_c_source_files = options.any_c_source_files, .any_non_single_threaded = options.any_non_single_threaded, + .any_error_tracing = any_error_tracing, + .root_error_tracing = root_error_tracing, .pie = pie, .lto = lto, .import_memory = import_memory, @@ -419,6 +440,7 @@ pub fn resolve(options: Options) !Config { .entry = entry, .wasi_exec_model = wasi_exec_model, .debug_format = debug_format, + .root_strip = root_strip, }; } diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 051303ca26..5e786a6fa2 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -114,9 +114,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { const strip = b: { if (options.inherited.strip) |x| break :b x; if (options.parent) |p| break :b p.strip; - if (optimize_mode == .ReleaseSmall) break :b true; - if (!target_util.hasDebugInfo(target)) break :b true; - break :b false; + break :b options.global.root_strip; }; const valgrind = b: { @@ -156,11 +154,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { const error_tracing = b: { if (options.inherited.error_tracing) |x| break :b x; if (options.parent) |p| break :b p.error_tracing; - if (strip) break :b false; - break :b switch (optimize_mode) { - .Debug => true, - .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, - }; + break :b options.global.root_error_tracing; }; const pic = b: { diff --git a/src/Sema.zig b/src/Sema.zig index 2d46faf435..6d14deb095 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1329,13 +1329,13 @@ fn analyzeBodyInner( }, .dbg_block_begin => { dbg_block_begins += 1; - try sema.zirDbgBlockBegin(block); + try zirDbgBlockBegin(block); i += 1; continue; }, .dbg_block_end => { dbg_block_begins -= 1; - try sema.zirDbgBlockEnd(block); + try zirDbgBlockEnd(block); i += 1; continue; }, @@ -1830,17 +1830,17 @@ fn analyzeBodyInner( }; // balance out dbg_block_begins in case of early noreturn - const noreturn_inst = block.instructions.popOrNull(); - while (dbg_block_begins > 0) { - dbg_block_begins -= 1; - if (block.is_comptime or mod.comp.bin_file.options.strip) continue; - - _ = try block.addInst(.{ - .tag = .dbg_block_end, - .data = undefined, - }); + if (!block.is_comptime and !block.ownerModule().strip) { + const noreturn_inst = block.instructions.popOrNull(); + while (dbg_block_begins > 0) { + dbg_block_begins -= 1; + _ = try block.addInst(.{ + .tag = .dbg_block_end, + .data = undefined, + }); + } + if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); } - if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); // We may have overwritten the capture scope due to a `repeat` instruction where // the body had a capture; restore it now. @@ -6272,7 +6272,7 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi // ZIR code that possibly will need to generate runtime code. So error messages // and other source locations must not rely on sema.src being set from dbg_stmt // instructions. - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; + if (block.is_comptime or block.ownerModule().strip) return; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; @@ -6297,8 +6297,8 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi }); } -fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; +fn zirDbgBlockBegin(block: *Block) CompileError!void { + if (block.is_comptime or block.ownerModule().strip) return; _ = try block.addInst(.{ .tag = .dbg_block_begin, @@ -6306,8 +6306,8 @@ fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void { }); } -fn zirDbgBlockEnd(sema: *Sema, block: *Block) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; +fn zirDbgBlockEnd(block: *Block) CompileError!void { + if (block.is_comptime or block.ownerModule().strip) return; _ = try block.addInst(.{ .tag = .dbg_block_end, @@ -6321,7 +6321,7 @@ fn zirDbgVar( inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, ) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; + if (block.is_comptime or block.ownerModule().strip) return; const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; const operand = try sema.resolveInst(str_op.operand); @@ -6519,7 +6519,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref const src = sema.src; if (!mod.backendSupportsFeature(.error_return_trace)) return .none; - if (!mod.comp.bin_file.options.error_return_tracing) return .none; + if (!block.ownerModule().error_tracing) return .none; if (block.is_comptime) return .none; @@ -6703,7 +6703,7 @@ fn zirCall( input_is_error = false; } - if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and + if (mod.backendSupportsFeature(.error_return_trace) and block.ownerModule().error_tracing and !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) { const return_ty = sema.typeOf(call_inst); @@ -7456,7 +7456,7 @@ fn analyzeCall( new_fn_info.return_type = sema.fn_ret_ty.toIntern(); const new_func_resolved_ty = try mod.funcType(new_fn_info); if (!is_comptime_call and !block.is_typeof) { - try sema.emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); + try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { @@ -7494,7 +7494,7 @@ fn analyzeCall( if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag(mod) != .NoReturn) { - try sema.emitDbgInline( + try emitDbgInline( block, module_fn_index, prev_fn_index, @@ -8067,15 +8067,13 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) } fn emitDbgInline( - sema: *Sema, block: *Block, old_func: InternPool.Index, new_func: InternPool.Index, new_func_ty: Type, tag: Air.Inst.Tag, ) CompileError!void { - const mod = sema.mod; - if (mod.comp.bin_file.options.strip) return; + if (block.ownerModule().strip) return; // Recursive inline call; no dbg_inline needed. if (old_func == new_func) return; @@ -9109,7 +9107,7 @@ fn handleExternLibName( ); break :blk; } - if (!target.isWasm() and !comp.bin_file.options.pic) { + if (!target.isWasm() and !block.ownerModule().pic) { return sema.fail( block, src_loc, @@ -18738,16 +18736,16 @@ fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { const mod = sema.mod; if (!mod.backendSupportsFeature(.error_return_trace)) return false; - return fn_ret_ty.isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + return fn_ret_ty.isError(mod) and mod.comp.config.any_error_tracing; } fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; + // TODO: replace all of these checks with logic in module creation if (!mod.backendSupportsFeature(.error_return_trace)) return; - if (!mod.comp.bin_file.options.error_return_tracing) return; + if (!block.ownerModule().error_tracing) return; // This is only relevant at runtime. if (block.is_comptime or block.is_typeof) return; @@ -18774,7 +18772,7 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) if (!mod.backendSupportsFeature(.error_return_trace)) return; if (!ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) return; - if (!mod.comp.bin_file.options.error_return_tracing) return; + if (!start_block.ownerModule().error_tracing) return; const tracy = trace(@src()); defer tracy.end(); @@ -20045,7 +20043,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { if (sema.owner_func_index != .none and ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and - mod.comp.bin_file.options.error_return_tracing and + mod.ownerModule().error_tracing and mod.backendSupportsFeature(.error_return_trace)) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); @@ -34504,7 +34502,9 @@ pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.return_type)); - if (mod.comp.bin_file.options.error_return_tracing and Type.fromInterned(fn_ty_info.return_type).isError(mod)) { + if (mod.comp.config.any_error_tracing and + Type.fromInterned(fn_ty_info.return_type).isError(mod)) + { // Ensure the type exists so that backends can assume that. _ = try sema.getBuiltinType("StackTrace"); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6545f84f9d..75fb373d9b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1398,7 +1398,7 @@ pub const Object = struct { }; const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + mod.comp.config.any_error_tracing; const err_ret_trace: Builder.Value = if (err_return_tracing) param: { const param = wip.arg(llvm_arg_i); @@ -2820,7 +2820,7 @@ pub const Object = struct { } if (Type.fromInterned(fn_info.return_type).isError(mod) and - o.module.comp.bin_file.options.error_return_tracing) + o.module.comp.config.any_error_tracing) { const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType()); try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); @@ -2988,7 +2988,7 @@ pub const Object = struct { } const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + mod.comp.config.any_error_tracing; if (err_return_tracing) { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); @@ -3677,7 +3677,7 @@ pub const Object = struct { } if (Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing) + mod.comp.config.any_error_tracing) { const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType()); try llvm_params.append(o.gpa, try o.lowerType(ptr_ty)); @@ -5142,7 +5142,7 @@ pub const FuncGen = struct { }; const err_return_tracing = return_type.isError(mod) and - o.module.comp.bin_file.options.error_return_tracing; + o.module.comp.config.any_error_tracing; if (err_return_tracing) { assert(self.err_ret_trace != .none); try llvm_args.append(self.err_ret_trace); diff --git a/src/main.zig b/src/main.zig index dbd9759015..b73023e5b3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1055,6 +1055,8 @@ fn buildOutputType( create_module.opts.any_unwind_tables = true; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; + if (mod_opts.error_tracing == true) + create_module.opts.any_error_tracing = true; const root_src = try introspect.resolvePath(arena, root_src_orig); try create_module.modules.put(arena, mod_name, .{ @@ -2535,6 +2537,8 @@ fn buildOutputType( create_module.opts.any_unwind_tables = true; if (mod_opts.strip == false) create_module.opts.any_non_stripped = true; + if (mod_opts.error_tracing == true) + create_module.opts.any_error_tracing = true; const src_path = try introspect.resolvePath(arena, unresolved_src_path); try create_module.modules.put(arena, "main", .{ @@ -3741,6 +3745,7 @@ fn createModule( create_module.opts.resolved_target = resolved_target; create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode; create_module.opts.root_strip = cli_mod.inherited.strip; + create_module.opts.root_error_tracing = cli_mod.inherited.error_tracing; const target = resolved_target.result; // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. -- cgit v1.2.3 From 2047a6b82d2e6cdbddde3e7f1c93df9e72216052 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Dec 2023 00:39:36 -0700 Subject: fix remaining compile errors except one --- src/Compilation.zig | 8 +- src/Module.zig | 11 +- src/Sema.zig | 2 +- src/arch/x86_64/CodeGen.zig | 46 +++--- src/codegen/llvm.zig | 2 +- src/link.zig | 298 ++++++++++++++++----------------------- src/link/C.zig | 1 + src/link/Coff.zig | 15 +- src/link/Coff/lld.zig | 45 +++--- src/link/Dwarf.zig | 6 +- src/link/Elf.zig | 12 +- src/link/Elf/Atom.zig | 6 +- src/link/Elf/Object.zig | 6 +- src/link/Elf/ZigObject.zig | 2 +- src/link/Elf/eh_frame.zig | 2 +- src/link/MachO.zig | 34 +++-- src/link/MachO/Object.zig | 16 ++- src/link/MachO/dead_strip.zig | 3 +- src/link/MachO/eh_frame.zig | 9 +- src/link/MachO/load_commands.zig | 33 +++-- src/link/MachO/thunks.zig | 6 +- src/link/MachO/zld.zig | 42 +++--- src/link/NvPtx.zig | 3 +- src/link/Plan9.zig | 11 +- src/link/SpirV.zig | 19 +-- src/link/Wasm.zig | 9 +- src/link/Wasm/Object.zig | 10 +- src/main.zig | 24 ++-- 28 files changed, 340 insertions(+), 341 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index a39d852563..14a39f1bb3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -229,7 +229,7 @@ pub const Emit = struct { /// Returns the full path to `basename` if it were in the same directory as the /// `Emit` sub_path. - pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 { + pub fn basenamePath(emit: Emit, arena: Allocator, basename: []const u8) ![:0]const u8 { const full_path = if (emit.directory.path) |p| try std.fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) else @@ -238,7 +238,7 @@ pub const Emit = struct { if (std.fs.path.dirname(full_path)) |dirname| { return try std.fs.path.joinZ(arena, &.{ dirname, basename }); } else { - return basename; + return try arena.dupeZ(u8, basename); } } }; @@ -1038,8 +1038,8 @@ pub const InitOptions = struct { linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null, linker_module_definition_file: ?[]const u8 = null, linker_sort_section: ?link.File.Elf.SortSection = null, - major_subsystem_version: ?u32 = null, - minor_subsystem_version: ?u32 = null, + major_subsystem_version: ?u16 = null, + minor_subsystem_version: ?u16 = null, clang_passthrough_mode: bool = false, verbose_cc: bool = false, verbose_link: bool = false, diff --git a/src/Module.zig b/src/Module.zig index 571301c8e6..6d5a2f78a6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4309,14 +4309,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @enumFromInt(decl_sub_index); if (decl.getOwnedFunction(mod) != null) { - switch (comp.bin_file.tag) { - .coff, .elf, .macho, .plan9 => { - // TODO Look into detecting when this would be unnecessary by storing enough state - // in `Decl` to notice that the line number did not change. - comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); - }, - .c, .wasm, .spirv, .nvptx => {}, - } + // TODO Look into detecting when this would be unnecessary by storing enough state + // in `Decl` to notice that the line number did not change. + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); } } diff --git a/src/Sema.zig b/src/Sema.zig index 6d14deb095..26afd285c7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20043,7 +20043,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { if (sema.owner_func_index != .none and ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and - mod.ownerModule().error_tracing and + block.ownerModule().error_tracing and mod.backendSupportsFeature(.error_return_trace)) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index dbb87fa566..55e241cbd4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -800,19 +800,20 @@ pub fn generate( ) CodeGenError!Result { const comp = bin_file.comp; const gpa = comp.gpa; - const mod = comp.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const zcu = comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; - const namespace = mod.namespacePtr(fn_owner_decl.src_namespace); - const target = namespace.file_scope.mod.resolved_target.result; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const mod = namespace.file_scope.mod; var function = Self{ .gpa = gpa, .air = air, .liveness = liveness, - .target = target, + .target = &mod.resolved_target.result, + .mod = mod, .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .func_index = func_index }, @@ -843,7 +844,7 @@ pub fn generate( wip_mir_log.debug("{}:", .{function.fmtDecl(func.owner_decl)}); - const ip = &mod.intern_pool; + const ip = &zcu.intern_pool; try function.frame_allocs.resize(gpa, FrameIndex.named_count); function.frame_allocs.set( @@ -858,7 +859,7 @@ pub fn generate( FrameAlloc.init(.{ .size = 0, .alignment = .@"1" }), ); - const fn_info = mod.typeToFunc(fn_type).?; + const fn_info = zcu.typeToFunc(fn_type).?; const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, @@ -877,14 +878,14 @@ pub fn generate( function.args = call_info.args; function.ret_mcv = call_info.return_value; function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(mod), - .alignment = Type.usize.abiAlignment(mod).min(call_info.stack_align), + .size = Type.usize.abiSize(zcu), + .alignment = Type.usize.abiAlignment(zcu).min(call_info.stack_align), })); function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(mod), + .size = Type.usize.abiSize(zcu), .alignment = Alignment.min( call_info.stack_align, - Alignment.fromNonzeroByteUnits(target.stackAlignment()), + Alignment.fromNonzeroByteUnits(function.target.stackAlignment()), ), })); function.frame_allocs.set( @@ -927,6 +928,9 @@ pub fn generate( .mir = mir, .cc = cc, .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }, .debug_output = debug_output, .code = code, @@ -970,16 +974,14 @@ pub fn generateLazy( ) CodeGenError!Result { const comp = bin_file.comp; const gpa = comp.gpa; - const zcu = comp.module.?; - const decl_index = lazy_sym.ty.getOwnerDecl(zcu); - const decl = zcu.declPtr(decl_index); - const namespace = zcu.namespacePtr(decl.src_namespace); - const target = namespace.file_scope.mod.resolved_target.result; + // This function is for generating global code, so we use the root module. + const mod = comp.root_mod; var function = Self{ .gpa = gpa, .air = undefined, .liveness = undefined, - .target = target, + .target = &mod.resolved_target.result, + .mod = mod, .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .lazy_sym = lazy_sym }, @@ -1020,6 +1022,9 @@ pub fn generateLazy( .mir = mir, .cc = abi.resolveCallingConvention(.Unspecified, function.target.*), .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }, .debug_output = debug_output, .code = code, @@ -1104,6 +1109,8 @@ fn formatWipMir( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { + const comp = data.self.bin_file.comp; + const mod = comp.root_mod; var lower = Lower{ .bin_file = data.self.bin_file, .allocator = data.self.gpa, @@ -1114,6 +1121,9 @@ fn formatWipMir( }, .cc = .Unspecified, .src_loc = data.self.src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }; var first = true; for ((lower.lowerMir(data.inst) catch |err| switch (err) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7e25a4b59b..3a53bddcb8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1168,7 +1168,7 @@ pub const Object = struct { pre_ir_path: ?[]const u8, pre_bc_path: ?[]const u8, bin_path: ?[*:0]const u8, - emit_asm: ?[*:0]const u8, + asm_path: ?[*:0]const u8, post_ir_path: ?[*:0]const u8, post_bc_path: ?[*:0]const u8, diff --git a/src/link.zig b/src/link.zig index 14b180ed2a..3e5caf9fc1 100644 --- a/src/link.zig +++ b/src/link.zig @@ -61,6 +61,7 @@ pub const File = struct { intermediary_basename: ?[]const u8 = null, disable_lld_caching: bool, gc_sections: bool, + print_gc_sections: bool, build_id: std.zig.BuildId, rpath_list: []const []const u8, /// List of symbols forced as undefined in the symbol table @@ -114,8 +115,8 @@ pub const File = struct { disable_lld_caching: bool, hash_style: Elf.HashStyle, sort_section: ?Elf.SortSection, - major_subsystem_version: ?u32, - minor_subsystem_version: ?u32, + major_subsystem_version: ?u16, + minor_subsystem_version: ?u16, gc_sections: ?bool, allow_shlib_undefined: ?bool, subsystem: ?std.Target.SubSystem, @@ -181,9 +182,15 @@ pub const File = struct { emit: Compilation.Emit, options: OpenOptions, ) !*File { - switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { - inline else => |tag| { - const ptr = try tag.Type().open(arena, comp, emit, options); + const tag = Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt); + switch (tag) { + .c => { + const ptr = try C.open(arena, comp, emit, options); + return &ptr.base; + }, + inline else => |t| { + if (build_options.only_c) unreachable; + const ptr = try t.Type().open(arena, comp, emit, options); return &ptr.base; }, } @@ -195,9 +202,15 @@ pub const File = struct { emit: Compilation.Emit, options: OpenOptions, ) !*File { - switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { - inline else => |tag| { - const ptr = try tag.Type().createEmpty(arena, comp, emit, options); + const tag = Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt); + switch (tag) { + .c => { + const ptr = try C.createEmpty(arena, comp, emit, options); + return &ptr.base; + }, + inline else => |t| { + if (build_options.only_c) unreachable; + const ptr = try t.Type().createEmpty(arena, comp, emit, options); return &ptr.base; }, } @@ -360,16 +373,12 @@ pub const File = struct { pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: InternPool.DeclIndex) UpdateDeclError!u32 { if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index), .spirv => unreachable, - .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).lowerUnnamedConst(tv, decl_index), + .c => unreachable, .nvptx => unreachable, - // zig fmt: on + inline else => |t| { + return @fieldParentPtr(t.Type(), "base", base).lowerUnnamedConst(tv, decl_index); + }, } } @@ -382,16 +391,13 @@ pub const File = struct { if (build_options.only_c) @compileError("unreachable"); log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name), - .elf => return @fieldParentPtr(Elf, "base", base).getGlobalSymbol(name, lib_name), - .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name), .plan9 => unreachable, .spirv => unreachable, - .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getGlobalSymbol(name, lib_name), + .c => unreachable, .nvptx => unreachable, - // zig fmt: on + inline else => |t| { + return @fieldParentPtr(t.Type(), "base", base).getGlobalSymbol(name, lib_name); + }, } } @@ -399,59 +405,48 @@ pub const File = struct { pub fn updateDecl(base: *File, module: *Module, decl_index: InternPool.DeclIndex) UpdateDeclError!void { const decl = module.declPtr(decl_index); assert(decl.has_tv); - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index); - } switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index), - .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl_index), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index), - // zig fmt: on + .c => { + return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateDecl(module, decl_index); + }, } } /// May be called before or after updateExports for any given Decl. - pub fn updateFunc(base: *File, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) UpdateDeclError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness); - } + pub fn updateFunc( + base: *File, + module: *Module, + func_index: InternPool.Index, + air: Air, + liveness: Liveness, + ) UpdateDeclError!void { switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).updateFunc(module, func_index, air, liveness), - .elf => return @fieldParentPtr(Elf, "base", base).updateFunc(module, func_index, air, liveness), - .macho => return @fieldParentPtr(MachO, "base", base).updateFunc(module, func_index, air, liveness), - .c => return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func_index, air, liveness), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func_index, air, liveness), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func_index, air, liveness), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func_index, air, liveness), - // zig fmt: on + .c => { + return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateFunc(module, func_index, air, liveness); + }, } } pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: InternPool.DeclIndex) UpdateDeclError!void { const decl = module.declPtr(decl_index); assert(decl.has_tv); - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index); - } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index), - .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index), .spirv, .nvptx => {}, + .c => { + return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateDeclLineNumber(module, decl_index); + }, } } @@ -477,44 +472,11 @@ pub const File = struct { base.misc_errors.deinit(gpa); } switch (base.tag) { - .coff => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(Coff, "base", base); - parent.deinit(); - }, - .elf => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(Elf, "base", base); - parent.deinit(); - }, - .macho => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(MachO, "base", base); - parent.deinit(); - }, - .c => { - const parent = @fieldParentPtr(C, "base", base); - parent.deinit(); - }, - .wasm => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(Wasm, "base", base); - parent.deinit(); - }, - .spirv => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(SpirV, "base", base); - parent.deinit(); - }, - .plan9 => { - if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(Plan9, "base", base); - parent.deinit(); - }, - .nvptx => { + .c => @fieldParentPtr(C, "base", base).deinit(), + + inline else => |tag| { if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(NvPtx, "base", base); - parent.deinit(); + @fieldParentPtr(tag.Type(), "base", base).deinit(); }, } } @@ -619,51 +581,36 @@ pub const File = struct { return base.linkAsArchive(comp, prog_node); } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node), - .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node), - .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node), - .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node), - .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node), - .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node), + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).flush(comp, prog_node); + }, } } /// Commit pending changes and write headers. Works based on `effectiveOutputMode` /// rather than final output mode. pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node); - } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node), - .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node), - .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node), - .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node), - .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node), - .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node), + .c => { + return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).flushModule(comp, prog_node); + }, } } /// Called when a Decl is deleted from the Module. pub fn freeDecl(base: *File, decl_index: InternPool.DeclIndex) void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).freeDecl(decl_index); - } switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index), - .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index), - .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index), - .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index), - .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index), - .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index), - .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index), - .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index), + .c => { + @fieldParentPtr(C, "base", base).freeDecl(decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + @fieldParentPtr(tag.Type(), "base", base).freeDecl(decl_index); + }, } } @@ -682,19 +629,14 @@ pub const File = struct { exported: Module.Exported, exports: []const *Module.Export, ) UpdateExportsError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports); - } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateExports(module, exported, exports), - .elf => return @fieldParentPtr(Elf, "base", base).updateExports(module, exported, exports), - .macho => return @fieldParentPtr(MachO, "base", base).updateExports(module, exported, exports), - .c => return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateExports(module, exported, exports), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateExports(module, exported, exports), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateExports(module, exported, exports), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateExports(module, exported, exports), + .c => { + return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateExports(module, exported, exports); + }, } } @@ -711,60 +653,64 @@ pub const File = struct { /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate /// the block/atom. pub fn getDeclVAddr(base: *File, decl_index: InternPool.DeclIndex, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) unreachable; + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info), - .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info), - .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).getDeclVAddr(decl_index, reloc_info); + }, } } pub const LowerResult = @import("codegen.zig").Result; - pub fn lowerAnonDecl(base: *File, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !LowerResult { - if (build_options.only_c) unreachable; + pub fn lowerAnonDecl( + base: *File, + decl_val: InternPool.Index, + decl_align: InternPool.Alignment, + src_loc: Module.SrcLoc, + ) !LowerResult { + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .elf => return @fieldParentPtr(Elf, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .macho => return @fieldParentPtr(MachO, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerAnonDecl(decl_val, src_loc), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).lowerAnonDecl(decl_val, decl_align, src_loc); + }, } } pub fn getAnonDeclVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) unreachable; + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .elf => return @fieldParentPtr(Elf, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .macho => return @fieldParentPtr(MachO, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getAnonDeclVAddr(decl_val, reloc_info), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getAnonDeclVAddr(decl_val, reloc_info), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).getAnonDeclVAddr(decl_val, reloc_info); + }, } } - pub fn deleteDeclExport(base: *File, decl_index: InternPool.DeclIndex, name: InternPool.NullTerminatedString) !void { - if (build_options.only_c) unreachable; + pub fn deleteDeclExport( + base: *File, + decl_index: InternPool.DeclIndex, + name: InternPool.NullTerminatedString, + ) !void { + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).deleteDeclExport(decl_index, name), - .elf => return @fieldParentPtr(Elf, "base", base).deleteDeclExport(decl_index, name), - .macho => return @fieldParentPtr(MachO, "base", base).deleteDeclExport(decl_index, name), - .plan9 => {}, - .c => {}, - .wasm => return @fieldParentPtr(Wasm, "base", base).deleteDeclExport(decl_index), - .spirv => {}, - .nvptx => {}, + .plan9, + .c, + .spirv, + .nvptx, + => {}, + + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).deleteDeclExport(decl_index, name); + }, } } @@ -1049,7 +995,7 @@ pub const File = struct { base: File, arena: Allocator, opt_loc: ?Compilation.EmitLoc, - ) Allocator.Error!?[*:0]u8 { + ) Allocator.Error!?[*:0]const u8 { const loc = opt_loc orelse return null; const slice = if (loc.directory) |directory| try directory.joinZ(arena, &.{loc.basename}) @@ -1071,7 +1017,7 @@ pub const File = struct { sub_prog_node.context.refresh(); defer sub_prog_node.end(); - try llvm_object.emit(comp, .{ + try llvm_object.emit(.{ .pre_ir_path = comp.verbose_llvm_ir, .pre_bc_path = comp.verbose_llvm_bc, .bin_path = try base.resolveEmitLoc(arena, .{ @@ -1079,8 +1025,8 @@ pub const File = struct { .basename = base.intermediary_basename.?, }), .asm_path = try base.resolveEmitLoc(arena, comp.emit_asm), - .post_llvm_ir_path = try base.resolveEmitLoc(arena, comp.emit_llvm_ir), - .post_llvm_bc_path = try base.resolveEmitLoc(arena, comp.emit_llvm_bc), + .post_ir_path = try base.resolveEmitLoc(arena, comp.emit_llvm_ir), + .post_bc_path = try base.resolveEmitLoc(arena, comp.emit_llvm_bc), .is_debug = comp.root_mod.optimize_mode == .Debug, .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, diff --git a/src/link/C.zig b/src/link/C.zig index 3592052cf8..669587bda9 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -135,6 +135,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = file, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index c02aecf2db..98c649ca84 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -14,9 +14,10 @@ nxcompat: bool, dynamicbase: bool, /// TODO this and minor_subsystem_version should be combined into one property and left as /// default or populated together. They should not be separate fields. -major_subsystem_version: u32, -minor_subsystem_version: u32, +major_subsystem_version: u16, +minor_subsystem_version: u16, lib_dirs: []const []const u8, +entry_addr: ?u32, ptr_width: PtrWidth, page_size: u32, @@ -238,7 +239,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*Coff { - if (build_options.only_c) unreachable; const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .coff); @@ -390,6 +390,7 @@ pub fn createEmpty( .emit = emit, .stack_size = options.stack_size orelse 16777216, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), + .print_gc_sections = options.print_gc_sections, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, .disable_lld_caching = options.disable_lld_caching, @@ -422,6 +423,8 @@ pub fn createEmpty( .major_subsystem_version = options.major_subsystem_version orelse 6, .minor_subsystem_version = options.minor_subsystem_version orelse 0, .lib_dirs = options.lib_dirs, + .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse + return error.EntryAddressTooBig, }; const use_llvm = comp.config.use_llvm; @@ -2329,8 +2332,8 @@ fn writeHeader(self: *Coff) !void { .minor_operating_system_version = 0, .major_image_version = 0, .minor_image_version = 0, - .major_subsystem_version = self.major_subsystem_version, - .minor_subsystem_version = self.minor_subsystem_version, + .major_subsystem_version = @intCast(self.major_subsystem_version), + .minor_subsystem_version = @intCast(self.minor_subsystem_version), .win32_version_value = 0, .size_of_image = size_of_image, .size_of_headers = size_of_headers, @@ -2342,7 +2345,7 @@ fn writeHeader(self: *Coff) !void { .size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_commit = default_size_of_heap_commit, .loader_flags = 0, - .number_of_rva_and_sizes = @as(u32, @intCast(self.data_directories.len)), + .number_of_rva_and_sizes = @intCast(self.data_directories.len), }; writer.writeAll(mem.asBytes(&opt_header)) catch unreachable; }, diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index ad9e18fa22..a94644a41e 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -21,7 +21,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -30,7 +31,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (self.base.comp.module != null) blk: { + const module_obj_path: ?[]const u8 = if (comp.module != null) blk: { try self.flushModule(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { @@ -45,12 +46,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod sub_prog_node.context.refresh(); defer sub_prog_node.end(); - const is_lib = self.base.comp.config.output_mode == .Lib; - const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib; - const is_exe_or_dyn_lib = is_dyn_lib or self.base.comp.config.output_mode == .Exe; + const is_lib = comp.config.output_mode == .Lib; + const is_dyn_lib = comp.config.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe; const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib; - const target = self.base.comp.root_mod.resolved_target.result; - const optimize_mode = self.base.comp.root_mod.optimize_mode; + const target = comp.root_mod.resolved_target.result; + const optimize_mode = comp.root_mod.optimize_mode; // See link/Elf.zig for comments on how this mechanism works. const id_symlink_basename = "lld.id"; @@ -85,8 +86,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod man.hash.addListOfBytes(self.lib_dirs); man.hash.add(comp.skip_linker_dependencies); if (comp.config.link_libc) { - man.hash.add(self.base.comp.libc_installation != null); - if (self.base.comp.libc_installation) |libc_installation| { + man.hash.add(comp.libc_installation != null); + if (comp.libc_installation) |libc_installation| { man.hash.addBytes(libc_installation.crt_dir.?); if (target.abi == .msvc) { man.hash.addBytes(libc_installation.msvc_lib_dir.?); @@ -94,7 +95,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } } - try link.hashAddSystemLibs(&man, self.base.comp.system_libs); + try link.hashAddSystemLibs(&man, comp.system_libs); man.hash.addListOfBytes(self.base.force_undefined_symbols.keys()); man.hash.addOptional(self.subsystem); man.hash.add(comp.config.is_test); @@ -136,7 +137,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod }; } - if (self.base.comp.config.output_mode == .Obj) { + if (comp.config.output_mode == .Obj) { // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. @@ -161,7 +162,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } else { // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(self.base.allocator); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // We will invoke ourselves as a child process to gain access to LLD. // This is necessary because LLD does not behave properly as a library - @@ -192,7 +193,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), } } - if (self.base.comp.config.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { try argv.append(try allocPrint(arena, "-STACK:{d}", .{self.base.stack_size})); } try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{self.image_base})); @@ -242,7 +243,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } if (comp.config.link_libc) { - if (self.base.comp.libc_installation) |libc_installation| { + if (comp.libc_installation) |libc_installation| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); if (target.abi == .msvc) { @@ -287,7 +288,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod if (self.subsystem) |explicit| break :blk explicit; switch (target.os.tag) { .windows => { - if (self.base.comp.module) |module| { + if (comp.module) |module| { if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) break :blk null; if (module.stage1_flags.have_c_main or comp.config.is_test or @@ -415,13 +416,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(try comp.get_libc_crt_file(arena, "uuid.lib")); for (mingw.always_link_libs) |name| { - if (!self.base.comp.system_libs.contains(name)) { + if (!comp.system_libs.contains(name)) { const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); } } } else { - const lib_str = switch (self.base.comp.config.link_mode) { + const lib_str = switch (comp.config.link_mode) { .Dynamic => "", .Static => "lib", }; @@ -429,7 +430,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod .Debug => "d", else => "", }; - switch (self.base.comp.config.link_mode) { + switch (comp.config.link_mode) { .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), } @@ -448,7 +449,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } else { try argv.append("-NODEFAULTLIB"); if (!is_lib and comp.config.entry == null) { - if (self.base.comp.module) |module| { + if (comp.module) |module| { if (module.stage1_flags.have_winmain_crt_startup) { try argv.append("-ENTRY:WinMainCRTStartup"); } else { @@ -485,8 +486,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); } - try argv.ensureUnusedCapacity(self.base.comp.system_libs.count()); - for (self.base.comp.system_libs.keys()) |key| { + try argv.ensureUnusedCapacity(comp.system_libs.count()); + for (comp.system_libs.keys()) |key| { const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); if (comp.crt_files.get(lib_basename)) |crt_file| { argv.appendAssumeCapacity(crt_file.full_object_path); @@ -512,7 +513,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod return error.DllImportLibraryNotFound; } - if (self.base.comp.verbose_link) { + if (comp.verbose_link) { // Skip over our own name so that the LLD linker name is the first argv item. Compilation.dump_argv(argv.items[1..]); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index faaf5100a4..cabaed1add 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1745,6 +1745,7 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void { } pub fn writeDbgAbbrev(self: *Dwarf) !void { + const gpa = self.allocator; // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. // zig fmt: off @@ -1887,7 +1888,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code; - try debug_abbrev.resize(wasm_file.base.allocator, needed_size); + try debug_abbrev.resize(gpa, needed_size); debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf; }, else => unreachable, @@ -2236,6 +2237,7 @@ fn writeDbgInfoNopsToArrayList( pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { const comp = self.bin_file.comp; + const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; const target_endian = target.cpu.arch.endian(); const ptr_width_bytes = self.ptrWidthBytes(); @@ -2301,7 +2303,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code; - try debug_ranges.resize(wasm_file.base.allocator, needed_size); + try debug_ranges.resize(gpa, needed_size); @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items); }, else => unreachable, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 47b4712b57..67478a59e8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -230,7 +230,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*Elf { - if (build_options.only_c) unreachable; const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .elf); @@ -380,6 +379,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, .file = null, @@ -1175,7 +1175,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const lib_name = flag["-l".len..]; success: { - if (!self.isStatic()) { + if (!self.base.isStatic()) { if (try self.accessLibPath(&test_path, &checked_paths, lc.crt_dir.?, lib_name, .Dynamic)) break :success; } @@ -1664,7 +1664,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append(p); } } else { - if (!self.isStatic()) { + if (!self.base.isStatic()) { if (target.dynamic_linker.get()) |path| { try argv.append("-dynamic-linker"); try argv.append(path); @@ -1742,7 +1742,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append("now"); } - if (self.isStatic()) { + if (self.base.isStatic()) { try argv.append("-static"); } else if (self.base.isDynLib()) { try argv.append("-shared"); @@ -2023,7 +2023,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { // TODO I think technically we should re-use the mechanism used by the frontend here. // Maybe we should hoist search-strategy all the way here? for (self.lib_dirs) |lib_dir| { - if (!self.isStatic()) { + if (!self.base.isStatic()) { if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Dynamic)) break :success; } @@ -3598,7 +3598,7 @@ fn initSyntheticSections(self: *Elf) !void { // In this case, if we do generate .interp section and segment, we will get // a segfault in the dynamic linker trying to load a binary that is static // and doesn't contain .dynamic section. - if (self.isStatic() and !comp.config.pie) break :blk false; + if (self.base.isStatic() and !comp.config.pie) break :blk false; break :blk target.dynamic_linker.get() != null; }; if (needs_interp) { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 9f57dab283..a7b876f619 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -377,8 +377,8 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { } pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { - const is_static = elf_file.isStatic(); - const is_dyn_lib = elf_file.isDynLib(); + const is_static = elf_file.base.isStatic(); + const is_dyn_lib = elf_file.base.isDynLib(); const file_ptr = self.file(elf_file).?; const rels = self.relocs(elf_file); var i: usize = 0; @@ -660,7 +660,7 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { fn outputType(elf_file: *Elf) u2 { const comp = elf_file.base.comp; - assert(!elf_file.isRelocatable()); + assert(!elf_file.base.isRelocatable()); return switch (elf_file.base.comp.config.output_mode) { .Obj => unreachable, .Lib => 0, diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 35df214b18..b6dced258c 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -250,7 +250,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.getString(shdr.sh_name); - if (elf_file.isRelocatable()) break :blk name; + if (elf_file.base.isRelocatable()) break :blk name; if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", @@ -278,7 +278,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem }; const flags = blk: { var flags = shdr.sh_flags; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); } break :blk switch (@"type") { @@ -520,7 +520,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.isDynLib()) break :blk false; + if (!elf_file.base.isDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index b6be4013ce..3a8a77de68 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -370,7 +370,7 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.isDynLib()) break :blk false; + if (!elf_file.base.isDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index e8500e8aa9..75401824ab 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -271,7 +271,7 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize { } } - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { offset += 4; // NULL terminator } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9af563b047..35d5c85c2d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -155,6 +155,7 @@ frameworks: []const Framework, install_name: ?[]const u8, /// Path to entitlements file. entitlements: ?[]const u8, +compatibility_version: ?std.SemanticVersion, /// When adding a new field, remember to update `hashAddFrameworks`. pub const Framework = struct { @@ -185,7 +186,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*MachO { - if (build_options.only_c) unreachable; const target = comp.root_mod.resolved_target.result; const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; @@ -295,6 +295,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, @@ -315,6 +316,7 @@ pub fn createEmpty( .frameworks = options.frameworks, .install_name = options.install_name, .entitlements = options.entitlements, + .compatibility_version = options.compatibility_version, }; if (use_llvm and comp.module != null) { @@ -632,7 +634,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No }); { const platform = Platform.fromTarget(target); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(self); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else if (platform.isVersionMinCompatible()) { @@ -2237,10 +2239,10 @@ fn allocateGlobal(self: *MachO) !u32 { return index; } -pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.got_table.lookup.contains(target)) return; +pub fn addGotEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.got_table.lookup.contains(reloc_target)) return; const gpa = self.base.comp.gpa; - const got_index = try self.got_table.allocateEntry(gpa, target); + const got_index = try self.got_table.allocateEntry(gpa, reloc_target); if (self.got_section_index == null) { self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{ .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, @@ -2249,20 +2251,22 @@ pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.mode == .incremental) { try self.writeOffsetTableEntry(got_index); self.got_table_count_dirty = true; - self.markRelocsDirtyByTarget(target); + self.markRelocsDirtyByTarget(reloc_target); } } -pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.stub_table.lookup.contains(target)) return; - const gpa = self.base.comp.gpa; - const stub_index = try self.stub_table.allocateEntry(gpa, target); +pub fn addStubEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.stub_table.lookup.contains(reloc_target)) return; + const comp = self.base.comp; + const gpa = comp.gpa; + const cpu_arch = comp.root_mod.resolved_target.result.cpu.arch; + const stub_index = try self.stub_table.allocateEntry(gpa, reloc_target); if (self.stubs_section_index == null) { self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{ .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved2 = stubs.stubSize(target.cpu.arch), + .reserved2 = stubs.stubSize(cpu_arch), }); self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{ .flags = macho.S_REGULAR | @@ -2276,14 +2280,14 @@ pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.mode == .incremental) { try self.writeStubTableEntry(stub_index); self.stub_table_count_dirty = true; - self.markRelocsDirtyByTarget(target); + self.markRelocsDirtyByTarget(reloc_target); } } -pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.tlv_ptr_table.lookup.contains(target)) return; +pub fn addTlvPtrEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.tlv_ptr_table.lookup.contains(reloc_target)) return; const gpa = self.base.comp.gpa; - _ = try self.tlv_ptr_table.allocateEntry(gpa, target); + _ = try self.tlv_ptr_table.allocateEntry(gpa, reloc_target); if (self.tlv_ptr_section_index == null) { self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 5185b93386..ad069b845e 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -342,19 +342,22 @@ pub const SplitIntoAtomsError = error{ }; pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) SplitIntoAtomsError!void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name }); try self.splitRegularSections(macho_file, object_id); try self.parseEhFrameSection(macho_file, object_id); try self.parseUnwindInfo(macho_file, object_id); - try self.parseDataInCode(macho_file.base.allocator); + try self.parseDataInCode(gpa); } /// Splits input regular sections into Atoms. /// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section /// into subsections where each subsection then represents an Atom. pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const sections = self.getSourceSections(); @@ -555,7 +558,8 @@ fn createAtomFromSubsection( alignment: Alignment, out_sect_id: u8, ) !Atom.Index { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment, @@ -671,7 +675,8 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void log.debug("parsing __TEXT,__eh_frame section", .{}); - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; if (macho_file.eh_frame_section_index == null) { macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{}); @@ -767,7 +772,8 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void } fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; const sect_id = self.unwind_info_sect_id orelse { diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index a92126c07e..02cf68e46e 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -1,7 +1,8 @@ //! An algorithm for dead stripping of unreferenced Atoms. pub fn gcAtoms(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 8223c830e0..4b51d09683 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -1,5 +1,6 @@ pub fn scanRelocs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; for (macho_file.objects.items, 0..) |*object, object_id| { var cies = std.AutoHashMap(u32, void).init(gpa); @@ -37,7 +38,8 @@ pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) error const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var size: u32 = 0; for (macho_file.objects.items, 0..) |*object, object_id| { @@ -89,7 +91,8 @@ pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void { const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa); defer { diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 8bcf439354..e155a7a8ed 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -63,7 +63,7 @@ fn calcLCsSize(m: *MachO, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 { } // LC_RPATH { - var it = RpathIterator.init(gpa, m.rpath_list); + var it = RpathIterator.init(gpa, m.base.rpath_list); defer it.deinit(); while (try it.next()) |rpath| { sizeofcmds += calcInstallNameLen( @@ -189,17 +189,20 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, lc_writer: anytype) !void { } } -pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { - assert(options.output_mode == .Lib and options.link_mode == .Dynamic); - const emit = options.emit.?; - const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); - defer if (options.install_name == null) gpa.free(install_name); - const curr = options.version orelse std.SemanticVersion{ +pub fn writeDylibIdLC(macho_file: *MachO, lc_writer: anytype) !void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + assert(comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic); + const emit = macho_file.base.emit; + const install_name = macho_file.install_name orelse + try emit.directory.join(gpa, &.{emit.sub_path}); + defer if (macho_file.install_name == null) gpa.free(install_name); + const curr = comp.version orelse std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, }; - const compat = options.compatibility_version orelse std.SemanticVersion{ + const compat = macho_file.compatibility_version orelse std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, @@ -237,8 +240,11 @@ const RpathIterator = struct { } }; -pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { - var it = RpathIterator.init(gpa, options.rpath_list); +pub fn writeRpathLCs(macho_file: *MachO, lc_writer: anytype) !void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + + var it = RpathIterator.init(gpa, macho_file.base.rpath_list); defer it.deinit(); while (try it.next()) |rpath| { @@ -467,13 +473,14 @@ pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion { }; } -pub fn inferSdkVersion(gpa: Allocator, comp: *const Compilation) ?std.SemanticVersion { +pub fn inferSdkVersion(macho_file: *MachO) ?std.SemanticVersion { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const macho_file = comp.bin_file.cast(MachO).?; - const sdk_layout = macho_file.sdk_layout orelse return null; const sdk_dir = switch (sdk_layout) { .sdk => comp.sysroot.?, diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index 774a8f7344..f080de7f80 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -68,7 +68,8 @@ pub fn createThunks(macho_file: *MachO, sect_id: u8) !void { const header = &macho_file.sections.items(.header)[sect_id]; if (header.size == 0) return; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?; header.size = 0; @@ -245,7 +246,8 @@ fn scanRelocs( macho_file.getSymbol(target).n_value, }); - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target_sym = macho_file.getSymbol(target); const thunk = &macho_file.thunks.items[thunk_index]; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 06fede6e1b..f8715aa447 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -15,7 +15,7 @@ pub fn linkWithZld( const arena = arena_allocator.allocator(); const directory = emit.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{emit.?.sub_path}); + const full_out_path = try directory.join(arena, &[_][]const u8{emit.sub_path}); const opt_zcu = macho_file.base.comp.module; // If there is no Zig code to compile, then we should skip flushing the output file because it @@ -71,14 +71,14 @@ pub fn linkWithZld( // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.add(stack_size); - man.hash.addOptional(macho_file.pagezero_vmsize); - man.hash.addOptional(macho_file.headerpad_size); + man.hash.add(macho_file.pagezero_vmsize); + man.hash.add(macho_file.headerpad_size); man.hash.add(macho_file.headerpad_max_install_names); man.hash.add(macho_file.base.gc_sections); man.hash.add(macho_file.dead_strip_dylibs); man.hash.add(macho_file.base.comp.root_mod.strip); try MachO.hashAddFrameworks(&man, macho_file.frameworks); - man.hash.addListOfBytes(macho_file.rpath_list); + man.hash.addListOfBytes(macho_file.base.rpath_list); if (is_dyn_lib) { man.hash.addOptionalBytes(macho_file.install_name); man.hash.addOptional(comp.version); @@ -151,7 +151,7 @@ pub fn linkWithZld( try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); } } else { - const sub_path = emit.?.sub_path; + const sub_path = emit.sub_path; const old_file = macho_file.base.file; // TODO is this needed at all? defer macho_file.base.file = old_file; @@ -241,7 +241,7 @@ pub fn linkWithZld( try argv.append(@tagName(platform.os_tag)); try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version})); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(macho_file); if (sdk_version) |ver| { try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); } else { @@ -254,13 +254,13 @@ pub fn linkWithZld( try argv.append(syslibroot); } - for (macho_file.rpath_list) |rpath| { + for (macho_file.base.rpath_list) |rpath| { try argv.append("-rpath"); try argv.append(rpath); } try argv.appendSlice(&.{ - "-pagezero_size", try std.fmt.allocPrint(arena, "0x{x}", .{macho_file.pagezero_size}), + "-pagezero_size", try std.fmt.allocPrint(arena, "0x{x}", .{macho_file.pagezero_vmsize}), "-headerpad_size", try std.fmt.allocPrint(arena, "0x{x}", .{macho_file.headerpad_size}), }); @@ -558,7 +558,7 @@ pub fn linkWithZld( }); { const platform = Platform.fromTarget(target); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(macho_file); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else { @@ -609,7 +609,8 @@ pub fn linkWithZld( } fn createSegments(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const page_size = MachO.getPageSize(target.cpu.arch); const aligned_pagezero_vmsize = mem.alignBackward(u64, macho_file.pagezero_vmsize, page_size); @@ -683,7 +684,8 @@ fn createSegments(macho_file: *MachO) !void { } fn writeAtoms(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const slice = macho_file.sections.slice(); for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { @@ -758,7 +760,8 @@ fn writeDyldPrivateAtom(macho_file: *MachO) !void { fn writeThunks(macho_file: *MachO) !void { const target = macho_file.base.comp.root_mod.resolved_target.result; assert(target.cpu.arch == .aarch64); - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const sect_id = macho_file.text_section_index orelse return; const header = macho_file.sections.items(.header)[sect_id]; @@ -778,7 +781,8 @@ fn writeThunks(macho_file: *MachO) !void { } fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const header = macho_file.sections.items(.header)[sect_id]; const capacity = math.cast(usize, header.size) orelse return error.Overflow; var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity); @@ -792,7 +796,8 @@ fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void { } fn writeStubs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?]; @@ -815,7 +820,8 @@ fn writeStubs(macho_file: *MachO) !void { } fn writeStubHelpers(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?]; @@ -859,7 +865,8 @@ fn writeStubHelpers(macho_file: *MachO) !void { } fn writeLaSymbolPtrs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target = macho_file.base.comp.root_mod.resolved_target.result; const cpu_arch = target.cpu.arch; const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?]; @@ -892,7 +899,8 @@ fn pruneAndSortSections(macho_file: *MachO) !void { } }; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len); defer entries.deinit(); diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index b59a527c5a..2025372323 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -31,8 +31,6 @@ pub fn createEmpty( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*NvPtx { - if (build_options.only_c) unreachable; - const target = comp.root_mod.resolved_target.result; const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; @@ -55,6 +53,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse false, + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 3fe6aee009..ceabfe2864 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -318,6 +318,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 16777216, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, @@ -1314,8 +1315,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*Plan9 { - if (build_options.only_c) unreachable; - const target = comp.root_mod.resolved_target.result; const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; @@ -1525,7 +1524,13 @@ pub fn getDeclVAddr( return undefined; } -pub fn lowerAnonDecl(self: *Plan9, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl( + self: *Plan9, + decl_val: InternPool.Index, + explicit_alignment: InternPool.Alignment, + src_loc: Module.SrcLoc, +) !codegen.Result { + _ = explicit_alignment; // This is basically the same as lowerUnnamedConst. // example: // const ty = mod.intern_pool.typeOf(decl_val).toType(); diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 853d97b014..5124ff19f6 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -24,7 +24,6 @@ const SpirV = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; const assert = std.debug.assert; const log = std.log.scoped(.link); @@ -65,6 +64,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse false, + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse 0, .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, @@ -98,8 +98,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*SpirV { - if (build_options.only_c) unreachable; - const target = comp.root_mod.resolved_target.result; const use_lld = build_options.have_llvm and comp.config.use_lld; const use_llvm = comp.config.use_llvm; @@ -194,7 +192,9 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No const spv = &self.object.spv; + const gpa = comp.gpa; const target = comp.getTarget(); + try writeCapabilities(spv, target); try writeMemoryModel(spv, target); @@ -214,11 +214,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. // We're using : as separator, which is a reserved character. - const escaped_name = try std.Uri.escapeString(self.base.allocator, name); - defer self.base.allocator.free(escaped_name); + const escaped_name = try std.Uri.escapeString(gpa, name); + defer gpa.free(escaped_name); try error_info.writer().print(":{s}", .{escaped_name}); } - try spv.sections.debug_strings.emit(spv.gpa, .OpSourceExtension, .{ + try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{ .extension = error_info.items, }); @@ -226,6 +226,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No } fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { + const gpa = spv.gpa; // TODO: Integrate with a hypothetical feature system const caps: []const spec.Capability = switch (target.os.tag) { .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .GenericPointer }, @@ -235,13 +236,15 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { }; for (caps) |cap| { - try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{ + try spv.sections.capabilities.emit(gpa, .OpCapability, .{ .capability = cap, }); } } fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { + const gpa = spv.gpa; + const addressing_model = switch (target.os.tag) { .opencl => switch (target.cpu.arch) { .spirv32 => spec.AddressingModel.Physical32, @@ -260,7 +263,7 @@ fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { }; // TODO: Put this in a proper section. - try spv.sections.extensions.emit(spv.gpa, .OpMemoryModel, .{ + try spv.sections.extensions.emit(gpa, .OpMemoryModel, .{ .addressing_model = addressing_model, .memory_model = memory_model, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 874cc2d9b3..7ec9506751 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -378,7 +378,6 @@ pub fn open( emit: Compilation.Emit, options: link.File.OpenOptions, ) !*Wasm { - if (build_options.only_c) unreachable; const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; assert(target.ofmt == .wasm); @@ -549,6 +548,7 @@ pub fn createEmpty( .comp = comp, .emit = emit, .gc_sections = options.gc_sections orelse (output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, @@ -1893,7 +1893,12 @@ pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: lin return target_symbol_index; } -pub fn deleteDeclExport(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { +pub fn deleteDeclExport( + wasm: *Wasm, + decl_index: InternPool.DeclIndex, + name: InternPool.NullTerminatedString, +) void { + _ = name; if (wasm.llvm_object) |_| return; const atom_index = wasm.decls.get(decl_index) orelse return; const sym_index = wasm.getAtom(atom_index).sym_index; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 610c534c88..f0c21b8c89 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -883,6 +883,8 @@ fn assertEnd(reader: anytype) !void { /// Parses an object file into atoms, for code and data sections pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index { + const comp = wasm.base.comp; + const gpa = comp.gpa; const symbol = &object.symtable[symbol_index]; const relocatable_data: RelocatableData = switch (symbol.tag) { .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)], @@ -900,7 +902,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 }; const final_index = try wasm.getMatchingSegment(object_index, symbol_index); const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + const atom = try wasm.managed_atoms.addOne(gpa); atom.* = Atom.empty; try wasm.appendAtomAtIndex(final_index, atom_index); @@ -910,7 +912,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 atom.alignment = relocatable_data.getAlignment(object); atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]); atom.original_offset = relocatable_data.offset; - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom_index); + try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index); const segment: *Wasm.Segment = &wasm.segments.items[final_index]; if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned segment.alignment = segment.alignment.max(atom.alignment); @@ -927,7 +929,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => { - try wasm.function_table.put(wasm.base.allocator, .{ + try wasm.function_table.put(gpa, .{ .file = object_index, .index = reloc.index, }, 0); @@ -938,7 +940,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 const sym = object.symtable[reloc.index]; if (sym.tag != .global) { try wasm.got_symbols.append( - wasm.base.allocator, + gpa, .{ .file = object_index, .index = reloc.index }, ); } diff --git a/src/main.zig b/src/main.zig index 26e1d79a7b..f412658a67 100644 --- a/src/main.zig +++ b/src/main.zig @@ -874,8 +874,8 @@ fn buildOutputType( var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var subsystem: ?std.Target.SubSystem = null; - var major_subsystem_version: ?u32 = null; - var minor_subsystem_version: ?u32 = null; + var major_subsystem_version: ?u16 = null; + var minor_subsystem_version: ?u16 = null; var enable_link_snapshots: bool = false; var debug_incremental: bool = false; var install_name: ?[]const u8 = null; @@ -2321,21 +2321,17 @@ fn buildOutputType( _ = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "--major-subsystem-version")) { const major = linker_args_it.nextOrFatal(); - major_subsystem_version = std.fmt.parseUnsigned( - u32, - major, - 10, - ) catch |err| { - fatal("unable to parse major subsystem version '{s}': {s}", .{ major, @errorName(err) }); + major_subsystem_version = std.fmt.parseUnsigned(u16, major, 10) catch |err| { + fatal("unable to parse major subsystem version '{s}': {s}", .{ + major, @errorName(err), + }); }; } else if (mem.eql(u8, arg, "--minor-subsystem-version")) { const minor = linker_args_it.nextOrFatal(); - minor_subsystem_version = std.fmt.parseUnsigned( - u32, - minor, - 10, - ) catch |err| { - fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) }); + minor_subsystem_version = std.fmt.parseUnsigned(u16, minor, 10) catch |err| { + fatal("unable to parse minor subsystem version '{s}': {s}", .{ + minor, @errorName(err), + }); }; } else if (mem.eql(u8, arg, "-framework")) { try frameworks.put(arena, linker_args_it.nextOrFatal(), .{}); -- cgit v1.2.3 From f2564318385ea90b08af044ae32ddef26e6346bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Dec 2023 15:22:31 -0700 Subject: fix compilation errors when enabling llvm --- src/Compilation.zig | 57 +++++++++++----- src/Module.zig | 2 +- src/Package/Module.zig | 76 +++++++++++---------- src/Sema.zig | 37 ++++++++-- src/glibc.zig | 23 ++++--- src/libcxx.zig | 182 +++++++++++++++++++++++++------------------------ src/libtsan.zig | 102 ++++++++++++++------------- src/libunwind.zig | 10 +-- src/link/Coff.zig | 4 ++ src/link/Coff/lld.zig | 23 +++---- src/link/Elf.zig | 20 +++--- src/main.zig | 2 +- src/mingw.zig | 21 ++++-- src/musl.zig | 39 +++++++---- src/wasi_libc.zig | 20 ++++-- 15 files changed, 363 insertions(+), 255 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index 7f3d696ecb..7314da3960 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1410,7 +1410,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .inherited = .{}, .global = options.config, .parent = options.root_mod, - .builtin_mod = options.root_mod.deps.get("builtin").?, + .builtin_mod = options.root_mod.getBuiltinDependency(), }); const zcu = try arena.create(Module); @@ -3830,6 +3830,7 @@ pub fn obtainCObjectCacheManifest( // that apply both to @cImport and compiling C objects. No linking stuff here! // Also nothing that applies only to compiling .zig code. man.hash.add(owner_mod.sanitize_c); + man.hash.add(owner_mod.sanitize_thread); man.hash.addListOfBytes(owner_mod.cc_argv); man.hash.add(comp.config.link_libcpp); @@ -3970,10 +3971,13 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module const dep_basename = std.fs.path.basename(out_dep_path); try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + }, + .incremental => {}, } const digest = man.final(); @@ -4425,10 +4429,15 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P const dep_basename = std.fs.path.basename(dep_file_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| { + if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + } + }, + .incremental => {}, } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { @@ -4724,10 +4733,13 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const dep_basename = std.fs.path.basename(out_dep_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + }, + .incremental => {}, } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { @@ -4799,10 +4811,13 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 for (dependencies_list.items) |dep_file_path| { try man.addFilePost(dep_file_path); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addFilePost(dep_file_path); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addFilePost(dep_file_path); + }, + .incremental => {}, } } @@ -6247,7 +6262,9 @@ pub fn build_crt_file( output_mode: std.builtin.OutputMode, misc_task_tag: MiscTask, prog_node: *std.Progress.Node, - c_source_files: []const CSourceFile, + /// These elements have to get mutated to add the owner module after it is + /// created within this function. + c_source_files: []CSourceFile, ) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -6305,6 +6322,10 @@ pub fn build_crt_file( .builtin_mod = null, }); + for (c_source_files) |*item| { + item.owner = root_mod; + } + const sub_compilation = try Compilation.create(gpa, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, diff --git a/src/Module.zig b/src/Module.zig index 6d5a2f78a6..f2049df378 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5275,7 +5275,7 @@ pub fn populateTestFunctions( ) !void { const gpa = mod.gpa; const ip = &mod.intern_pool; - const builtin_mod = mod.main_mod.deps.get("builtin").?; + const builtin_mod = mod.main_mod.getBuiltinDependency(); const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file; const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?); const builtin_namespace = mod.namespacePtr(root_decl.src_namespace); diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 2d54c9296c..0ebd951cc6 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -310,7 +310,39 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { break :b buf.items[0 .. buf.items.len - 1 :0].ptr; }; - const builtin_mod = options.builtin_mod orelse b: { + const mod = try arena.create(Module); + mod.* = .{ + .root = options.paths.root, + .root_src_path = options.paths.root_src_path, + .fully_qualified_name = options.fully_qualified_name, + .resolved_target = .{ + .result = target, + .is_native_os = resolved_target.is_native_os, + .is_native_abi = resolved_target.is_native_abi, + .llvm_cpu_features = llvm_cpu_features, + }, + .optimize_mode = optimize_mode, + .single_threaded = single_threaded, + .error_tracing = error_tracing, + .valgrind = valgrind, + .pic = pic, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, + .stack_check = stack_check, + .stack_protector = stack_protector, + .code_model = code_model, + .red_zone = red_zone, + .sanitize_c = sanitize_c, + .sanitize_thread = sanitize_thread, + .unwind_tables = unwind_tables, + .cc_argv = options.cc_argv, + .structured_cfg = structured_cfg, + .builtin_file = null, + }; + + const opt_builtin_mod = options.builtin_mod orelse b: { + if (!options.global.have_zcu) break :b null; + const generated_builtin_source = try Builtin.generate(.{ .target = target, .zig_backend = zig_backend, @@ -388,38 +420,10 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { break :b new; }; - const mod = try arena.create(Module); - mod.* = .{ - .root = options.paths.root, - .root_src_path = options.paths.root_src_path, - .fully_qualified_name = options.fully_qualified_name, - .resolved_target = .{ - .result = target, - .is_native_os = resolved_target.is_native_os, - .is_native_abi = resolved_target.is_native_abi, - .llvm_cpu_features = llvm_cpu_features, - }, - .optimize_mode = optimize_mode, - .single_threaded = single_threaded, - .error_tracing = error_tracing, - .valgrind = valgrind, - .pic = pic, - .strip = strip, - .omit_frame_pointer = omit_frame_pointer, - .stack_check = stack_check, - .stack_protector = stack_protector, - .code_model = code_model, - .red_zone = red_zone, - .sanitize_c = sanitize_c, - .sanitize_thread = sanitize_thread, - .unwind_tables = unwind_tables, - .cc_argv = options.cc_argv, - .structured_cfg = structured_cfg, - .builtin_file = null, - }; - - try mod.deps.ensureUnusedCapacity(arena, 1); - mod.deps.putAssumeCapacityNoClobber("builtin", builtin_mod); + if (opt_builtin_mod) |builtin_mod| { + try mod.deps.ensureUnusedCapacity(arena, 1); + mod.deps.putAssumeCapacityNoClobber("builtin", builtin_mod); + } return mod; } @@ -462,8 +466,10 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P return mod; } -pub fn getBuiltinDependency(m: *Module) *Module { - return m.deps.values()[0]; +pub fn getBuiltinDependency(m: Module) *Module { + const result = m.deps.values()[0]; + assert(result.isBuiltin()); + return result; } const Module = @This(); diff --git a/src/Sema.zig b/src/Sema.zig index 26afd285c7..c2e05e4a2c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5759,14 +5759,39 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr }; return sema.failWithOwnedErrorMsg(&child_block, msg); } - const c_import_mod = try Package.Module.create(comp.arena.allocator(), .{ - .root = .{ - .root_dir = Compilation.Directory.cwd(), - .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + const parent_mod = parent_block.ownerModule(); + const c_import_mod = Package.Module.create(comp.arena.allocator(), .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = Compilation.Directory.cwd(), + .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + }, + .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), }, - .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), .fully_qualified_name = c_import_res.out_zig_path, - }); + .cc_argv = parent_mod.cc_argv, + .inherited = .{}, + .global = comp.config, + .parent = parent_mod, + .builtin_mod = parent_mod.getBuiltinDependency(), + }) catch |err| switch (err) { + // None of these are possible because we are creating a package with + // the exact same configuration as the parent package, which already + // passed these checks. + error.ValgrindUnsupportedOnTarget => unreachable, + error.TargetRequiresSingleThreaded => unreachable, + error.BackendRequiresSingleThreaded => unreachable, + error.TargetRequiresPic => unreachable, + error.PieRequiresPic => unreachable, + error.DynamicLinkingRequiresPic => unreachable, + error.TargetHasNoRedZone => unreachable, + error.StackCheckUnsupportedByTarget => unreachable, + error.StackProtectorUnsupportedByTarget => unreachable, + error.StackProtectorUnavailableWithoutLibC => unreachable, + + else => |e| return e, + }; const result = mod.importPkg(c_import_mod) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); diff --git a/src/glibc.zig b/src/glibc.zig index 0502bc3799..d478c6e939 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -170,7 +170,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const target = comp.getTarget(); + const target = comp.root_mod.resolved_target.result; const target_ver = target.os.version_range.linux.glibc; const start_old_init_fini = target_ver.order(.{ .major = 2, .minor = 33, .patch = 0 }) != .gt; @@ -196,12 +196,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.S"), .cache_exempt_flags = args.items, + .owner = comp.root_mod, }, - }); + }; + return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &files); }, .crtn_o => { var args = std.ArrayList([]const u8).init(arena); @@ -215,12 +217,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.S"), .cache_exempt_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &files); }, .scrt1_o => { const start_o: Compilation.CSourceFile = blk: { @@ -244,6 +248,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr break :blk .{ .src_path = try start_asm_path(comp, arena, src_path), .cache_exempt_flags = args.items, + .owner = undefined, }; }; const abi_note_o: Compilation.CSourceFile = blk: { @@ -263,11 +268,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr break :blk .{ .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), .cache_exempt_flags = args.items, + .owner = undefined, }; }; - return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{ - start_o, abi_note_o, - }); + var files = [_]Compilation.CSourceFile{ start_o, abi_note_o }; + return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files); }, .libc_nonshared_a => { const s = path.sep_str; @@ -364,6 +369,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr files_buf[files_index] = .{ .src_path = try lib_path(comp, arena, dep.path), .cache_exempt_flags = args.items, + .owner = undefined, }; files_index += 1; } @@ -1065,6 +1071,7 @@ fn buildSharedLib( const c_source_files = [1]Compilation.CSourceFile{ .{ .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }), + .owner = undefined, }, }; diff --git a/src/libcxx.zig b/src/libcxx.zig index aedb1f3930..abcbc0187e 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -138,6 +138,50 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { const abi_namespace_arg = try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ @intFromEnum(comp.libcxx_abi_version), }); + + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + .lto = comp.config.lto, + }); + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = comp.config.any_sanitize_thread, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .pic = comp.root_mod.pic, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = try std.ArrayList(Compilation.CSourceFile).initCapacity(arena, libcxx_files.len); for (libcxx_files) |cxx_src| { @@ -224,52 +268,10 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, + .owner = root_mod, }); } - const optimize_mode = comp.compilerRtOptMode(); - const strip = comp.compilerRtStrip(); - - const config = try Compilation.Config.resolve(.{ - .output_mode = output_mode, - .link_mode = link_mode, - .resolved_target = comp.root_mod.resolved_target, - .is_test = false, - .have_zcu = false, - .emit_bin = true, - .root_optimize_mode = optimize_mode, - .root_strip = strip, - .link_libc = true, - .lto = comp.config.lto, - }); - - const root_mod = try Module.create(arena, .{ - .global_cache_directory = comp.global_cache_directory, - .paths = .{ - .root = .{ .root_dir = comp.zig_lib_directory }, - .root_src_path = "", - }, - .fully_qualified_name = "root", - .inherited = .{ - .resolved_target = comp.root_mod.resolved_target, - .strip = strip, - .stack_check = false, - .stack_protector = 0, - .sanitize_c = false, - .sanitize_thread = comp.config.any_sanitize_thread, - .red_zone = comp.root_mod.red_zone, - .omit_frame_pointer = comp.root_mod.omit_frame_pointer, - .valgrind = false, - .optimize_mode = optimize_mode, - .structured_cfg = comp.root_mod.structured_cfg, - .pic = comp.root_mod.pic, - }, - .global = config, - .cc_argv = &.{}, - .parent = null, - .builtin_mod = null, - }); - const sub_compilation = try Compilation.create(comp.gpa, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, @@ -339,6 +341,53 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { const abi_namespace_arg = try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ @intFromEnum(comp.libcxx_abi_version), }); + + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + const unwind_tables = true; + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + .any_unwind_tables = unwind_tables, + .lto = comp.config.lto, + }); + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = comp.config.any_sanitize_thread, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .unwind_tables = unwind_tables, + .pic = comp.root_mod.pic, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = try std.ArrayList(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len); for (libcxxabi_files) |cxxabi_src| { @@ -406,55 +455,10 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, + .owner = root_mod, }); } - const optimize_mode = comp.compilerRtOptMode(); - const strip = comp.compilerRtStrip(); - const unwind_tables = true; - - const config = try Compilation.Config.resolve(.{ - .output_mode = output_mode, - .link_mode = link_mode, - .resolved_target = comp.root_mod.resolved_target, - .is_test = false, - .have_zcu = false, - .emit_bin = true, - .root_optimize_mode = optimize_mode, - .root_strip = strip, - .link_libc = true, - .any_unwind_tables = unwind_tables, - .lto = comp.config.lto, - }); - - const root_mod = try Module.create(arena, .{ - .global_cache_directory = comp.global_cache_directory, - .paths = .{ - .root = .{ .root_dir = comp.zig_lib_directory }, - .root_src_path = "", - }, - .fully_qualified_name = "root", - .inherited = .{ - .resolved_target = comp.root_mod.resolved_target, - .strip = strip, - .stack_check = false, - .stack_protector = 0, - .sanitize_c = false, - .sanitize_thread = comp.config.any_sanitize_thread, - .red_zone = comp.root_mod.red_zone, - .omit_frame_pointer = comp.root_mod.omit_frame_pointer, - .valgrind = false, - .optimize_mode = optimize_mode, - .structured_cfg = comp.root_mod.structured_cfg, - .unwind_tables = unwind_tables, - .pic = comp.root_mod.pic, - }, - .global = config, - .cc_argv = &.{}, - .parent = null, - .builtin_mod = null, - }); - const sub_compilation = try Compilation.create(comp.gpa, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, diff --git a/src/libtsan.zig b/src/libtsan.zig index 553597e485..a736c32713 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -34,6 +34,52 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { .basename = basename, }; + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + }); + + const common_flags = [_][]const u8{ + "-DTSAN_CONTAINS_UBSAN=0", + }; + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = false, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .pic = true, + }, + .global = config, + .cc_argv = &common_flags, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); try c_source_files.ensureUnusedCapacity(tsan_sources.len); @@ -50,8 +96,9 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { try cflags.append("-fno-rtti"); c_source_files.appendAssumeCapacity(.{ - .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", tsan_src }), + .src_path = try comp.zig_lib_directory.join(arena, &.{ "tsan", tsan_src }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -74,6 +121,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files.appendAssumeCapacity(.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", tsan_src }), .extra_flags = cflags.items, + .owner = root_mod, }); } { @@ -94,6 +142,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files.appendAssumeCapacity(.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", asm_source }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -117,6 +166,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", common_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -141,6 +191,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -161,6 +212,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -189,56 +241,10 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "interception", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } - const common_flags = [_][]const u8{ - "-DTSAN_CONTAINS_UBSAN=0", - }; - - const optimize_mode = comp.compilerRtOptMode(); - const strip = comp.compilerRtStrip(); - - const config = try Compilation.Config.resolve(.{ - .output_mode = output_mode, - .link_mode = link_mode, - .resolved_target = comp.root_mod.resolved_target, - .is_test = false, - .have_zcu = false, - .emit_bin = true, - .root_optimize_mode = optimize_mode, - .root_strip = strip, - .link_libc = true, - }); - - const root_mod = try Module.create(arena, .{ - .global_cache_directory = comp.global_cache_directory, - .paths = .{ - .root = .{ .root_dir = comp.zig_lib_directory }, - .root_src_path = "", - }, - .fully_qualified_name = "root", - .inherited = .{ - .resolved_target = comp.root_mod.resolved_target, - .strip = strip, - .stack_check = false, - .stack_protector = 0, - .sanitize_c = false, - .sanitize_thread = false, - .red_zone = comp.root_mod.red_zone, - .omit_frame_pointer = comp.root_mod.omit_frame_pointer, - .valgrind = false, - .optimize_mode = optimize_mode, - .structured_cfg = comp.root_mod.structured_cfg, - .pic = true, - .cc_argv = &common_flags, - }, - .global = config, - .cc_argv = &.{}, - .parent = null, - .builtin_mod = null, - }); - const sub_compilation = try Compilation.create(comp.gpa, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, diff --git a/src/libunwind.zig b/src/libunwind.zig index c59b5255d2..0f23f8b352 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -33,7 +33,8 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { // Disable LTO to avoid https://github.com/llvm/llvm-project/issues/56825 .lto = false, }); - const root_mod = Module.create(.{ + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, .paths = .{ .root = .{ .root_dir = comp.zig_lib_directory }, .root_src_path = "", @@ -55,6 +56,8 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { }, .global = config, .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, }); const root_name = "unwind"; @@ -117,6 +120,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files[i] = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}), .extra_flags = cflags.items, + .owner = root_mod, }; } const sub_compilation = try Compilation.create(comp.gpa, .{ @@ -132,7 +136,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .link_mode = link_mode, .function_sections = comp.function_sections, .c_source_files = &c_source_files, .verbose_cc = comp.verbose_cc, @@ -150,8 +153,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node); assert(comp.libunwind_static_lib == null); - - comp.libunwind_static_lib = try sub_compilation.toOwnedLock(); + comp.libunwind_static_lib = try sub_compilation.toCrtFile(); } const unwind_src_list = [_][]const u8{ diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 98c649ca84..b6a3979122 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -18,6 +18,8 @@ major_subsystem_version: u16, minor_subsystem_version: u16, lib_dirs: []const []const u8, entry_addr: ?u32, +module_definition_file: ?[]const u8, +pdb_out_path: ?[]const u8, ptr_width: PtrWidth, page_size: u32, @@ -425,6 +427,8 @@ pub fn createEmpty( .lib_dirs = options.lib_dirs, .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse return error.EntryAddressTooBig, + .module_definition_file = options.module_definition_file, + .pdb_out_path = options.pdb_out_path, }; const use_llvm = comp.config.use_llvm; diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index a94644a41e..39e6d5288d 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -82,7 +82,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try man.addOptionalFile(module_obj_path); man.hash.addOptionalBytes(comp.config.entry); man.hash.add(self.base.stack_size); - man.hash.addOptional(self.image_base); + man.hash.add(self.image_base); man.hash.addListOfBytes(self.lib_dirs); man.hash.add(comp.skip_linker_dependencies); if (comp.config.link_libc) { @@ -102,10 +102,10 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod man.hash.add(self.tsaware); man.hash.add(self.nxcompat); man.hash.add(self.dynamicbase); - man.hash.addOptional(self.base.allow_shlib_undefined); + man.hash.add(self.base.allow_shlib_undefined); // strip does not need to go into the linker hash because it is part of the hash namespace - man.hash.addOptional(self.major_subsystem_version); - man.hash.addOptional(self.minor_subsystem_version); + man.hash.add(self.major_subsystem_version); + man.hash.add(self.minor_subsystem_version); man.hash.addOptional(comp.version); try man.addOptionalFile(self.module_definition_file); @@ -237,7 +237,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); - if (self.implib_emit) |emit| { + if (comp.implib_emit) |emit| { const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); } @@ -310,16 +310,9 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const Mode = enum { uefi, win32 }; const mode: Mode = mode: { if (resolved_subsystem) |subsystem| { - const subsystem_suffix = ss: { - if (self.major_subsystem_version) |major| { - if (self.minor_subsystem_version) |minor| { - break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor }); - } else { - break :ss try allocPrint(arena, ",{d}", .{major}); - } - } - break :ss ""; - }; + const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{ + self.major_subsystem_version, self.minor_subsystem_version, + }); switch (subsystem) { .Console => { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 52e08af665..e5e5c98ec0 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -23,6 +23,8 @@ soname: ?[]const u8, bind_global_refs_locally: bool, linker_script: ?[]const u8, version_script: ?[]const u8, +print_icf_sections: bool, +print_map: bool, ptr_width: PtrWidth, @@ -307,6 +309,8 @@ pub fn createEmpty( .bind_global_refs_locally = options.bind_global_refs_locally, .linker_script = options.linker_script, .version_script = options.version_script, + .print_icf_sections = options.print_icf_sections, + .print_map = options.print_map, }; if (use_llvm and comp.config.have_zcu) { self.llvm_object = try LlvmObject.create(arena, comp); @@ -1294,12 +1298,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Look for entry address in objects if not set by the incremental compiler. if (self.entry_index == null) { - const entry: ?[]const u8 = entry: { - if (comp.config.entry) |entry| break :entry entry; - if (!self.base.isDynLib()) break :entry "_start"; - break :entry null; - }; - self.entry_index = if (entry) |name| self.globalByName(name) else null; + if (comp.config.entry) |name| { + self.entry_index = self.globalByName(name); + } } if (self.base.gc_sections) { @@ -2420,7 +2421,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.addOptionalBytes(comp.config.entry); - man.hash.addOptional(self.image_base); + man.hash.add(self.image_base); man.hash.add(self.base.gc_sections); man.hash.addOptional(self.sort_section); man.hash.add(self.eh_frame_hdr); @@ -5896,7 +5897,7 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { break :blk index; } else { log.debug(" (allocating symbol index {d})", .{self.symbols.items.len}); - const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); + const index: Symbol.Index = @intCast(self.symbols.items.len); _ = self.symbols.addOneAssumeCapacity(); break :blk index; } @@ -5961,6 +5962,7 @@ pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { const gop = try self.resolver.getOrPut(gpa, name_off); if (!gop.found_existing) { const index = try self.addSymbol(); + log.debug("added symbol '{s}' at index {d}", .{ name, index }); const global = self.symbol(index); global.name_offset = name_off; global.flags.global = true; @@ -5996,7 +5998,7 @@ pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateC const off = try self.strings.insert(gpa, name); const gop = try self.comdat_groups_table.getOrPut(gpa, off); if (!gop.found_existing) { - const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); + const index: ComdatGroupOwner.Index = @intCast(self.comdat_groups_owners.items.len); const owner = try self.comdat_groups_owners.addOne(gpa); owner.* = .{}; gop.value_ptr.* = index; diff --git a/src/main.zig b/src/main.zig index f412658a67..a65c8959b3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3481,7 +3481,7 @@ fn buildOutputType( defer if (!comp_destroyed) comp.destroy(); if (show_builtin) { - const builtin_mod = comp.root_mod.deps.get("builtin").?; + const builtin_mod = comp.root_mod.getBuiltinDependency(); const source = builtin_mod.builtin_file.?.source; return std.io.getStdOut().writeAll(source); } diff --git a/src/mingw.zig b/src/mingw.zig index 5a36e00824..fb8972df21 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -41,14 +41,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr //"-D_UNICODE", //"-DWPRFLAG=1", }); - return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtexe.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files); }, .dllcrt2_o => { @@ -60,14 +62,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-U__CRTDLL__", "-D__MSVCRT__", }); - return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtdll.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files); }, .mingw32_lib => { @@ -97,6 +101,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", "crt", dep, }), .extra_flags = args.items, + .owner = undefined, }; } return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files); @@ -125,6 +130,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr (try c_source_files.addOne()).* = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }), .extra_flags = extra_flags, + .owner = undefined, }; } if (comp.getTarget().cpu.arch == .x86) { @@ -134,6 +140,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else { @@ -143,6 +150,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } @@ -175,6 +183,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } const target = comp.getTarget(); @@ -185,6 +194,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else if (target.cpu.arch.isARM()) { @@ -194,6 +204,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else if (target.cpu.arch.isAARCH64()) { @@ -203,6 +214,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else { @@ -238,6 +250,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", "libsrc", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files); diff --git a/src/musl.zig b/src/musl.zig index ce5c5b23dd..4a62d5eded 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -34,12 +34,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.s"), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &files); }, .crtn_o => { var args = std.ArrayList([]const u8).init(arena); @@ -47,12 +49,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.s"), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &files); }, .crt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -61,14 +65,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "crt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &files); }, .rcrt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -78,14 +84,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "rcrt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &files); }, .scrt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -95,14 +103,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "Scrt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &files); }, .libc_a => { // When there is a src//foo.* then it should substitute for src/foo.* @@ -186,6 +196,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr c_source_file.* = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", src_file }), .extra_flags = args.items, + .owner = undefined, }; } return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items); @@ -235,7 +246,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr .structured_cfg = comp.root_mod.structured_cfg, }, .global = config, - .cc_argv = &.{}, + .cc_argv = cc_argv, .parent = null, .builtin_mod = null, }); @@ -261,9 +272,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .c_source_files = &[_]Compilation.CSourceFile{ - .{ .src_path = try comp.zig_lib_directory.join(arena, &.{ "libc", "musl", "libc.S" }) }, + .{ + .src_path = try comp.zig_lib_directory.join(arena, &.{ "libc", "musl", "libc.S" }), + .owner = root_mod, + }, }, - .cc_argv = cc_argv, .skip_linker_dependencies = true, .soname = "libc.so", }); diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 2d5f0bc685..3c0ff26314 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -74,27 +74,31 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, .{}); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_reactor_src_file), }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &files); }, .crt1_command_o => { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, .{}); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_command_src_file), }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &files); }, .libc_a => { var libc_sources = std.ArrayList(Compilation.CSourceFile).init(arena); @@ -109,6 +113,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -125,6 +130,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -141,6 +147,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -159,6 +166,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items); @@ -175,6 +183,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items); @@ -191,6 +200,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items); @@ -208,6 +218,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -224,6 +235,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } -- cgit v1.2.3 From 529d01c2baf695c2844b5dd42642a74749bef0d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Dec 2023 15:22:47 -0700 Subject: resolve error tracing logic at module creation time rather than checking multiple conditions in Sema --- src/Compilation/Config.zig | 10 ++++++++++ src/Module.zig | 11 +---------- src/Sema.zig | 14 ++++---------- src/target.zig | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 20 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 442b7a1b9a..fc4b774baa 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -408,9 +408,17 @@ pub fn resolve(options: Options) !Config { }; }; + const backend_supports_error_tracing = target_util.backendSupportsFeature( + target.cpu.arch, + target.ofmt, + use_llvm, + .error_return_trace, + ); + const root_error_tracing = b: { if (options.root_error_tracing) |x| break :b x; if (root_strip) break :b false; + if (!backend_supports_error_tracing) break :b false; break :b switch (root_optimize_mode) { .Debug => true, .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, @@ -418,6 +426,8 @@ pub fn resolve(options: Options) !Config { }; const any_error_tracing = root_error_tracing or options.any_error_tracing; + if (any_error_tracing and !backend_supports_error_tracing) + return error.BackendLacksErrorTracing; const rdynamic = options.rdynamic orelse false; diff --git a/src/Module.zig b/src/Module.zig index d7b82d15a2..86c124e3b5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5589,16 +5589,7 @@ pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool { const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; const ofmt = zcu.root_mod.resolved_target.result.ofmt; const use_llvm = zcu.comp.config.use_llvm; - return switch (feature) { - .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64, - .panic_unwrap_error => ofmt == .c or use_llvm, - .safety_check_formatted => ofmt == .c or use_llvm, - .error_return_trace => use_llvm, - .is_named_enum_value => use_llvm, - .error_set_has_value => use_llvm or cpu_arch.isWasm(), - .field_reordering => use_llvm, - .safety_checked_instructions => use_llvm, - }; + return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature); } /// Shortcut for calling `intern_pool.get`. diff --git a/src/Sema.zig b/src/Sema.zig index c2e05e4a2c..3a54b20ea4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2045,9 +2045,10 @@ fn analyzeAsType( pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { const mod = sema.mod; + const comp = mod.comp; const gpa = sema.gpa; const ip = &mod.intern_pool; - if (!mod.backendSupportsFeature(.error_return_trace)) return; + if (!comp.config.any_error_tracing) return; assert(!block.is_comptime); var err_trace_block = block.makeSubBlock(); @@ -6543,7 +6544,6 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref const gpa = sema.gpa; const src = sema.src; - if (!mod.backendSupportsFeature(.error_return_trace)) return .none; if (!block.ownerModule().error_tracing) return .none; if (block.is_comptime) @@ -6728,7 +6728,7 @@ fn zirCall( input_is_error = false; } - if (mod.backendSupportsFeature(.error_return_trace) and block.ownerModule().error_tracing and + if (block.ownerModule().error_tracing and !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) { const return_ty = sema.typeOf(call_inst); @@ -18759,8 +18759,6 @@ fn retWithErrTracing( fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { const mod = sema.mod; - if (!mod.backendSupportsFeature(.error_return_trace)) return false; - return fn_ret_ty.isError(mod) and mod.comp.config.any_error_tracing; } @@ -18768,8 +18766,6 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; - // TODO: replace all of these checks with logic in module creation - if (!mod.backendSupportsFeature(.error_return_trace)) return; if (!block.ownerModule().error_tracing) return; // This is only relevant at runtime. @@ -18795,7 +18791,6 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) const mod = sema.mod; const ip = &mod.intern_pool; - if (!mod.backendSupportsFeature(.error_return_trace)) return; if (!ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) return; if (!start_block.ownerModule().error_tracing) return; @@ -20068,8 +20063,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { if (sema.owner_func_index != .none and ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and - block.ownerModule().error_tracing and - mod.backendSupportsFeature(.error_return_trace)) + block.ownerModule().error_tracing) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); } diff --git a/src/target.zig b/src/target.zig index 4adc26aff8..d1a98601cf 100644 --- a/src/target.zig +++ b/src/target.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Type = @import("type.zig").Type; const AddressSpace = std.builtin.AddressSpace; const Alignment = @import("InternPool.zig").Alignment; +const Feature = @import("Module.zig").Feature; pub const default_stack_protector_buffer_size = 4; @@ -665,6 +666,24 @@ pub fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBacken }; } +pub fn backendSupportsFeature( + cpu_arch: std.Target.Cpu.Arch, + ofmt: std.Target.ObjectFormat, + use_llvm: bool, + feature: Feature, +) bool { + return switch (feature) { + .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64, + .panic_unwrap_error => ofmt == .c or use_llvm, + .safety_check_formatted => ofmt == .c or use_llvm, + .error_return_trace => use_llvm, + .is_named_enum_value => use_llvm, + .error_set_has_value => use_llvm or cpu_arch.isWasm(), + .field_reordering => use_llvm, + .safety_checked_instructions => use_llvm, + }; +} + pub fn defaultEntrySymbolName( target: std.Target, /// May be `undefined` when `target` is not WASI. -- cgit v1.2.3 From e22102dfc6356a16e83341d0cd0526762c696ad6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Dec 2023 23:16:31 -0700 Subject: Compilation: make create() take an arena allocator Instead of making its own inside create. 10 out of 10 calls to create() had already an arena in scope, so this commit means that 10 instances of Compilation now reuse an existing arena with the same lifetime rather than creating a redundant one. In other words, this very slightly optimizes initialization of the frontend in terms of memory allocation. --- src/Compilation.zig | 28 ++++++++-------------------- src/Sema.zig | 2 +- src/glibc.zig | 2 +- src/libcxx.zig | 4 ++-- src/libtsan.zig | 2 +- src/libunwind.zig | 2 +- src/main.zig | 4 ++-- src/musl.zig | 2 +- 8 files changed, 17 insertions(+), 29 deletions(-) (limited to 'src/Sema.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index e086e23830..66c136d076 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -45,9 +45,9 @@ 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`. -arena: std.heap.ArenaAllocator, +/// Arena-allocated memory, mostly used during initialization. However, it can +/// be used for other things requiring the same lifetime as the `Compilation`. +arena: Allocator, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. /// TODO: rename to zcu: ?*Zcu module: ?*Module, @@ -1178,7 +1178,7 @@ fn addModuleTableToCacheHash( } } -pub fn create(gpa: Allocator, options: CreateOptions) !*Compilation { +pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compilation { const output_mode = options.config.output_mode; const is_dyn_lib = switch (output_mode) { .Obj, .Exe => false, @@ -1197,13 +1197,6 @@ pub fn create(gpa: Allocator, options: CreateOptions) !*Compilation { const have_zcu = options.config.have_zcu; const comp: *Compilation = comp: { - // For allocations that have the same lifetime as Compilation. This - // arena is used only during this initialization and then is freed in - // deinit(). - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - errdefer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - // We put the `Compilation` itself in the arena. Freeing the arena will free the module. // It's initialized later after we prepare the initialization options. const root_name = try arena.dupeZ(u8, options.root_name); @@ -1454,7 +1447,7 @@ pub fn create(gpa: Allocator, options: CreateOptions) !*Compilation { comp.* = .{ .gpa = gpa, - .arena = undefined, // populated after we are finished with `arena` + .arena = arena, .module = opt_zcu, .cache_use = undefined, // populated below .bin_file = null, // populated below @@ -1696,7 +1689,6 @@ pub fn create(gpa: Allocator, options: CreateOptions) !*Compilation { if (opt_zcu) |zcu| zcu.llvm_object = try LlvmObject.create(arena, comp); } - comp.arena = arena_allocator; break :comp comp; }; errdefer comp.destroy(); @@ -1971,10 +1963,6 @@ pub fn destroy(comp: *Compilation) void { comp.clearMiscFailures(); comp.cache_parent.manifest_dir.close(); - - // This destroys `comp`. - var arena_instance = comp.arena; - arena_instance.deinit(); } pub fn clearMiscFailures(comp: *Compilation) void { @@ -4082,7 +4070,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module }; } - const out_zig_path = try comp.local_cache_directory.join(comp.arena.allocator(), &.{ + const out_zig_path = try comp.local_cache_directory.join(comp.arena, &.{ "o", &digest, cimport_zig_basename, }); if (comp.verbose_cimport) { @@ -6302,7 +6290,7 @@ fn buildOutputFromZig( .output_mode = output_mode, }); - const sub_compilation = try Compilation.create(gpa, .{ + const sub_compilation = try Compilation.create(gpa, arena, .{ .global_cache_directory = comp.global_cache_directory, .local_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, @@ -6411,7 +6399,7 @@ pub fn build_crt_file( item.owner = root_mod; } - const sub_compilation = try Compilation.create(gpa, .{ + const sub_compilation = try Compilation.create(gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, diff --git a/src/Sema.zig b/src/Sema.zig index 3a54b20ea4..a01dd174a8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5761,7 +5761,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr return sema.failWithOwnedErrorMsg(&child_block, msg); } const parent_mod = parent_block.ownerModule(); - const c_import_mod = Package.Module.create(comp.arena.allocator(), .{ + const c_import_mod = Package.Module.create(comp.arena, .{ .global_cache_directory = comp.global_cache_directory, .paths = .{ .root = .{ diff --git a/src/glibc.zig b/src/glibc.zig index 838f55c879..00d76f32fb 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -1116,7 +1116,7 @@ fn buildSharedLib( }, }; - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = zig_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, diff --git a/src/libcxx.zig b/src/libcxx.zig index abcbc0187e..8e28c2174d 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -272,7 +272,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { }); } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, @@ -459,7 +459,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { }); } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, diff --git a/src/libtsan.zig b/src/libtsan.zig index a736c32713..14a7db0888 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -245,7 +245,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { }); } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, diff --git a/src/libunwind.zig b/src/libunwind.zig index 0f23f8b352..5f0613c361 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -123,7 +123,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { .owner = root_mod, }; } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .self_exe_path = comp.self_exe_path, .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, diff --git a/src/main.zig b/src/main.zig index e026e3d7ac..091e4b2668 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3130,7 +3130,7 @@ fn buildOutputType( gimmeMoreOfThoseSweetSweetFileDescriptors(); - const comp = Compilation.create(gpa, .{ + const comp = Compilation.create(gpa, arena, .{ .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, @@ -5508,7 +5508,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi try root_mod.deps.put(arena, "@build", build_mod); - const comp = Compilation.create(gpa, .{ + const comp = Compilation.create(gpa, arena, .{ .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, diff --git a/src/musl.zig b/src/musl.zig index 4a62d5eded..c244257a70 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -251,7 +251,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr .builtin_mod = null, }); - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, -- cgit v1.2.3