From e785d4f013843081046d311870ca2ab9f996e38a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Dec 2023 15:25:06 -0700 Subject: [PATCH] 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 | 1471 ++++++++------------------ src/Compilation/Config.zig | 382 +++++++ src/Module.zig | 8 +- src/Package/Module.zig | 411 +++++++- src/Sema.zig | 2 +- src/codegen/llvm.zig | 38 +- src/link.zig | 526 ++++------ 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(+), 2711 deletions(-) create mode 100644 src/Builtin.zig create mode 100644 src/Compilation/Config.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fcd189c997a..6e2b5ae2ce18 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 b7865e730b5e..18c1ffa07a2a 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 81fab2be5e69..9daa89deb8d5 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 000000000000..7224a6fd24fb --- /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 154c5378c240..2bea6a830a79 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 = &.{}, }; @@ -751,6 +803,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); } @@ -760,7 +828,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. @@ -779,8 +847,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 `. @@ -807,11 +873,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, @@ -827,7 +903,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 @@ -837,14 +912,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{}, @@ -858,40 +931,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, @@ -902,14 +951,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, @@ -947,8 +993,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 @@ -962,23 +1006,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 @@ -999,84 +1038,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(); + var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + try seen_table.put(gpa, root_mod, {}); - const modules = try allocator.alloc(Package.Module.Deps.KV, mod_table.count()); - { - // Copy over the hashmap entries to our slice - var table_it = mod_table.iterator(); - var idx: usize = 0; - while (table_it.next()) |entry| : (idx += 1) { - modules[idx] = .{ - .key = entry.key_ptr.*, - .value = entry.value_ptr.*, - }; - } - } - // Sort the slice by package name - mem.sortUnstable(Package.Module.Deps.KV, modules, {}, struct { - fn lessThan(_: void, lhs: Package.Module.Deps.KV, rhs: Package.Module.Deps.KV) bool { - return std.mem.lessThan(u8, lhs.key, rhs.key); + 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 (modules) |mod| { - if ((try seen_table.getOrPut(mod.value)).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.key); switch (hash_type) { .path_bytes => { - hash.addBytes(mod.value.root_src_path); - hash.addOptionalBytes(mod.value.root.root_dir.path); - hash.addBytes(mod.value.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 mod.value.root.joinString( - allocator, - mod.value.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, mod.value.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(); @@ -1086,366 +1134,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; - - // 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; - } + const any_unwind_tables = options.config.any_unwind_tables; - break :blk false; - }; + const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; + const build_id = options.build_id orelse .none; - 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); - } - - 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); @@ -1470,43 +1297,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; @@ -1519,13 +1328,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 @@ -1535,13 +1338,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); @@ -1583,85 +1384,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"; @@ -1692,13 +1414,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, @@ -1706,31 +1429,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, }; @@ -1743,9 +1459,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, }; } @@ -1770,17 +1486,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, }; @@ -1794,13 +1510,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| { @@ -1823,7 +1539,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, }; }; @@ -1846,132 +1562,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, - .parent_compilation_link_libc = options.parent_compilation_link_libc, - .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, @@ -1985,8 +1589,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, @@ -1994,7 +1596,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, @@ -2013,22 +1614,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); @@ -2128,7 +1817,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 }, }); } @@ -2190,7 +1879,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. @@ -2302,7 +1991,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 { @@ -2455,7 +2144,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 @@ -2468,7 +2157,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); } @@ -2497,7 +2186,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 }); } } @@ -2722,16 +2411,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); @@ -2786,8 +2466,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); @@ -3385,7 +3063,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; @@ -3395,7 +3073,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; @@ -4193,7 +3871,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); } }; @@ -5365,9 +5043,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) { @@ -5662,68 +5340,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; @@ -6105,7 +5721,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 { @@ -6418,7 +6034,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() }); @@ -6471,7 +6087,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, @@ -6489,7 +6105,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, @@ -6498,244 +6114,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, - \\}; - \\ - ); - } - - // 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 = comp.bin_file.options.link_libc or - (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_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(comp.bin_file.options.optimize_mode)), - 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( @@ -6782,21 +6161,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. @@ -6806,33 +6210,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, @@ -6867,7 +6256,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 000000000000..0b9c70fc4244 --- /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 e4efa8a8a191..1a582482de7a 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. @@ -3603,7 +3604,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; @@ -3928,10 +3929,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 7e6b518892b4..94431856acf0 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,17 +14,409 @@ fully_qualified_name: []const u8, /// responsible for detecting these names and using the correct package. deps: Deps = .{}, -pub const Deps = std.StringHashMapUnmanaged(*Module); +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 { /// Each `Package` exposes a `Module` with build.zig as its root source file. 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 ec94e52c7c54..4321f2d1d7a5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -36667,7 +36667,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 6dfcf5530791..6ec1398a1b54 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 8c53a74f52b3..46dab1b6edbf 100644 --- a/src/link.zig +++ b/src/link.zig @@ -66,238 +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, - parent_compilation_link_libc: 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, @@ -308,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 { @@ -665,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); }, } } @@ -1198,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 { @@ -1236,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"); @@ -1246,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 5fbf02871adf..e477aeffcf48 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 6f3c59c4e74e..ef25c63e3221 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 61446b3300d6..f61e78c2331a 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 681833857b97..5685ddad27b2 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 624a67d043f0..f610e55fb04c 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, + }; +}